3 * Provides a JavaScript API to broadcast text editor configuration changes.
5 * Filter implementations may listen to the drupalEditorFeatureAdded,
6 * drupalEditorFeatureRemoved, and drupalEditorFeatureRemoved events on document
7 * to automatically adjust their settings based on the editor configuration.
10 (function ($, _, Drupal, document) {
12 * Editor configuration namespace.
16 Drupal.editorConfiguration = {
19 * Must be called by a specific text editor's configuration whenever a
20 * feature is added by the user.
22 * Triggers the drupalEditorFeatureAdded event on the document, which
23 * receives a {@link Drupal.EditorFeature} object.
25 * @param {Drupal.EditorFeature} feature
26 * A text editor feature object.
28 * @fires event:drupalEditorFeatureAdded
30 addedFeature(feature) {
31 $(document).trigger('drupalEditorFeatureAdded', feature);
35 * Must be called by a specific text editor's configuration whenever a
36 * feature is removed by the user.
38 * Triggers the drupalEditorFeatureRemoved event on the document, which
39 * receives a {@link Drupal.EditorFeature} object.
41 * @param {Drupal.EditorFeature} feature
42 * A text editor feature object.
44 * @fires event:drupalEditorFeatureRemoved
46 removedFeature(feature) {
47 $(document).trigger('drupalEditorFeatureRemoved', feature);
51 * Must be called by a specific text editor's configuration whenever a
52 * feature is modified, i.e. has different rules.
54 * For example when the "Bold" button is configured to use the `<b>` tag
55 * instead of the `<strong>` tag.
57 * Triggers the drupalEditorFeatureModified event on the document, which
58 * receives a {@link Drupal.EditorFeature} object.
60 * @param {Drupal.EditorFeature} feature
61 * A text editor feature object.
63 * @fires event:drupalEditorFeatureModified
65 modifiedFeature(feature) {
66 $(document).trigger('drupalEditorFeatureModified', feature);
70 * May be called by a specific text editor's configuration whenever a
71 * feature is being added, to check whether it would require the filter
72 * settings to be updated.
74 * The canonical use case is when a text editor is being enabled:
76 * this would not cause the filter settings to be changed; rather, the
77 * default set of buttons (features) for the text editor should adjust
78 * itself to not cause filter setting changes.
80 * Note: for filters to integrate with this functionality, it is necessary
82 * `Drupal.filterSettingsForEditors[filterID].getRules()`.
84 * @param {Drupal.EditorFeature} feature
85 * A text editor feature object.
88 * Whether the given feature is allowed by the current filters.
90 featureIsAllowedByFilters(feature) {
92 * Generate the universe U of possible values that can result from the
93 * feature's rules' requirements.
95 * This generates an object of this form:
98 * 'touchedByAllowedPropertyRule': false,
100 * 'attributes:href': false,
101 * 'classes:external': false,
104 * 'touchedByAllowedPropertyRule': false,
108 * 'touchedByAllowedPropertyRule': false,
110 * 'attributes:src': false
114 * In this example, the given text editor feature resulted in the above
115 * universe, which shows that it must be allowed to generate the a,
116 * strong and img tags. For the a tag, it must be able to set the "href"
117 * attribute and the "external" class. For the strong tag, no further
118 * properties are required. For the img tag, the "src" attribute is
119 * required. The "tag" key is used to track whether that tag was
120 * explicitly allowed by one of the filter's rules. The
121 * "touchedByAllowedPropertyRule" key is used for state tracking that is
122 * essential for filterStatusAllowsFeature() to be able to reason: when
123 * all of a filter's rules have been applied, and none of the forbidden
124 * rules matched (which would have resulted in early termination) yet the
125 * universe has not been made empty (which would be the end result if
126 * everything in the universe were explicitly allowed), then this piece
127 * of state data enables us to determine whether a tag whose properties
128 * were not all explicitly allowed are in fact still allowed, because its
129 * tag was explicitly allowed and there were no filter rules applying
130 * "allowed tag property value" restrictions for this particular tag.
132 * @param {object} feature
133 * The feature in question.
136 * The universe generated.
138 * @see findPropertyValueOnTag()
139 * @see filterStatusAllowsFeature()
141 function generateUniverseFromFeatureRequirements(feature) {
142 const properties = ['attributes', 'styles', 'classes'];
145 for (let r = 0; r < feature.rules.length; r++) {
146 const featureRule = feature.rules[r];
148 // For each tag required by this feature rule, create a basic entry in
150 const requiredTags = featureRule.required.tags;
151 for (let t = 0; t < requiredTags.length; t++) {
152 universe[requiredTags[t]] = {
153 // Whether this tag was allowed or not.
155 // Whether any filter rule that applies to this tag had an allowed
156 // property rule. i.e. will become true if >=1 filter rule has >=1
157 // allowed property rule.
158 touchedByAllowedPropertyRule: false,
159 // Analogous, but for forbidden property rule.
160 touchedBytouchedByForbiddenPropertyRule: false,
164 // If no required properties are defined for this rule, we can move on
165 // to the next feature.
166 if (emptyProperties(featureRule.required)) {
170 // Expand the existing universe, assume that each tags' property
171 // value is disallowed. If the filter rules allow everything in the
172 // feature's universe, then the feature is allowed.
173 for (let p = 0; p < properties.length; p++) {
174 const property = properties[p];
175 for (let pv = 0; pv < featureRule.required[property].length; pv++) {
176 const propertyValue = featureRule.required[property];
177 universe[requiredTags][`${property}:${propertyValue}`] = false;
186 * Provided a section of a feature or filter rule, checks if no property
187 * values are defined for all properties: attributes, classes and styles.
189 * @param {object} section
190 * The section to check.
193 * Returns true if the section has empty properties, false otherwise.
195 function emptyProperties(section) {
196 return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
200 * Calls findPropertyValueOnTag on the given tag for every property value
201 * that is listed in the "propertyValues" parameter. Supports the wildcard
204 * @param {object} universe
205 * The universe to check.
206 * @param {string} tag
207 * The tag to look for.
208 * @param {string} property
209 * The property to check.
210 * @param {Array} propertyValues
211 * Values of the property to check.
212 * @param {bool} allowing
213 * Whether to update the universe or not.
216 * Returns true if found, false otherwise.
218 function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
219 // Detect the wildcard case.
221 return findPropertyValuesOnAllTags(universe, property, propertyValues, allowing);
224 let atLeastOneFound = false;
225 _.each(propertyValues, (propertyValue) => {
226 if (findPropertyValueOnTag(universe, tag, property, propertyValue, allowing)) {
227 atLeastOneFound = true;
230 return atLeastOneFound;
234 * Calls findPropertyValuesOnAllTags for all tags in the universe.
236 * @param {object} universe
237 * The universe to check.
238 * @param {string} property
239 * The property to check.
240 * @param {Array} propertyValues
241 * Values of the property to check.
242 * @param {bool} allowing
243 * Whether to update the universe or not.
246 * Returns true if found, false otherwise.
248 function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) {
249 let atLeastOneFound = false;
250 _.each(_.keys(universe), (tag) => {
251 if (findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing)) {
252 atLeastOneFound = true;
255 return atLeastOneFound;
259 * Finds out if a specific property value (potentially containing
260 * wildcards) exists on the given tag. When the "allowing" parameter
261 * equals true, the universe will be updated if that specific property
262 * value exists. Returns true if found, false otherwise.
264 * @param {object} universe
265 * The universe to check.
266 * @param {string} tag
267 * The tag to look for.
268 * @param {string} property
269 * The property to check.
270 * @param {string} propertyValue
271 * The property value to check.
272 * @param {bool} allowing
273 * Whether to update the universe or not.
276 * Returns true if found, false otherwise.
278 function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) {
279 // If the tag does not exist in the universe, then it definitely can't
280 // have this specific property value.
281 if (!_.has(universe, tag)) {
285 const key = `${property}:${propertyValue}`;
287 // Track whether a tag was touched by a filter rule that allows specific
288 // property values on this particular tag.
289 // @see generateUniverseFromFeatureRequirements
291 universe[tag].touchedByAllowedPropertyRule = true;
294 // The simple case: no wildcard in property value.
295 if (_.indexOf(propertyValue, '*') === -1) {
296 if (_.has(universe, tag) && _.has(universe[tag], key)) {
298 universe[tag][key] = true;
304 // The complex case: wildcard in property value.
306 let atLeastOneFound = false;
307 const regex = key.replace(/\*/g, '[^ ]*');
308 _.each(_.keys(universe[tag]), (key) => {
309 if (key.match(regex)) {
310 atLeastOneFound = true;
312 universe[tag][key] = true;
316 return atLeastOneFound;
320 * Deletes a tag from the universe if the tag itself and each of its
321 * properties are marked as allowed.
323 * @param {object} universe
324 * The universe to delete from.
325 * @param {string} tag
329 * Whether something was deleted from the universe.
331 function deleteFromUniverseIfAllowed(universe, tag) {
332 // Detect the wildcard case.
334 return deleteAllTagsFromUniverseIfAllowed(universe);
336 if (_.has(universe, tag) && _.every(_.omit(universe[tag], 'touchedByAllowedPropertyRule'))) {
337 delete universe[tag];
344 * Calls deleteFromUniverseIfAllowed for all tags in the universe.
346 * @param {object} universe
347 * The universe to delete from.
350 * Whether something was deleted from the universe.
352 function deleteAllTagsFromUniverseIfAllowed(universe) {
353 let atLeastOneDeleted = false;
354 _.each(_.keys(universe), (tag) => {
355 if (deleteFromUniverseIfAllowed(universe, tag)) {
356 atLeastOneDeleted = true;
359 return atLeastOneDeleted;
363 * Checks if any filter rule forbids either a tag or a tag property value
364 * that exists in the universe.
366 * @param {object} universe
368 * @param {object} filterStatus
369 * Filter status to use for check.
372 * Whether any filter rule forbids something in the universe.
374 function anyForbiddenFilterRuleMatches(universe, filterStatus) {
375 const properties = ['attributes', 'styles', 'classes'];
377 // Check if a tag in the universe is forbidden.
378 const allRequiredTags = _.keys(universe);
380 for (let i = 0; i < filterStatus.rules.length; i++) {
381 filterRule = filterStatus.rules[i];
382 if (filterRule.allow === false) {
383 if (_.intersection(allRequiredTags, filterRule.tags).length > 0) {
389 // Check if a property value of a tag in the universe is forbidden.
390 // For all filter rules…
391 for (let n = 0; n < filterStatus.rules.length; n++) {
392 filterRule = filterStatus.rules[n];
393 // … if there are tags with restricted property values …
394 if (filterRule.restrictedTags.tags.length && !emptyProperties(filterRule.restrictedTags.forbidden)) {
395 // … for all those tags …
396 for (let j = 0; j < filterRule.restrictedTags.tags.length; j++) {
397 const tag = filterRule.restrictedTags.tags[j];
398 // … then iterate over all properties …
399 for (let k = 0; k < properties.length; k++) {
400 const property = properties[k];
401 // … and return true if just one of the forbidden property
402 // values for this tag and property is listed in the universe.
403 if (findPropertyValuesOnTag(universe, tag, property, filterRule.restrictedTags.forbidden[property], false)) {
415 * Applies every filter rule's explicit allowing of a tag or a tag
416 * property value to the universe. Whenever both the tag and all of its
417 * required property values are marked as explicitly allowed, they are
418 * deleted from the universe.
420 * @param {object} universe
421 * Universe to delete from.
422 * @param {object} filterStatus
423 * The filter status in question.
425 function markAllowedTagsAndPropertyValues(universe, filterStatus) {
426 const properties = ['attributes', 'styles', 'classes'];
428 // Check if a tag in the universe is allowed.
431 for (let l = 0; !_.isEmpty(universe) && l < filterStatus.rules.length; l++) {
432 filterRule = filterStatus.rules[l];
433 if (filterRule.allow === true) {
434 for (let m = 0; !_.isEmpty(universe) && m < filterRule.tags.length; m++) {
435 tag = filterRule.tags[m];
436 if (_.has(universe, tag)) {
437 universe[tag].tag = true;
438 deleteFromUniverseIfAllowed(universe, tag);
444 // Check if a property value of a tag in the universe is allowed.
445 // For all filter rules…
446 for (let i = 0; !_.isEmpty(universe) && i < filterStatus.rules.length; i++) {
447 filterRule = filterStatus.rules[i];
448 // … if there are tags with restricted property values …
449 if (filterRule.restrictedTags.tags.length && !emptyProperties(filterRule.restrictedTags.allowed)) {
450 // … for all those tags …
451 for (let j = 0; !_.isEmpty(universe) && j < filterRule.restrictedTags.tags.length; j++) {
452 tag = filterRule.restrictedTags.tags[j];
453 // … then iterate over all properties …
454 for (let k = 0; k < properties.length; k++) {
455 const property = properties[k];
456 // … and try to delete this tag from the universe if just one
457 // of the allowed property values for this tag and property is
458 // listed in the universe. (Because everything might be allowed
460 if (findPropertyValuesOnTag(universe, tag, property, filterRule.restrictedTags.allowed[property], true)) {
461 deleteFromUniverseIfAllowed(universe, tag);
470 * Checks whether the current status of a filter allows a specific feature
471 * by building the universe of potential values from the feature's
472 * requirements and then checking whether anything in the filter prevents
475 * @param {object} filterStatus
476 * The filter status in question.
477 * @param {object} feature
478 * The feature requested.
481 * Whether the current status of the filter allows specified feature.
483 * @see generateUniverseFromFeatureRequirements()
485 function filterStatusAllowsFeature(filterStatus, feature) {
486 // An inactive filter by definition allows the feature.
487 if (!filterStatus.active) {
491 // A feature that specifies no rules has no HTML requirements and is
492 // hence allowed by definition.
493 if (feature.rules.length === 0) {
497 // Analogously for a filter that specifies no rules.
498 if (filterStatus.rules.length === 0) {
502 // Generate the universe U of possible values that can result from the
503 // feature's rules' requirements.
504 const universe = generateUniverseFromFeatureRequirements(feature);
506 // If anything that is in the universe (and is thus required by the
507 // feature) is forbidden by any of the filter's rules, then this filter
508 // does not allow this feature.
509 if (anyForbiddenFilterRuleMatches(universe, filterStatus)) {
513 // Mark anything in the universe that is allowed by any of the filter's
514 // rules as allowed. If everything is explicitly allowed, then the
515 // universe will become empty.
516 markAllowedTagsAndPropertyValues(universe, filterStatus);
518 // If there was at least one filter rule allowing tags, then everything
519 // in the universe must be allowed for this feature to be allowed, and
520 // thus by now it must be empty. However, it is still possible that the
521 // filter allows the feature, due to no rules for allowing tag property
522 // values and/or rules for forbidding tag property values. For details:
523 // see the comments below.
524 // @see generateUniverseFromFeatureRequirements()
525 if (_.some(_.pluck(filterStatus.rules, 'allow'))) {
526 // If the universe is empty, then everything was explicitly allowed
527 // and our job is done: this filter allows this feature!
528 if (_.isEmpty(universe)) {
531 // Otherwise, it is still possible that this feature is allowed.
533 // Every tag must be explicitly allowed if there are filter rules
534 // doing tag whitelisting.
535 if (!_.every(_.pluck(universe, 'tag'))) {
538 // Every tag was explicitly allowed, but since the universe is not
539 // empty, one or more tag properties are disallowed. However, if
540 // only blacklisting of tag properties was applied to these tags,
541 // and no whitelisting was ever applied, then it's still fine:
542 // since none of the tag properties were blacklisted, we got to
543 // this point, and since no whitelisting was applied, it doesn't
544 // matter that the properties: this could never have happened
545 // anyway. It's only this late that we can know this for certain.
547 const tags = _.keys(universe);
548 // Figure out if there was any rule applying whitelisting tag
549 // restrictions to each of the remaining tags.
550 for (let i = 0; i < tags.length; i++) {
552 if (_.has(universe, tag)) {
553 if (universe[tag].touchedByAllowedPropertyRule === false) {
554 delete universe[tag];
558 return _.isEmpty(universe);
560 // Otherwise, if all filter rules were doing blacklisting, then the sole
561 // fact that we got to this point indicates that this filter allows for
562 // everything that is required for this feature.
567 // If any filter's current status forbids the editor feature, return
569 Drupal.filterConfiguration.update();
570 for (const filterID in Drupal.filterConfiguration.statuses) {
571 if (Drupal.filterConfiguration.statuses.hasOwnProperty(filterID)) {
572 const filterStatus = Drupal.filterConfiguration.statuses[filterID];
573 if (!(filterStatusAllowsFeature(filterStatus, feature))) {
584 * Constructor for an editor feature HTML rule.
586 * Intended to be used in combination with {@link Drupal.EditorFeature}.
588 * A text editor feature rule object describes both:
589 * - required HTML tags, attributes, styles and classes: without these, the
590 * text editor feature is unable to function. It's possible that a
591 * - allowed HTML tags, attributes, styles and classes: these are optional
592 * in the strictest sense, but it is possible that the feature generates
595 * The structure can be very clearly seen below: there's a "required" and an
596 * "allowed" key. For each of those, there are objects with the "tags",
597 * "attributes", "styles" and "classes" keys. For all these keys the values
598 * are initialized to the empty array. List each possible value as an array
599 * value. Besides the "required" and "allowed" keys, there's an optional
600 * "raw" key: it allows text editor implementations to optionally pass in
601 * their raw representation instead of the Drupal-defined representation for
606 * attributes: ['href', 'alt']
607 * styles: ['color', 'text-decoration']
608 * classes: ['external', 'internal']
612 * @see Drupal.EditorFeature
614 Drupal.EditorFeatureHTMLRule = function () {
620 * @prop {Array} attributes
621 * @prop {Array} styles
622 * @prop {Array} classes
624 this.required = { tags: [], attributes: [], styles: [], classes: [] };
631 * @prop {Array} attributes
632 * @prop {Array} styles
633 * @prop {Array} classes
635 this.allowed = { tags: [], attributes: [], styles: [], classes: [] };
645 * A text editor feature object. Initialized with the feature name.
647 * Contains a set of HTML rules ({@link Drupal.EditorFeatureHTMLRule} objects)
648 * that describe which HTML tags, attributes, styles and classes are required
649 * (i.e. essential for the feature to function at all) and which are allowed
650 * (i.e. the feature may generate this, but they're not essential).
652 * It is necessary to allow for multiple HTML rules per feature: with just
653 * one HTML rule per feature, there is not enough expressiveness to describe
654 * certain cases. For example: a "table" feature would probably require the
655 * `<table>` tag, and might allow e.g. the "summary" attribute on that tag.
656 * However, the table feature would also require the `<tr>` and `<td>` tags,
657 * but it doesn't make sense to allow for a "summary" attribute on these tags.
658 * Hence these would need to be split in two separate rules.
660 * HTML rules must be added with the `addHTMLRule()` method. A feature that
661 * has zero HTML rules does not create or modify HTML.
665 * @param {string} name
666 * The name of the feature.
668 * @see Drupal.EditorFeatureHTMLRule
670 Drupal.EditorFeature = function (name) {
676 * Adds a HTML rule to the list of HTML rules for this feature.
678 * @param {Drupal.EditorFeatureHTMLRule} rule
679 * A text editor feature HTML rule.
681 Drupal.EditorFeature.prototype.addHTMLRule = function (rule) {
682 this.rules.push(rule);
686 * Text filter status object. Initialized with the filter ID.
688 * Indicates whether the text filter is currently active (enabled) or not.
690 * Contains a set of HTML rules ({@link Drupal.FilterHTMLRule} objects) that
691 * describe which HTML tags are allowed or forbidden. They can also describe
692 * for a set of tags (or all tags) which attributes, styles and classes are
693 * allowed and which are forbidden.
695 * It is necessary to allow for multiple HTML rules per feature, for
696 * analogous reasons as {@link Drupal.EditorFeature}.
698 * HTML rules must be added with the `addHTMLRule()` method. A filter that has
699 * zero HTML rules does not disallow any HTML.
703 * @param {string} name
704 * The name of the feature.
706 * @see Drupal.FilterHTMLRule
708 Drupal.FilterStatus = function (name) {
723 * @type {Array.<Drupal.FilterHTMLRule>}
729 * Adds a HTML rule to the list of HTML rules for this filter.
731 * @param {Drupal.FilterHTMLRule} rule
732 * A text filter HTML rule.
734 Drupal.FilterStatus.prototype.addHTMLRule = function (rule) {
735 this.rules.push(rule);
739 * A text filter HTML rule object.
741 * Intended to be used in combination with {@link Drupal.FilterStatus}.
743 * A text filter rule object describes:
744 * 1. allowed or forbidden tags: (optional) whitelist or blacklist HTML tags
745 * 2. restricted tag properties: (optional) whitelist or blacklist
746 * attributes, styles and classes on a set of HTML tags.
748 * Typically, each text filter rule object does either 1 or 2, not both.
750 * The structure can be very clearly seen below:
751 * 1. use the "tags" key to list HTML tags, and set the "allow" key to
752 * either true (to allow these HTML tags) or false (to forbid these HTML
753 * tags). If you leave the "tags" key's default value (the empty array),
754 * no restrictions are applied.
755 * 2. all nested within the "restrictedTags" key: use the "tags" subkey to
756 * list HTML tags to which you want to apply property restrictions, then
757 * use the "allowed" subkey to whitelist specific property values, and
758 * similarly use the "forbidden" subkey to blacklist specific property
762 * <caption>Whitelist the "p", "strong" and "a" HTML tags.</caption>
764 * tags: ['p', 'strong', 'a'],
768 * allowed: { attributes: [], styles: [], classes: [] },
769 * forbidden: { attributes: [], styles: [], classes: [] }
773 * <caption>For the "a" HTML tag, only allow the "href" attribute
774 * and the "external" class and disallow the "target" attribute.</caption>
780 * allowed: { attributes: ['href'], styles: [], classes: ['external'] },
781 * forbidden: { attributes: ['target'], styles: [], classes: [] }
785 * <caption>For all tags, allow the "data-*" attribute (that is, any
786 * attribute that begins with "data-").</caption>
792 * allowed: { attributes: ['data-*'], styles: [], classes: [] },
793 * forbidden: { attributes: [], styles: [], classes: [] }
798 * An object with the following structure:
805 * allowed: {attributes: Array, styles: Array, classes: Array},
806 * forbidden: {attributes: Array, styles: Array, classes: Array}
811 * @see Drupal.FilterStatus
813 Drupal.FilterHTMLRule = function () {
814 // Allow or forbid tags.
818 // Apply restrictions to properties set on tags.
819 this.restrictedTags = {
821 allowed: { attributes: [], styles: [], classes: [] },
822 forbidden: { attributes: [], styles: [], classes: [] },
828 Drupal.FilterHTMLRule.prototype.clone = function () {
829 const clone = new Drupal.FilterHTMLRule();
830 clone.tags = this.tags.slice(0);
831 clone.allow = this.allow;
832 clone.restrictedTags.tags = this.restrictedTags.tags.slice(0);
833 clone.restrictedTags.allowed.attributes = this.restrictedTags.allowed.attributes.slice(0);
834 clone.restrictedTags.allowed.styles = this.restrictedTags.allowed.styles.slice(0);
835 clone.restrictedTags.allowed.classes = this.restrictedTags.allowed.classes.slice(0);
836 clone.restrictedTags.forbidden.attributes = this.restrictedTags.forbidden.attributes.slice(0);
837 clone.restrictedTags.forbidden.styles = this.restrictedTags.forbidden.styles.slice(0);
838 clone.restrictedTags.forbidden.classes = this.restrictedTags.forbidden.classes.slice(0);
843 * Tracks the configuration of all text filters in {@link Drupal.FilterStatus}
844 * objects for {@link Drupal.editorConfiguration.featureIsAllowedByFilters}.
848 Drupal.filterConfiguration = {
851 * Drupal.FilterStatus objects, keyed by filter ID.
853 * @type {Object.<string, Drupal.FilterStatus>}
858 * Live filter setting parsers.
860 * Object keyed by filter ID, for those filters that implement it.
862 * Filters should load the implementing JavaScript on the filter
863 * configuration form and implement
864 * `Drupal.filterSettings[filterID].getRules()`, which should return an
865 * array of {@link Drupal.FilterHTMLRule} objects.
869 liveSettingParsers: {},
872 * Updates all {@link Drupal.FilterStatus} objects to reflect current state.
874 * Automatically checks whether a filter is currently enabled or not. To
875 * support more finegrained.
877 * If a filter implements a live setting parser, then that will be used to
878 * keep the HTML rules for the {@link Drupal.FilterStatus} object
882 for (const filterID in Drupal.filterConfiguration.statuses) {
883 if (Drupal.filterConfiguration.statuses.hasOwnProperty(filterID)) {
885 Drupal.filterConfiguration.statuses[filterID].active = $(`[name="filters[${filterID}][status]"]`).is(':checked');
887 // Update current rules.
888 if (Drupal.filterConfiguration.liveSettingParsers[filterID]) {
889 Drupal.filterConfiguration.statuses[filterID].rules = Drupal.filterConfiguration.liveSettingParsers[filterID].getRules();
898 * Initializes {@link Drupal.filterConfiguration}.
900 * @type {Drupal~behavior}
902 * @prop {Drupal~behaviorAttach} attach
903 * Gets filter configuration from filter form input.
905 Drupal.behaviors.initializeFilterConfiguration = {
906 attach(context, settings) {
907 const $context = $(context);
909 $context.find('#filters-status-wrapper input.form-checkbox').once('filter-editor-status').each(function () {
910 const $checkbox = $(this);
911 const nameAttribute = $checkbox.attr('name');
913 // The filter's checkbox has a name attribute of the form
914 // "filters[<name of filter>][status]", parse "<name of filter>"
916 const filterID = nameAttribute.substring(8, nameAttribute.indexOf(']'));
918 // Create a Drupal.FilterStatus object to track the state (whether it's
919 // active or not and its current settings, if any) of each filter.
920 Drupal.filterConfiguration.statuses[filterID] = new Drupal.FilterStatus(filterID);
924 }(jQuery, _, Drupal, document));