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 = {
18 * Must be called by a specific text editor's configuration whenever a
19 * feature is added by the user.
21 * Triggers the drupalEditorFeatureAdded event on the document, which
22 * receives a {@link Drupal.EditorFeature} object.
24 * @param {Drupal.EditorFeature} feature
25 * A text editor feature object.
27 * @fires event:drupalEditorFeatureAdded
29 addedFeature(feature) {
30 $(document).trigger('drupalEditorFeatureAdded', feature);
34 * Must be called by a specific text editor's configuration whenever a
35 * feature is removed by the user.
37 * Triggers the drupalEditorFeatureRemoved event on the document, which
38 * receives a {@link Drupal.EditorFeature} object.
40 * @param {Drupal.EditorFeature} feature
41 * A text editor feature object.
43 * @fires event:drupalEditorFeatureRemoved
45 removedFeature(feature) {
46 $(document).trigger('drupalEditorFeatureRemoved', feature);
50 * Must be called by a specific text editor's configuration whenever a
51 * feature is modified, i.e. has different rules.
53 * For example when the "Bold" button is configured to use the `<b>` tag
54 * instead of the `<strong>` tag.
56 * Triggers the drupalEditorFeatureModified event on the document, which
57 * receives a {@link Drupal.EditorFeature} object.
59 * @param {Drupal.EditorFeature} feature
60 * A text editor feature object.
62 * @fires event:drupalEditorFeatureModified
64 modifiedFeature(feature) {
65 $(document).trigger('drupalEditorFeatureModified', feature);
69 * May be called by a specific text editor's configuration whenever a
70 * feature is being added, to check whether it would require the filter
71 * settings to be updated.
73 * The canonical use case is when a text editor is being enabled:
75 * this would not cause the filter settings to be changed; rather, the
76 * default set of buttons (features) for the text editor should adjust
77 * itself to not cause filter setting changes.
79 * Note: for filters to integrate with this functionality, it is necessary
81 * `Drupal.filterSettingsForEditors[filterID].getRules()`.
83 * @param {Drupal.EditorFeature} feature
84 * A text editor feature object.
87 * Whether the given feature is allowed by the current filters.
89 featureIsAllowedByFilters(feature) {
91 * Provided a section of a feature or filter rule, checks if no property
92 * values are defined for all properties: attributes, classes and styles.
94 * @param {object} section
95 * The section to check.
98 * Returns true if the section has empty properties, false otherwise.
100 function emptyProperties(section) {
102 section.attributes.length === 0 &&
103 section.classes.length === 0 &&
104 section.styles.length === 0
109 * Generate the universe U of possible values that can result from the
110 * feature's rules' requirements.
112 * This generates an object of this form:
115 * 'touchedByAllowedPropertyRule': false,
117 * 'attributes:href': false,
118 * 'classes:external': false,
121 * 'touchedByAllowedPropertyRule': false,
125 * 'touchedByAllowedPropertyRule': false,
127 * 'attributes:src': false
131 * In this example, the given text editor feature resulted in the above
132 * universe, which shows that it must be allowed to generate the a,
133 * strong and img tags. For the a tag, it must be able to set the "href"
134 * attribute and the "external" class. For the strong tag, no further
135 * properties are required. For the img tag, the "src" attribute is
136 * required. The "tag" key is used to track whether that tag was
137 * explicitly allowed by one of the filter's rules. The
138 * "touchedByAllowedPropertyRule" key is used for state tracking that is
139 * essential for filterStatusAllowsFeature() to be able to reason: when
140 * all of a filter's rules have been applied, and none of the forbidden
141 * rules matched (which would have resulted in early termination) yet the
142 * universe has not been made empty (which would be the end result if
143 * everything in the universe were explicitly allowed), then this piece
144 * of state data enables us to determine whether a tag whose properties
145 * were not all explicitly allowed are in fact still allowed, because its
146 * tag was explicitly allowed and there were no filter rules applying
147 * "allowed tag property value" restrictions for this particular tag.
149 * @param {object} feature
150 * The feature in question.
153 * The universe generated.
155 * @see findPropertyValueOnTag()
156 * @see filterStatusAllowsFeature()
158 function generateUniverseFromFeatureRequirements(feature) {
159 const properties = ['attributes', 'styles', 'classes'];
162 for (let r = 0; r < feature.rules.length; r++) {
163 const featureRule = feature.rules[r];
165 // For each tag required by this feature rule, create a basic entry in
167 const requiredTags = featureRule.required.tags;
168 for (let t = 0; t < requiredTags.length; t++) {
169 universe[requiredTags[t]] = {
170 // Whether this tag was allowed or not.
172 // Whether any filter rule that applies to this tag had an allowed
173 // property rule. i.e. will become true if >=1 filter rule has >=1
174 // allowed property rule.
175 touchedByAllowedPropertyRule: false,
176 // Analogous, but for forbidden property rule.
177 touchedBytouchedByForbiddenPropertyRule: false,
181 // If no required properties are defined for this rule, we can move on
182 // to the next feature.
183 if (emptyProperties(featureRule.required)) {
187 // Expand the existing universe, assume that each tags' property
188 // value is disallowed. If the filter rules allow everything in the
189 // feature's universe, then the feature is allowed.
190 for (let p = 0; p < properties.length; p++) {
191 const property = properties[p];
192 for (let pv = 0; pv < featureRule.required[property].length; pv++) {
193 const propertyValue = featureRule.required[property];
194 universe[requiredTags][`${property}:${propertyValue}`] = false;
203 * Finds out if a specific property value (potentially containing
204 * wildcards) exists on the given tag. When the "allowing" parameter
205 * equals true, the universe will be updated if that specific property
206 * value exists. Returns true if found, false otherwise.
208 * @param {object} universe
209 * The universe to check.
210 * @param {string} tag
211 * The tag to look for.
212 * @param {string} property
213 * The property to check.
214 * @param {string} propertyValue
215 * The property value to check.
216 * @param {bool} allowing
217 * Whether to update the universe or not.
220 * Returns true if found, false otherwise.
222 function findPropertyValueOnTag(
229 // If the tag does not exist in the universe, then it definitely can't
230 // have this specific property value.
231 if (!_.has(universe, tag)) {
235 const key = `${property}:${propertyValue}`;
237 // Track whether a tag was touched by a filter rule that allows specific
238 // property values on this particular tag.
239 // @see generateUniverseFromFeatureRequirements
241 universe[tag].touchedByAllowedPropertyRule = true;
244 // The simple case: no wildcard in property value.
245 if (_.indexOf(propertyValue, '*') === -1) {
246 if (_.has(universe, tag) && _.has(universe[tag], key)) {
248 universe[tag][key] = true;
254 // The complex case: wildcard in property value.
256 let atLeastOneFound = false;
257 const regex = key.replace(/\*/g, '[^ ]*');
258 _.each(_.keys(universe[tag]), key => {
259 if (key.match(regex)) {
260 atLeastOneFound = true;
262 universe[tag][key] = true;
266 return atLeastOneFound;
270 * Calls findPropertyValuesOnAllTags for all tags in the universe.
272 * @param {object} universe
273 * The universe to check.
274 * @param {string} property
275 * The property to check.
276 * @param {Array} propertyValues
277 * Values of the property to check.
278 * @param {bool} allowing
279 * Whether to update the universe or not.
282 * Returns true if found, false otherwise.
284 function findPropertyValuesOnAllTags(
290 let atLeastOneFound = false;
291 _.each(_.keys(universe), tag => {
293 // eslint-disable-next-line no-use-before-define
294 findPropertyValuesOnTag(
302 atLeastOneFound = true;
305 return atLeastOneFound;
309 * Calls findPropertyValueOnTag on the given tag for every property value
310 * that is listed in the "propertyValues" parameter. Supports the wildcard
313 * @param {object} universe
314 * The universe to check.
315 * @param {string} tag
316 * The tag to look for.
317 * @param {string} property
318 * The property to check.
319 * @param {Array} propertyValues
320 * Values of the property to check.
321 * @param {bool} allowing
322 * Whether to update the universe or not.
325 * Returns true if found, false otherwise.
327 function findPropertyValuesOnTag(
334 // Detect the wildcard case.
336 return findPropertyValuesOnAllTags(
344 let atLeastOneFound = false;
345 _.each(propertyValues, propertyValue => {
347 findPropertyValueOnTag(
355 atLeastOneFound = true;
358 return atLeastOneFound;
362 * Calls deleteFromUniverseIfAllowed for all tags in the universe.
364 * @param {object} universe
365 * The universe to delete from.
368 * Whether something was deleted from the universe.
370 function deleteAllTagsFromUniverseIfAllowed(universe) {
371 let atLeastOneDeleted = false;
372 _.each(_.keys(universe), tag => {
373 // eslint-disable-next-line no-use-before-define
374 if (deleteFromUniverseIfAllowed(universe, tag)) {
375 atLeastOneDeleted = true;
378 return atLeastOneDeleted;
382 * Deletes a tag from the universe if the tag itself and each of its
383 * properties are marked as allowed.
385 * @param {object} universe
386 * The universe to delete from.
387 * @param {string} tag
391 * Whether something was deleted from the universe.
393 function deleteFromUniverseIfAllowed(universe, tag) {
394 // Detect the wildcard case.
396 return deleteAllTagsFromUniverseIfAllowed(universe);
399 _.has(universe, tag) &&
400 _.every(_.omit(universe[tag], 'touchedByAllowedPropertyRule'))
402 delete universe[tag];
409 * Checks if any filter rule forbids either a tag or a tag property value
410 * that exists in the universe.
412 * @param {object} universe
414 * @param {object} filterStatus
415 * Filter status to use for check.
418 * Whether any filter rule forbids something in the universe.
420 function anyForbiddenFilterRuleMatches(universe, filterStatus) {
421 const properties = ['attributes', 'styles', 'classes'];
423 // Check if a tag in the universe is forbidden.
424 const allRequiredTags = _.keys(universe);
426 for (let i = 0; i < filterStatus.rules.length; i++) {
427 filterRule = filterStatus.rules[i];
428 if (filterRule.allow === false) {
429 if (_.intersection(allRequiredTags, filterRule.tags).length > 0) {
435 // Check if a property value of a tag in the universe is forbidden.
436 // For all filter rules…
437 for (let n = 0; n < filterStatus.rules.length; n++) {
438 filterRule = filterStatus.rules[n];
439 // … if there are tags with restricted property values …
441 filterRule.restrictedTags.tags.length &&
442 !emptyProperties(filterRule.restrictedTags.forbidden)
444 // … for all those tags …
445 for (let j = 0; j < filterRule.restrictedTags.tags.length; j++) {
446 const tag = filterRule.restrictedTags.tags[j];
447 // … then iterate over all properties …
448 for (let k = 0; k < properties.length; k++) {
449 const property = properties[k];
450 // … and return true if just one of the forbidden property
451 // values for this tag and property is listed in the universe.
453 findPropertyValuesOnTag(
457 filterRule.restrictedTags.forbidden[property],
472 * Applies every filter rule's explicit allowing of a tag or a tag
473 * property value to the universe. Whenever both the tag and all of its
474 * required property values are marked as explicitly allowed, they are
475 * deleted from the universe.
477 * @param {object} universe
478 * Universe to delete from.
479 * @param {object} filterStatus
480 * The filter status in question.
482 function markAllowedTagsAndPropertyValues(universe, filterStatus) {
483 const properties = ['attributes', 'styles', 'classes'];
485 // Check if a tag in the universe is allowed.
490 !_.isEmpty(universe) && l < filterStatus.rules.length;
493 filterRule = filterStatus.rules[l];
494 if (filterRule.allow === true) {
497 !_.isEmpty(universe) && m < filterRule.tags.length;
500 tag = filterRule.tags[m];
501 if (_.has(universe, tag)) {
502 universe[tag].tag = true;
503 deleteFromUniverseIfAllowed(universe, tag);
509 // Check if a property value of a tag in the universe is allowed.
510 // For all filter rules…
513 !_.isEmpty(universe) && i < filterStatus.rules.length;
516 filterRule = filterStatus.rules[i];
517 // … if there are tags with restricted property values …
519 filterRule.restrictedTags.tags.length &&
520 !emptyProperties(filterRule.restrictedTags.allowed)
522 // … for all those tags …
525 !_.isEmpty(universe) && j < filterRule.restrictedTags.tags.length;
528 tag = filterRule.restrictedTags.tags[j];
529 // … then iterate over all properties …
530 for (let k = 0; k < properties.length; k++) {
531 const property = properties[k];
532 // … and try to delete this tag from the universe if just one
533 // of the allowed property values for this tag and property is
534 // listed in the universe. (Because everything might be allowed
537 findPropertyValuesOnTag(
541 filterRule.restrictedTags.allowed[property],
545 deleteFromUniverseIfAllowed(universe, tag);
554 * Checks whether the current status of a filter allows a specific feature
555 * by building the universe of potential values from the feature's
556 * requirements and then checking whether anything in the filter prevents
559 * @param {object} filterStatus
560 * The filter status in question.
561 * @param {object} feature
562 * The feature requested.
565 * Whether the current status of the filter allows specified feature.
567 * @see generateUniverseFromFeatureRequirements()
569 function filterStatusAllowsFeature(filterStatus, feature) {
570 // An inactive filter by definition allows the feature.
571 if (!filterStatus.active) {
575 // A feature that specifies no rules has no HTML requirements and is
576 // hence allowed by definition.
577 if (feature.rules.length === 0) {
581 // Analogously for a filter that specifies no rules.
582 if (filterStatus.rules.length === 0) {
586 // Generate the universe U of possible values that can result from the
587 // feature's rules' requirements.
588 const universe = generateUniverseFromFeatureRequirements(feature);
590 // If anything that is in the universe (and is thus required by the
591 // feature) is forbidden by any of the filter's rules, then this filter
592 // does not allow this feature.
593 if (anyForbiddenFilterRuleMatches(universe, filterStatus)) {
597 // Mark anything in the universe that is allowed by any of the filter's
598 // rules as allowed. If everything is explicitly allowed, then the
599 // universe will become empty.
600 markAllowedTagsAndPropertyValues(universe, filterStatus);
602 // If there was at least one filter rule allowing tags, then everything
603 // in the universe must be allowed for this feature to be allowed, and
604 // thus by now it must be empty. However, it is still possible that the
605 // filter allows the feature, due to no rules for allowing tag property
606 // values and/or rules for forbidding tag property values. For details:
607 // see the comments below.
608 // @see generateUniverseFromFeatureRequirements()
609 if (_.some(_.pluck(filterStatus.rules, 'allow'))) {
610 // If the universe is empty, then everything was explicitly allowed
611 // and our job is done: this filter allows this feature!
612 if (_.isEmpty(universe)) {
615 // Otherwise, it is still possible that this feature is allowed.
617 // Every tag must be explicitly allowed if there are filter rules
618 // doing tag whitelisting.
619 if (!_.every(_.pluck(universe, 'tag'))) {
622 // Every tag was explicitly allowed, but since the universe is not
623 // empty, one or more tag properties are disallowed. However, if
624 // only blacklisting of tag properties was applied to these tags,
625 // and no whitelisting was ever applied, then it's still fine:
626 // since none of the tag properties were blacklisted, we got to
627 // this point, and since no whitelisting was applied, it doesn't
628 // matter that the properties: this could never have happened
629 // anyway. It's only this late that we can know this for certain.
631 const tags = _.keys(universe);
632 // Figure out if there was any rule applying whitelisting tag
633 // restrictions to each of the remaining tags.
634 for (let i = 0; i < tags.length; i++) {
636 if (_.has(universe, tag)) {
637 if (universe[tag].touchedByAllowedPropertyRule === false) {
638 delete universe[tag];
642 return _.isEmpty(universe);
644 // Otherwise, if all filter rules were doing blacklisting, then the sole
645 // fact that we got to this point indicates that this filter allows for
646 // everything that is required for this feature.
651 // If any filter's current status forbids the editor feature, return
653 Drupal.filterConfiguration.update();
654 return Object.keys(Drupal.filterConfiguration.statuses).every(filterID =>
655 filterStatusAllowsFeature(
656 Drupal.filterConfiguration.statuses[filterID],
664 * Constructor for an editor feature HTML rule.
666 * Intended to be used in combination with {@link Drupal.EditorFeature}.
668 * A text editor feature rule object describes both:
669 * - required HTML tags, attributes, styles and classes: without these, the
670 * text editor feature is unable to function. It's possible that a
671 * - allowed HTML tags, attributes, styles and classes: these are optional
672 * in the strictest sense, but it is possible that the feature generates
675 * The structure can be very clearly seen below: there's a "required" and an
676 * "allowed" key. For each of those, there are objects with the "tags",
677 * "attributes", "styles" and "classes" keys. For all these keys the values
678 * are initialized to the empty array. List each possible value as an array
679 * value. Besides the "required" and "allowed" keys, there's an optional
680 * "raw" key: it allows text editor implementations to optionally pass in
681 * their raw representation instead of the Drupal-defined representation for
686 * attributes: ['href', 'alt']
687 * styles: ['color', 'text-decoration']
688 * classes: ['external', 'internal']
692 * @see Drupal.EditorFeature
694 Drupal.EditorFeatureHTMLRule = function() {
700 * @prop {Array} attributes
701 * @prop {Array} styles
702 * @prop {Array} classes
716 * @prop {Array} attributes
717 * @prop {Array} styles
718 * @prop {Array} classes
735 * A text editor feature object. Initialized with the feature name.
737 * Contains a set of HTML rules ({@link Drupal.EditorFeatureHTMLRule} objects)
738 * that describe which HTML tags, attributes, styles and classes are required
739 * (i.e. essential for the feature to function at all) and which are allowed
740 * (i.e. the feature may generate this, but they're not essential).
742 * It is necessary to allow for multiple HTML rules per feature: with just
743 * one HTML rule per feature, there is not enough expressiveness to describe
744 * certain cases. For example: a "table" feature would probably require the
745 * `<table>` tag, and might allow e.g. the "summary" attribute on that tag.
746 * However, the table feature would also require the `<tr>` and `<td>` tags,
747 * but it doesn't make sense to allow for a "summary" attribute on these tags.
748 * Hence these would need to be split in two separate rules.
750 * HTML rules must be added with the `addHTMLRule()` method. A feature that
751 * has zero HTML rules does not create or modify HTML.
755 * @param {string} name
756 * The name of the feature.
758 * @see Drupal.EditorFeatureHTMLRule
760 Drupal.EditorFeature = function(name) {
766 * Adds a HTML rule to the list of HTML rules for this feature.
768 * @param {Drupal.EditorFeatureHTMLRule} rule
769 * A text editor feature HTML rule.
771 Drupal.EditorFeature.prototype.addHTMLRule = function(rule) {
772 this.rules.push(rule);
776 * Text filter status object. Initialized with the filter ID.
778 * Indicates whether the text filter is currently active (enabled) or not.
780 * Contains a set of HTML rules ({@link Drupal.FilterHTMLRule} objects) that
781 * describe which HTML tags are allowed or forbidden. They can also describe
782 * for a set of tags (or all tags) which attributes, styles and classes are
783 * allowed and which are forbidden.
785 * It is necessary to allow for multiple HTML rules per feature, for
786 * analogous reasons as {@link Drupal.EditorFeature}.
788 * HTML rules must be added with the `addHTMLRule()` method. A filter that has
789 * zero HTML rules does not disallow any HTML.
793 * @param {string} name
794 * The name of the feature.
796 * @see Drupal.FilterHTMLRule
798 Drupal.FilterStatus = function(name) {
813 * @type {Array.<Drupal.FilterHTMLRule>}
819 * Adds a HTML rule to the list of HTML rules for this filter.
821 * @param {Drupal.FilterHTMLRule} rule
822 * A text filter HTML rule.
824 Drupal.FilterStatus.prototype.addHTMLRule = function(rule) {
825 this.rules.push(rule);
829 * A text filter HTML rule object.
831 * Intended to be used in combination with {@link Drupal.FilterStatus}.
833 * A text filter rule object describes:
834 * 1. allowed or forbidden tags: (optional) whitelist or blacklist HTML tags
835 * 2. restricted tag properties: (optional) whitelist or blacklist
836 * attributes, styles and classes on a set of HTML tags.
838 * Typically, each text filter rule object does either 1 or 2, not both.
840 * The structure can be very clearly seen below:
841 * 1. use the "tags" key to list HTML tags, and set the "allow" key to
842 * either true (to allow these HTML tags) or false (to forbid these HTML
843 * tags). If you leave the "tags" key's default value (the empty array),
844 * no restrictions are applied.
845 * 2. all nested within the "restrictedTags" key: use the "tags" subkey to
846 * list HTML tags to which you want to apply property restrictions, then
847 * use the "allowed" subkey to whitelist specific property values, and
848 * similarly use the "forbidden" subkey to blacklist specific property
852 * <caption>Whitelist the "p", "strong" and "a" HTML tags.</caption>
854 * tags: ['p', 'strong', 'a'],
858 * allowed: { attributes: [], styles: [], classes: [] },
859 * forbidden: { attributes: [], styles: [], classes: [] }
863 * <caption>For the "a" HTML tag, only allow the "href" attribute
864 * and the "external" class and disallow the "target" attribute.</caption>
870 * allowed: { attributes: ['href'], styles: [], classes: ['external'] },
871 * forbidden: { attributes: ['target'], styles: [], classes: [] }
875 * <caption>For all tags, allow the "data-*" attribute (that is, any
876 * attribute that begins with "data-").</caption>
882 * allowed: { attributes: ['data-*'], styles: [], classes: [] },
883 * forbidden: { attributes: [], styles: [], classes: [] }
888 * An object with the following structure:
895 * allowed: {attributes: Array, styles: Array, classes: Array},
896 * forbidden: {attributes: Array, styles: Array, classes: Array}
901 * @see Drupal.FilterStatus
903 Drupal.FilterHTMLRule = function() {
904 // Allow or forbid tags.
908 // Apply restrictions to properties set on tags.
909 this.restrictedTags = {
911 allowed: { attributes: [], styles: [], classes: [] },
912 forbidden: { attributes: [], styles: [], classes: [] },
918 Drupal.FilterHTMLRule.prototype.clone = function() {
919 const clone = new Drupal.FilterHTMLRule();
920 clone.tags = this.tags.slice(0);
921 clone.allow = this.allow;
922 clone.restrictedTags.tags = this.restrictedTags.tags.slice(0);
923 clone.restrictedTags.allowed.attributes = this.restrictedTags.allowed.attributes.slice(
926 clone.restrictedTags.allowed.styles = this.restrictedTags.allowed.styles.slice(
929 clone.restrictedTags.allowed.classes = this.restrictedTags.allowed.classes.slice(
932 clone.restrictedTags.forbidden.attributes = this.restrictedTags.forbidden.attributes.slice(
935 clone.restrictedTags.forbidden.styles = this.restrictedTags.forbidden.styles.slice(
938 clone.restrictedTags.forbidden.classes = this.restrictedTags.forbidden.classes.slice(
945 * Tracks the configuration of all text filters in {@link Drupal.FilterStatus}
946 * objects for {@link Drupal.editorConfiguration.featureIsAllowedByFilters}.
950 Drupal.filterConfiguration = {
952 * Drupal.FilterStatus objects, keyed by filter ID.
954 * @type {Object.<string, Drupal.FilterStatus>}
959 * Live filter setting parsers.
961 * Object keyed by filter ID, for those filters that implement it.
963 * Filters should load the implementing JavaScript on the filter
964 * configuration form and implement
965 * `Drupal.filterSettings[filterID].getRules()`, which should return an
966 * array of {@link Drupal.FilterHTMLRule} objects.
970 liveSettingParsers: {},
973 * Updates all {@link Drupal.FilterStatus} objects to reflect current state.
975 * Automatically checks whether a filter is currently enabled or not. To
976 * support more finegrained.
978 * If a filter implements a live setting parser, then that will be used to
979 * keep the HTML rules for the {@link Drupal.FilterStatus} object
983 Object.keys(Drupal.filterConfiguration.statuses || {}).forEach(
986 Drupal.filterConfiguration.statuses[filterID].active = $(
987 `[name="filters[${filterID}][status]"]`,
990 // Update current rules.
991 if (Drupal.filterConfiguration.liveSettingParsers[filterID]) {
992 Drupal.filterConfiguration.statuses[
994 ].rules = Drupal.filterConfiguration.liveSettingParsers[
1004 * Initializes {@link Drupal.filterConfiguration}.
1006 * @type {Drupal~behavior}
1008 * @prop {Drupal~behaviorAttach} attach
1009 * Gets filter configuration from filter form input.
1011 Drupal.behaviors.initializeFilterConfiguration = {
1012 attach(context, settings) {
1013 const $context = $(context);
1016 .find('#filters-status-wrapper input.form-checkbox')
1017 .once('filter-editor-status')
1019 const $checkbox = $(this);
1020 const nameAttribute = $checkbox.attr('name');
1022 // The filter's checkbox has a name attribute of the form
1023 // "filters[<name of filter>][status]", parse "<name of filter>"
1025 const filterID = nameAttribute.substring(
1027 nameAttribute.indexOf(']'),
1030 // Create a Drupal.FilterStatus object to track the state (whether it's
1031 // active or not and its current settings, if any) of each filter.
1032 Drupal.filterConfiguration.statuses[
1034 ] = new Drupal.FilterStatus(filterID);
1038 })(jQuery, _, Drupal, document);