2 * DO NOT EDIT THIS FILE.
3 * See the following change record for more information,
4 * https://www.drupal.org/node/2815083
8 (function ($, Drupal) {
13 Drupal.states = states;
15 Drupal.behaviors.states = {
16 attach: function attach(context, settings) {
17 var $states = $(context).find('[data-drupal-states]');
18 var il = $states.length;
20 var _loop = function _loop(i) {
21 var config = JSON.parse($states[i].getAttribute('data-drupal-states'));
22 Object.keys(config || {}).forEach(function (state) {
23 new states.Dependent({
24 element: $($states[i]),
25 state: states.State.sanitize(state),
26 constraints: config[state]
31 for (var i = 0; i < il; i++) {
35 while (states.postponed.length) {
36 states.postponed.shift()();
41 states.Dependent = function (args) {
44 $.extend(this, { values: {}, oldValue: null }, args);
46 this.dependees = this.getDependees();
47 Object.keys(this.dependees || {}).forEach(function (selector) {
48 _this.initializeDependee(selector, _this.dependees[selector]);
52 states.Dependent.comparisons = {
53 RegExp: function RegExp(reference, value) {
54 return reference.test(value);
56 Function: function Function(reference, value) {
57 return reference(value);
59 Number: function Number(reference, value) {
60 return typeof value === 'string' ? _compare2(reference.toString(), value) : _compare2(reference, value);
64 states.Dependent.prototype = {
65 initializeDependee: function initializeDependee(selector, dependeeStates) {
69 function stateEventHandler(e) {
70 self.update(e.data.selector, e.data.state, e.value);
73 this.values[selector] = {};
75 for (var i in dependeeStates) {
76 if (dependeeStates.hasOwnProperty(i)) {
77 state = dependeeStates[i];
79 if ($.inArray(state, dependeeStates) === -1) {
83 state = states.State.sanitize(state);
85 this.values[selector][state.name] = null;
87 $(selector).on('state:' + state, { selector: selector, state: state }, stateEventHandler);
89 new states.Trigger({ selector: selector, state: state });
93 compare: function compare(reference, selector, state) {
94 var value = this.values[selector][state.name];
95 if (reference.constructor.name in states.Dependent.comparisons) {
96 return states.Dependent.comparisons[reference.constructor.name](reference, value);
99 return _compare2(reference, value);
101 update: function update(selector, state, value) {
102 if (value !== this.values[selector][state.name]) {
103 this.values[selector][state.name] = value;
107 reevaluate: function reevaluate() {
108 var value = this.verifyConstraints(this.constraints);
110 if (value !== this.oldValue) {
111 this.oldValue = value;
113 value = invert(value, this.state.invert);
115 this.element.trigger({ type: 'state:' + this.state, value: value, trigger: true });
118 verifyConstraints: function verifyConstraints(constraints, selector) {
120 if ($.isArray(constraints)) {
121 var hasXor = $.inArray('xor', constraints) === -1;
122 var len = constraints.length;
123 for (var i = 0; i < len; i++) {
124 if (constraints[i] !== 'xor') {
125 var constraint = this.checkConstraints(constraints[i], selector, i);
127 if (constraint && (hasXor || result)) {
130 result = result || constraint;
133 } else if ($.isPlainObject(constraints)) {
134 for (var n in constraints) {
135 if (constraints.hasOwnProperty(n)) {
136 result = ternary(result, this.checkConstraints(constraints[n], selector, n));
138 if (result === false) {
146 checkConstraints: function checkConstraints(value, selector, state) {
147 if (typeof state !== 'string' || /[0-9]/.test(state[0])) {
149 } else if (typeof selector === 'undefined') {
154 if (state !== null) {
155 state = states.State.sanitize(state);
156 return invert(this.compare(value, selector, state), state.invert);
159 return this.verifyConstraints(value, selector);
161 getDependees: function getDependees() {
164 var _compare = this.compare;
165 this.compare = function (reference, selector, state) {
166 (cache[selector] || (cache[selector] = [])).push(state.name);
169 this.verifyConstraints(this.constraints);
171 this.compare = _compare;
177 states.Trigger = function (args) {
178 $.extend(this, args);
180 if (this.state in states.Trigger.states) {
181 this.element = $(this.selector);
183 if (!this.element.data('trigger:' + this.state)) {
189 states.Trigger.prototype = {
190 initialize: function initialize() {
193 var trigger = states.Trigger.states[this.state];
195 if (typeof trigger === 'function') {
196 trigger.call(window, this.element);
198 Object.keys(trigger || {}).forEach(function (event) {
199 _this2.defaultTrigger(event, trigger[event]);
203 this.element.data('trigger:' + this.state, true);
205 defaultTrigger: function defaultTrigger(event, valueFn) {
206 var oldValue = valueFn.call(this.element);
208 this.element.on(event, $.proxy(function (e) {
209 var value = valueFn.call(this.element, e);
211 if (oldValue !== value) {
212 this.element.trigger({ type: 'state:' + this.state, value: value, oldValue: oldValue });
217 states.postponed.push($.proxy(function () {
218 this.element.trigger({ type: 'state:' + this.state, value: oldValue, oldValue: null });
223 states.Trigger.states = {
225 keyup: function keyup() {
226 return this.val() === '';
231 change: function change() {
233 this.each(function () {
234 checked = $(this).prop('checked');
243 keyup: function keyup() {
244 if (this.length > 1) {
245 return this.filter(':checked').val() || false;
249 change: function change() {
250 if (this.length > 1) {
251 return this.filter(':checked').val() || false;
258 collapsed: function collapsed(e) {
259 return typeof e !== 'undefined' && 'value' in e ? e.value : !this.is('[open]');
264 states.State = function (state) {
265 this.pristine = state;
270 while (this.name.charAt(0) === '!') {
271 this.name = this.name.substring(1);
272 this.invert = !this.invert;
275 if (this.name in states.State.aliases) {
276 this.name = states.State.aliases[this.name];
283 states.State.sanitize = function (state) {
284 if (state instanceof states.State) {
288 return new states.State(state);
291 states.State.aliases = {
292 enabled: '!disabled',
293 invisible: '!visible',
295 untouched: '!touched',
296 optional: '!required',
298 unchecked: '!checked',
299 irrelevant: '!relevant',
300 expanded: '!collapsed',
303 readwrite: '!readonly'
306 states.State.prototype = {
309 toString: function toString() {
314 var $document = $(document);
315 $document.on('state:disabled', function (e) {
317 $(e.target).prop('disabled', e.value).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value).find('select, input, textarea').prop('disabled', e.value);
321 $document.on('state:required', function (e) {
324 var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : '');
325 var $label = $(e.target).attr({ required: 'required', 'aria-required': 'aria-required' }).closest('.js-form-item, .js-form-wrapper').find(label);
327 if (!$label.hasClass('js-form-required').length) {
328 $label.addClass('js-form-required form-required');
331 $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required');
336 $document.on('state:visible', function (e) {
338 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value);
342 $document.on('state:checked', function (e) {
344 $(e.target).prop('checked', e.value);
348 $document.on('state:collapsed', function (e) {
350 if ($(e.target).is('[open]') === e.value) {
351 $(e.target).find('> summary').trigger('click');
356 function ternary(a, b) {
357 if (typeof a === 'undefined') {
359 } else if (typeof b === 'undefined') {
366 function invert(a, invertState) {
367 return invertState && typeof a !== 'undefined' ? !a : a;
370 function _compare2(a, b) {
372 return typeof a === 'undefined' ? a : true;
375 return typeof a === 'undefined' || typeof b === 'undefined';