PageSourceSearch

https://www.onespan.com/modules/contrib/multiselect_dropdown/js/dist/multiselect-dropdowns.js

js onespan.com collected 2026-10-01 09:08:51 UTC 15,878 bytes, 546 lines download raw bytes

1/**
2 * @file
3 * Object for interacting with multiselect dropdowns and tracking state.
4 */
5
6'use strict';
7
8function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
9function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
10function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
11function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
12function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
13var _attribute = /*#__PURE__*/new WeakMap();
14var _selector = /*#__PURE__*/new WeakMap();
15var _drupalAttribute = /*#__PURE__*/new WeakMap();
16var _opened = /*#__PURE__*/new WeakMap();
17var _persistedOnLoad = /*#__PURE__*/new WeakMap();
18var _focus = /*#__PURE__*/new WeakMap();
19var _shouldFocus = /*#__PURE__*/new WeakMap();
20var _search = /*#__PURE__*/new WeakMap();
21var _type = /*#__PURE__*/new WeakMap();
22export default class MultiselectDropdowns {
23  constructor() {
24    /**
25     * Multiselect dropdown data attribute base.
26     *
27     * @type {String}
28     */
29    _classPrivateFieldInitSpec(this, _attribute, 'data-multiselect-dropdown');
30    /**
31     * Multiselect dropdown selector.
32     *
33     * @type {String}
34     */
35    _classPrivateFieldInitSpec(this, _selector, "[".concat(_classPrivateFieldGet(_attribute, this), "]"));
36    /**
37     * Drupal data selector attribute.
38     *
39     * @type {String}
40     */
41    _classPrivateFieldInitSpec(this, _drupalAttribute, 'data-drupal-selector');
42    /**
43     * Opened multiselect dropdown dialogs.
44     *
45     * @type {Array}
46     */
47    _classPrivateFieldInitSpec(this, _opened, []);
48    /**
49     * Multiselect dropdown dialogs to open on load.
50     *
51     * @type {Array}
52     */
53    _classPrivateFieldInitSpec(this, _persistedOnLoad, []);
54    /**
55     * Selector to focus on AJAX load.
56     *
57     * @type {String}
58     */
59    _classPrivateFieldInitSpec(this, _focus, '');
60    /**
61     * Focus should happen on load.
62     *
63     * @type {Boolean}
64     */
65    _classPrivateFieldInitSpec(this, _shouldFocus, false);
66    /**
67     * Search text to add on AJAX load.
68     *
69     * @type {Map}
70     */
71    _classPrivateFieldInitSpec(this, _search, new Map());
72    /**
73     * The type of modal.
74     *
75     * Values map to the ModalType enum.
76     *
77     * @type {{breakpoint: String, dialog: String, modal: String}}
78     */
79    _classPrivateFieldInitSpec(this, _type, {
80      breakpoint: 'breakpoint',
81      dialog: 'dialog',
82      modal: 'modal'
83    });
84  }
85  /**
86   * Get the ID of a multiselect dropdown.
87   *
88   * @param {HTMLElement} multiselect
89   *
90   * @return {String}
91   */
92  getId(multiselect) {
93    return multiselect.getAttribute(_classPrivateFieldGet(_drupalAttribute, this));
94  }
95
96  /**
97   * Generate a unique selector for a multiselect dropdown.
98   *
99   * @param {HTMLElement} multiselect
100   *
101   * @return {String}
102   */
103  getSelector(multiselect) {
104    return "[".concat(_classPrivateFieldGet(_drupalAttribute, this), "=\"").concat(this.getId(multiselect), "\"]");
105  }
106
107  /**
108   * Get the toggle button for a multiselect dropdown.
109   *
110   * @param {HTMLElement} multiselect
111   *
112   * @return {HTMLButtonElement|null}
113   */
114  getToggle(multiselect) {
115    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-toggle]"));
116  }
117
118  /**
119   * Get the dialog for a multiselect dropdown.
120   *
121   * @param {HTMLElement} multiselect
122   *
123   * @return {HTMLDialogElement|null}
124   */
125  getDialog(multiselect) {
126    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-dialog]"));
127  }
128
129  /**
130   * Get the dialog close button for a multiselect dropdown.
131   *
132   * @param {HTMLElement} multiselect
133   *
134   * @return {HTMLDialogElement|null}
135   */
136  getDialogClose(multiselect) {
137    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-
137dialog-close]"));
138  }
139
140  /**
141   * Get the select all button for a multiselect dropdown.
142   *
143   * @param {HTMLElement} multiselect
144   *
145   * @return {HTMLButtonElement|null}
146   */
147  getSelectAll(multiselect) {
148    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-select-all]"));
149  }
150
151  /**
152   * Get the select none button for a multiselect dropdown.
153   *
154   * @param {HTMLElement} multiselect
155   *
156   * @return {HTMLButtonElement|null}
157   */
158  getSelectNone(multiselect) {
159    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-select-none]"));
160  }
161
162  /**
163   * Get the search input for a multiselect dropdown.
164   *
165   * @param {HTMLElement} multiselect
166   *
167   * @returns {HTMLInputElement|null}
168   */
169  getSearchInput(multiselect) {
170    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-search]"));
171  }
172
173  /**
174   * Get the checkbox list for a multiselect dropdown.
175   *
176   * @param {HTMLElement} multiselect
177   *
178   * @return {HTMLUListElement|null}
179   */
180  getList(multiselect) {
181    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-list]"));
182  }
183
184  /**
185   * Get the checkbox list items for a multiselect dropdown.
186   *
187   * @param {HTMLElement} multiselect
188   *
189   * @return {NodeListOf<HTMLLIElement>}
190   */
191  getListItems(multiselect) {
192    return this.getList(multiselect).querySelectorAll('li');
193  }
194
195  /**
196   * Get the submit button for a multiselect dropdown.
197   *
198   * @param {HTMLElement} multiselect
199   *
200   * @return {HTMLButtonElement|null}
201   */
202  getSubmit(multiselect) {
203    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-submit]"));
204  }
205
206  /**
207   * Get the clear button for a multiselect dropdown.
208   *
209   * @param {HTMLElement} multiselect
210   *
211   * @return {HTMLButtonElement|null}
212   */
213  getClear(multiselect) {
214    return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-clear]"));
215  }
216
217  /**
218   * Get the multiselect dropdown checkboxes.
219   *
220   * @param {HTMLElement} multiselect
221   *
222   * @return {NodeListOf<Element|HTMLInputElement>}
223   */
224  static getCheckboxes(multiselect) {
225    return multiselect.querySelectorAll('[type="checkbox"]');
226  }
227
228  /**
229   * Get the checked multiselect dropdown checkboxes.
230   *
231   * @param {HTMLElement} multiselect
232   *
233   * @return {NodeListOf<Element|HTMLInputElement>}
234   */
235  static getCheckboxesChecked(multiselect) {
236    return multiselect.querySelectorAll('[type="checkbox"]:checked');
237  }
238
239  /**
240   * Get the unchecked multiselect dropdown checkboxes.
241   *
242   * @param {HTMLElement} multiselect
243   *
244   * @return {NodeListOf<Element|HTMLInputElement>}
245   */
246  static getCheckboxesUnchecked(multiselect) {
247    return multiselect.querySelectorAll('[type="checkbox"]:not(:checked)');
248  }
249
250  /**
251   * Get the breakpoint of a multiselect dropdown.
252   *
253   * @param {HTMLElement} multiselect
254   *
255   * @return {Number}
256   */
257  getBreakpoint(multiselect) {
258    const breakpoint = multiselect.getAttribute('data-multiselect-dropdown-breakpoint');
259    switch (breakpoint) {
260      case _classPrivateFieldGet(_type, this).dialog:
261        return 0;
262      case _classPrivateFieldGet(_type, this).modal:
263        return 99999999;
264      default:
265        return Number.parseInt(breakpoint, 10);
266    }
267  }
268
269  /**
270   * Get all multiselect dropdown instances contained in an HTML element.
271   *
272   * @param {Document|HTMLElement} html
273   *
274   * @return {NodeListOf<Element|HTMLElement>|HTMLElement[]}
275   */
276  getInstances(html) {
277    if (html instanceof HTMLElement && html.matches(_classPrivateFieldGet(_selector, this))) {
278      return [html];
279    }
280    return html.querySelectorAll(_classPrivateFieldGet(_selector, this));
281  }
282
283  /**
284   * Get a multiselect from an element contained in the multiselect.
285   *
286   * @param {Element|HTMLElement} element
287   *
288   * @return {Element|HTMLElement|null}
289   */
290  getInstance(element) {
291    return element.closest(_classPrivateFieldGet(_selector, this));
292  }
293
294  /**
295   * Set an element to focus on.
296   *
297   * @param {HTMLElement|null} element
298   */
299  setFocus(element) {
300    _classPrivateFieldSet(_shouldFocus, this, true);
301    _classPrivateFieldSet(_focus, this, element ? "[data-drupal-selector=\"".concat(element.getAttribute('data-drupal-selector'), "\"]") : '');
302  }
303
304  /**
305   * Get the focus selector.
306   *
307   * If no focus selector is set, focus the view results.
308   *
309   * @return {String}
310   */
311  getFocus() {
312    if (!_classPrivateFieldGet(_shouldFocus, this)) {
313      return '';
314    }
315    return _classPrivateFieldGet(_focus, this) ? _classPrivateFieldGet(_focus, this) : '.view-content, .view-empty, [data-multiselect-dropdown-view-results]';
316  }
317
318  /**
319   * Set the search text for a multiselect.
320   *
321   * @param {HTMLElement} multiselect
322   * @param {String} value
323   */
324  setSearch(multiselect, value) {
325    if (value) {
326      _classPrivateFieldGet(_search, this).set(this.getId(multiselect), value);
327    } else {
328      _classPrivateFieldGet(_search, this).delete(this.getId(multiselect));
329    }
330  }
331
332  /**
333   * Get the search text for a multiselect.
334   *
335   * @param {HTMLElement} multiselect
336   *
337   * @return {String}
338   */
339  getSearch(multiselect) {
340    const value = _classPrivateFieldGet(_search, this).get(this.getId(multiselect));
341    return value === undefined ? '' : value;
342  }
343
344  /**
345   * Check if an element is a multiselect dropdown.
346   *
347   * @param {HTMLElement} element
348   *
349   * @return {Boolean}
350   */
351  isMultiselect(element) {
352    return element.hasAttribute(_classPrivateFieldGet(_attribute, this));
353  }
354
355  /**
356   * Check if a multiselect dropdown is open.
357   *
358   * @param {HTMLElement} multiselect
359   *
360   * @return {Boolean}
361   */
362  isOpen(multiselect) {
363    return _classPrivateFieldGet(_opened, this).includes(this.getId(multiselect));
364  }
365
366  /**
367   * Open a multiselect dropdown.
368   *
369   * @param {HTMLElement} multiselect
370   * @param {Boolean} userTriggered
371   */
372  open(multiselect, userTriggered) {
373    const dialog = this.getDialog(multiselect);
374    if (!dialog) {
375      throw new Error('Multiselect does not contain an HTMLDialogElement with the attribute data-multiselect-dropdown-dialog.');
376    }
377    if (window.innerWidth <= this.getBreakpoint(multiselect)) {
378      dialog.showModal();
379    } else if (userTriggered) {
380      dialog.show();
381    } else {
382      dialog.open = true;
383    }
384    multiselect.setAttribute('data-multiselect-dropdown-open', '');
385    const toggle = this.getToggle(multiselect);
386    if (toggle) {
387      toggle.setAttribute('aria-expanded', 'true');
388    }
389    dialog.addEventListener('close', () => {
390      this.close(multiselect);
391    });
392    if (!this.isOpen(multiselect)) {
393      _classPrivateFieldGet(_opened, this).push(this.getId(multiselect));
394    }
395    if (this.shouldOpenOnLoad(multiselect)) {
396      this.persistOnLoad(multiselect);
397    }
398  }
399
400  /**
401   * Close a multiselect dropdown.
402   *
403   * @param {HTMLElement} multiselect
404   */
405  close(multiselect) {
406    const dialog = this.getDialog(multiselect);
407    if (!dialog) {
408      throw new Error('Multiselect does not contain an HTMLDialogElement with the attribute data-multiselect-dropdown-dialog.');
409    }
410    dialog.close();
411    multiselect.removeAttribute('data-multiselect-dropdown-open');
412    const toggle = this.getToggle(multiselect);
413    if (toggle) {
414      toggle.setAttribute('aria-expanded', 'false');
415    }
416    if (toggle && document.activeElement === this.getList(multiselect)) {
417      toggle.focus({
418        preventScroll: true
419      });
420    }
421    if (this.isOpen(multiselect)) {
422      _classPrivateFieldGet(_opened, this).splice(_classPrivateFieldGet(_opened, this).indexOf(this.getId(multiselect)), 1);
423    }
424    this.doNotPersistOnLoad(multiselect);
425  }
426
427  /**
428   * Switch the multiselect dropdown open state.
429   *
430   * @param {HTMLElement} multiselect
431   * @param {Boolean} userTriggered
432   */
433  toggle(multiselect, userTriggered) {
434    if (this.isOpen(multiselect)) {
435      this.close(multiselect);
436    } else {
437      this.open(multiselect, userTriggered);
438    }
439  }
440
441  /**
442   * Check if a multiselect should open on load.
443   *
444   * @param {HTMLElement} multiselect
445   *
446   * @return {Boolean}
447   */
448  shouldOpenOnLoad(multiselect) {
449    return multiselect.hasAttribute("".concat(_classPrivateFieldGet(_attribute, this), "-persist-open"));
450  }
451
452  /**
453   * Check if a multiselect is marked as open on load.
454   *
455   * @param {HTMLElement} multiselect
456   *
457   * @return {Boolean}
458   */
459  isPersistedOnLoad(multiselect) {
460    return _classPrivateFieldGet(_persistedOnLoad, this).includes(this.getId(multiselect));
461  }
462
463  /**
464   * Open a multiselect on load.
465   *
466   * @param {HTMLElement} multiselect
467   */
468  persistOnLoad(multiselect) {
469    if (!this.isPersistedOnLoad(multiselect)) {
470      _classPrivateFieldGet(_persistedOnLoad, this).push(this.getId(multiselect));
471    }
472  }
473
474  /**
475   * Do not open a multiselect on load.
476   *
477   * @param {HTMLElement} multiselect
478   */
479  doNotPersistOnLoad(multiselect) {
480    if (this.isPersistedOnLoad(multiselect)) {
481      _classPrivateFieldGet(_persistedOnLoad, this).splice(_classPrivateFieldGet(_persistedOnLoad, this).indexOf(this.getId(multiselect)), 1);
482      this.setSearch(multiselect, '');
483    }
484  }
485
486  /**
487   * Update the multiselect dropdown toggle label.
488   *
489   * @param {HTMLElement} multiselect
490   */
491  updateLabel(multiselect) {
492    var _MultiselectDropdowns2, _MultiselectDropdowns3, _multiselect$getAttri;
493    let attribute;
494    const count = (_MultiselectDropdowns2 = (_MultiselectDropdowns3 = MultiselectDropdowns.getCheckboxesChecked(multiselect)) === null || _MultiselectDropdowns3 === void 0 ? void 0 : _MultiselectDropdowns3.length) !== null && _MultiselectDropdowns2 !== void 0 ? _MultiselectDropdowns2 : 0;
495    switch (count) {
496      case 0:
497        attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-none");
498        break;
499      case 1:
500        attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-single");
501        break;
502      case MultiselectDropdowns.getCheckboxes(multiselect).length:
503        attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-all");
504        break;
505      default:
506        attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-plural");
507        break;
508    }
509    this.getToggle(multiselect).innerText = ((_multiselect$getAttri = multiselect.getAttribute(attribute)) !== null && _multiselect$getAttri !== void 0 ? _multiselect$getAttri : '').replace(/%d/, count.toString());
510  }
511
512  /**
513   * Submit a multiselect dropdown.
514   *
515   * @param {HTMLElement} multiselect
516   */
517  submit(multiselect) {
518    const submitButton = multiselect.closest('form').querySelector('[type="submit"]');
519    if (submitButton) {
520      submitButton.click();
521    } else {
522      multiselect.closest('form').requestSubmit();
523    }
524    _classPrivateFieldSet(_opened, this, []);
525  }
526
527  /**
528   * Filter a multiselect's items.
529   *
530   * @param {HTMLElement} multiselect
531   */
532  filter(multiselect) {
533    const searchInput = this.getSearchInput(multiselect);
534    const input = searchInput.value.toLowerCase();
535    const threshold = searchInput.getAttribute("".concat(_classPrivateFieldGet(_attribute, this), "-search-character-threshold"));
536    this.setSearch(multiselect, searchInput.value);
537    this.getListItems(multiselect).forEach(item => {
538      if (input.length < threshold) {
539        item.style.display = 'initial';
540      } else {
541        const match = item.querySelector('label').innerText.toLowerCase().indexOf(input) === -1;
542        item.style.display = match ? 'none' : 'initial';
543      }
544    });
545  }
546}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.