X-Git-Url: https://yaffs.net/gitweb/?a=blobdiff_plain;f=web%2Fcore%2Fmisc%2Fstates.es6.js;h=811a5112d1746cbc3bd65608cf54e56f12dc0e2d;hb=4e1bfbf98b844da83b18aca92ef00f11a4735806;hp=b47b90de57e40c12a61a636e777c03fa2f8af298;hpb=af6d1fb995500ae68849458ee10d66abbdcfb252;p=yaffs-website diff --git a/web/core/misc/states.es6.js b/web/core/misc/states.es6.js index b47b90de5..811a5112d 100644 --- a/web/core/misc/states.es6.js +++ b/web/core/misc/states.es6.js @@ -3,7 +3,7 @@ * Drupal's states library. */ -(function ($, Drupal) { +(function($, Drupal) { /** * The base States namespace. * @@ -13,7 +13,6 @@ * @namespace Drupal.states */ const states = { - /** * An array of functions that should be postponed. */ @@ -22,6 +21,68 @@ Drupal.states = states; + /** + * Inverts a (if it's not undefined) when invertState is true. + * + * @function Drupal.states~invert + * + * @param {*} a + * The value to maybe invert. + * @param {bool} invertState + * Whether to invert state or not. + * + * @return {bool} + * The result. + */ + function invert(a, invertState) { + return invertState && typeof a !== 'undefined' ? !a : a; + } + + /** + * Compares two values while ignoring undefined values. + * + * @function Drupal.states~compare + * + * @param {*} a + * Value a. + * @param {*} b + * Value b. + * + * @return {bool} + * The comparison result. + */ + function compare(a, b) { + if (a === b) { + return typeof a === 'undefined' ? a : true; + } + + return typeof a === 'undefined' || typeof b === 'undefined'; + } + + /** + * Bitwise AND with a third undefined state. + * + * @function Drupal.states~ternary + * + * @param {*} a + * Value a. + * @param {*} b + * Value b + * + * @return {bool} + * The result. + */ + function ternary(a, b) { + if (typeof a === 'undefined') { + return b; + } + if (typeof b === 'undefined') { + return a; + } + + return a && b; + } + /** * Attaches the states. * @@ -35,8 +96,10 @@ const $states = $(context).find('[data-drupal-states]'); const il = $states.length; for (let i = 0; i < il; i++) { - const config = JSON.parse($states[i].getAttribute('data-drupal-states')); - Object.keys(config || {}).forEach((state) => { + const config = JSON.parse( + $states[i].getAttribute('data-drupal-states'), + ); + Object.keys(config || {}).forEach(state => { new states.Dependent({ element: $($states[i]), state: states.State.sanitize(state), @@ -47,7 +110,7 @@ // Execute all postponed functions now. while (states.postponed.length) { - (states.postponed.shift())(); + states.postponed.shift()(); } }, }; @@ -68,11 +131,11 @@ * element depends on. It can be nested and can contain * arbitrary AND and OR clauses. */ - states.Dependent = function (args) { + states.Dependent = function(args) { $.extend(this, { values: {}, oldValue: null }, args); this.dependees = this.getDependees(); - Object.keys(this.dependees || {}).forEach((selector) => { + Object.keys(this.dependees || {}).forEach(selector => { this.initializeDependee(selector, this.dependees[selector]); }); }; @@ -102,12 +165,13 @@ // compare(). // Otherwise numeric keys in the form's #states array fail to match // string values returned from jQuery's val(). - return (typeof value === 'string') ? compare(reference.toString(), value) : compare(reference, value); + return typeof value === 'string' + ? compare(reference.toString(), value) + : compare(reference, value); }, }; states.Dependent.prototype = { - /** * Initializes one of the elements this dependent depends on. * @@ -120,38 +184,30 @@ * dependee's compliance status. */ initializeDependee(selector, dependeeStates) { - let state; - const self = this; - - function stateEventHandler(e) { - self.update(e.data.selector, e.data.state, e.value); - } - // Cache for the states of this dependee. this.values[selector] = {}; - // eslint-disable-next-line no-restricted-syntax - for (const i in dependeeStates) { - if (dependeeStates.hasOwnProperty(i)) { - state = dependeeStates[i]; - // Make sure we're not initializing this selector/state combination - // twice. - if ($.inArray(state, dependeeStates) === -1) { - continue; - } + Object.keys(dependeeStates).forEach(i => { + let state = dependeeStates[i]; + // Make sure we're not initializing this selector/state combination + // twice. + if ($.inArray(state, dependeeStates) === -1) { + return; + } - state = states.State.sanitize(state); + state = states.State.sanitize(state); - // Initialize the value of this state. - this.values[selector][state.name] = null; + // Initialize the value of this state. + this.values[selector][state.name] = null; - // Monitor state changes of the specified state for this dependee. - $(selector).on(`state:${state}`, { selector, state }, stateEventHandler); + // Monitor state changes of the specified state for this dependee. + $(selector).on(`state:${state}`, { selector, state }, e => { + this.update(e.data.selector, e.data.state, e.value); + }); - // Make sure the event we just bound ourselves to is actually fired. - new states.Trigger({ selector, state }); - } - } + // Make sure the event we just bound ourselves to is actually fired. + new states.Trigger({ selector, state }); + }); }, /** @@ -173,10 +229,13 @@ const value = this.values[selector][state.name]; if (reference.constructor.name in states.Dependent.comparisons) { // Use a custom compare function for certain reference value types. - return states.Dependent.comparisons[reference.constructor.name](reference, value); + return states.Dependent.comparisons[reference.constructor.name]( + reference, + value, + ); } - // Do a plain comparison otherwise. + // Do a plain comparison otherwise. return compare(reference, value); }, @@ -220,7 +279,11 @@ // By adding "trigger: true", we ensure that state changes don't go into // infinite loops. - this.element.trigger({ type: `state:${this.state}`, value, trigger: true }); + this.element.trigger({ + type: `state:${this.state}`, + value, + trigger: true, + }); } }, @@ -247,7 +310,11 @@ const len = constraints.length; for (let i = 0; i < len; i++) { if (constraints[i] !== 'xor') { - const constraint = this.checkConstraints(constraints[i], selector, i); + const constraint = this.checkConstraints( + constraints[i], + selector, + i, + ); // Return if this is OR and we have a satisfied constraint or if // this is XOR and we have a second satisfied constraint. if (constraint && (hasXor || result)) { @@ -265,7 +332,10 @@ // eslint-disable-next-line no-restricted-syntax for (const n in constraints) { if (constraints.hasOwnProperty(n)) { - result = ternary(result, this.checkConstraints(constraints[n], selector, n)); + result = ternary( + result, + this.checkConstraints(constraints[n], selector, n), + ); // False and anything else will evaluate to false, so return when // any false condition is found. if (result === false) { @@ -302,10 +372,9 @@ checkConstraints(value, selector, state) { // Normalize the last parameter. If it's non-numeric, we treat it either // as a selector (in case there isn't one yet) or as a trigger/state. - if (typeof state !== 'string' || (/[0-9]/).test(state[0])) { + if (typeof state !== 'string' || /[0-9]/.test(state[0])) { state = null; - } - else if (typeof selector === 'undefined') { + } else if (typeof selector === 'undefined') { // Propagate the state to the selector when there isn't one yet. selector = state; state = null; @@ -317,7 +386,7 @@ return invert(this.compare(value, selector, state), state.invert); } - // Resolve this constraint as an AND/OR operator. + // Resolve this constraint as an AND/OR operator. return this.verifyConstraints(value, selector); }, @@ -334,7 +403,7 @@ // Swivel the lookup function so that we can record all available // selector- state combinations for initialization. const _compare = this.compare; - this.compare = function (reference, selector, state) { + this.compare = function(reference, selector, state) { (cache[selector] || (cache[selector] = [])).push(state.name); // Return nothing (=== undefined) so that the constraint loops are not // broken. @@ -360,7 +429,7 @@ * @param {object} args * Trigger arguments. */ - states.Trigger = function (args) { + states.Trigger = function(args) { $.extend(this, args); if (this.state in states.Trigger.states) { @@ -375,7 +444,6 @@ }; states.Trigger.prototype = { - /** * @memberof Drupal.states.Trigger# */ @@ -385,9 +453,8 @@ if (typeof trigger === 'function') { // We have a custom trigger initialization function. trigger.call(window, this.element); - } - else { - Object.keys(trigger || {}).forEach((event) => { + } else { + Object.keys(trigger || {}).forEach(event => { this.defaultTrigger(event, trigger[event]); }); } @@ -408,19 +475,32 @@ let oldValue = valueFn.call(this.element); // Attach the event callback. - this.element.on(event, $.proxy(function (e) { - const value = valueFn.call(this.element, e); - // Only trigger the event if the value has actually changed. - if (oldValue !== value) { - this.element.trigger({ type: `state:${this.state}`, value, oldValue }); - oldValue = value; - } - }, this)); - - states.postponed.push($.proxy(function () { - // Trigger the event once for initialization purposes. - this.element.trigger({ type: `state:${this.state}`, value: oldValue, oldValue: null }); - }, this)); + this.element.on( + event, + $.proxy(function(e) { + const value = valueFn.call(this.element, e); + // Only trigger the event if the value has actually changed. + if (oldValue !== value) { + this.element.trigger({ + type: `state:${this.state}`, + value, + oldValue, + }); + oldValue = value; + } + }, this), + ); + + states.postponed.push( + $.proxy(function() { + // Trigger the event once for initialization purposes. + this.element.trigger({ + type: `state:${this.state}`, + value: oldValue, + oldValue: null, + }); + }, this), + ); }, }; @@ -454,7 +534,7 @@ // support selectors matching multiple checkboxes, iterate over all and // return whether any is checked. let checked = false; - this.each(function () { + this.each(function() { // Use prop() here as we want a boolean of the checkbox state. // @see http://api.jquery.com/prop/ checked = $(this).prop('checked'); @@ -487,7 +567,9 @@ collapsed: { collapsed(e) { - return (typeof e !== 'undefined' && 'value' in e) ? e.value : !this.is('[open]'); + return typeof e !== 'undefined' && 'value' in e + ? e.value + : !this.is('[open]'); }, }, }; @@ -500,7 +582,7 @@ * @param {string} state * The name of the state. */ - states.State = function (state) { + states.State = function(state) { /** * Original unresolved name. */ @@ -519,8 +601,7 @@ // Replace the state with its normalized name. if (this.name in states.State.aliases) { this.name = states.State.aliases[this.name]; - } - else { + } else { process = false; } } while (process); @@ -537,7 +618,7 @@ * @return {Drupal.states.state} * A state object. */ - states.State.sanitize = function (state) { + states.State.sanitize = function(state) { if (state instanceof states.State) { return state; } @@ -567,7 +648,6 @@ }; states.State.prototype = { - /** * @memberof Drupal.states.State# */ @@ -594,7 +674,7 @@ */ const $document = $(document); - $document.on('state:disabled', (e) => { + $document.on('state:disabled', e => { // Only act when this change was triggered by a dependency and not by the // element monitoring itself. if (e.trigger) { @@ -610,17 +690,19 @@ } }); - $document.on('state:required', (e) => { + $document.on('state:required', e => { if (e.trigger) { if (e.value) { const label = `label${e.target.id ? `[for=${e.target.id}]` : ''}`; - const $label = $(e.target).attr({ required: 'required', 'aria-required': 'aria-required' }).closest('.js-form-item, .js-form-wrapper').find(label); + const $label = $(e.target) + .attr({ required: 'required', 'aria-required': 'aria-required' }) + .closest('.js-form-item, .js-form-wrapper') + .find(label); // Avoids duplicate required markers on initialization. if (!$label.hasClass('js-form-required').length) { $label.addClass('js-form-required form-required'); } - } - else { + } else { $(e.target) .removeAttr('required aria-required') .closest('.js-form-item, .js-form-wrapper') @@ -630,90 +712,27 @@ } }); - $document.on('state:visible', (e) => { + $document.on('state:visible', e => { if (e.trigger) { - $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value); + $(e.target) + .closest('.js-form-item, .js-form-submit, .js-form-wrapper') + .toggle(e.value); } }); - $document.on('state:checked', (e) => { + $document.on('state:checked', e => { if (e.trigger) { $(e.target).prop('checked', e.value); } }); - $document.on('state:collapsed', (e) => { + $document.on('state:collapsed', e => { if (e.trigger) { if ($(e.target).is('[open]') === e.value) { - $(e.target).find('> summary').trigger('click'); + $(e.target) + .find('> summary') + .trigger('click'); } } }); - - /** - * These are helper functions implementing addition "operators" and don't - * implement any logic that is particular to states. - */ - - /** - * Bitwise AND with a third undefined state. - * - * @function Drupal.states~ternary - * - * @param {*} a - * Value a. - * @param {*} b - * Value b - * - * @return {bool} - * The result. - */ - function ternary(a, b) { - if (typeof a === 'undefined') { - return b; - } - else if (typeof b === 'undefined') { - return a; - } - - return a && b; - } - - /** - * Inverts a (if it's not undefined) when invertState is true. - * - * @function Drupal.states~invert - * - * @param {*} a - * The value to maybe invert. - * @param {bool} invertState - * Whether to invert state or not. - * - * @return {bool} - * The result. - */ - function invert(a, invertState) { - return (invertState && typeof a !== 'undefined') ? !a : a; - } - - /** - * Compares two values while ignoring undefined values. - * - * @function Drupal.states~compare - * - * @param {*} a - * Value a. - * @param {*} b - * Value b. - * - * @return {bool} - * The comparison result. - */ - function compare(a, b) { - if (a === b) { - return typeof a === 'undefined' ? a : true; - } - - return typeof a === 'undefined' || typeof b === 'undefined'; - } -}(jQuery, Drupal)); +})(jQuery, Drupal);