1(function ($, Drupal, once) { 2 'use strict'; 3 4 const fixChosenTabs = () => { 5 $("select[tabindex='-1']+.chosen-container").attr('tabindex', '0'); 6 }; 7 8 const watchChosenTabs = () => { 9 $('button.usa-accordion__button[aria-controls="edit-secondary-content"]').on('click', fixChosenTabs); 10 }; 11 12 Drupal.behaviors.chosenTabFix = { 13 attach: function (context) { 14 fixChosenTabs(); 15 watchChosenTabs(); 16 }, 17 }; 18 19 const CHOSEN_EVENTS = [ 20 'chosen:ready', 21 'chosen:showing_dropdown', 22 'chosen:hiding_dropdown', 23 'chosen:updated', 24 'chosen:search', 25 ]; 26 27 /** 28 * Ensures Chosen no-results items satisfy listbox child requirements. 29 */ 30 function ensureNoResultsOption(resultsEl) { 31 const noResults = resultsEl.querySelector('li.no-results'); 32 if (noResults && !noResults.getAttribute('role')) { 33 noResults.setAttribute('role', 'option'); 34 } 35 } 36 37 /** 38 * Determines whether a Chosen results list has valid listbox children. 39 */ 40 function hasListboxOptions(resultsEl) { 41 ensureNoResultsOption(resultsEl); 42 return resultsEl.querySelectorAll('li[role="option"]').length > 0; 43 } 44 45 /** 46 * Syncs accessibility attributes on a Chosen results listbox. 47 * 48 * Empty listboxes are removed from the accessibility tree to satisfy 49 * SiteImprove SIA-R68 (#1546). Options are restored when results exist. 50 */ 51 function syncChosenResultsAccessibility(resultsEl) { 52 if (!resultsEl || !resultsEl.classList.contains('chosen-results')) { 53 return; 54 } 55 56 if (hasListboxOptions(resultsEl)) { 57 resultsEl.removeAttribute('hidden'); 58 resultsEl.removeAttribute('aria-hidden'); 59 resultsEl.setAttribute('role', 'listbox'); 60 } 61 else { 62 resultsEl.setAttribute('hidden', ''); 63 resultsEl.setAttribute('aria-hidden', 'true'); 64 resultsEl.setAttribute('role', 'presentation'); 65 } 66 } 67 68 /** 69 * Binds Chosen lifecycle listeners for a select element. 70 */ 71 function bindChosenAccessibilityEvents(selectEl) { 72 CHOSEN_EVENTS.forEach((eventName) => { 73 selectEl.addEventListener(eventName, () => { 74 const container = selectEl.nextElementSibling; 75 if (!container || !container.classList.contains('chosen-container')) { 76 return; 77 } 78 syncChosenResultsAccessibility(container.querySelector('.chosen-results')); 79 }); 80 }); 81 } 82 83 Drupal.behaviors.chosenEmptyListboxFix = { 84 attach(context) { 85 once('chosen-empty-listbox-fix', 'select', context).forEach((selectEl) => { 86 bindChosenAccessibilityEvents(selectEl); 87 88 const container = selectEl.nextElementSibling; 89 if (container && container.classList.contains('chosen-container')) { 90 syncChosenResultsAccessibility(container.querySelector('.chosen-results')); 91 } 92 }); 93 94 once('chosen-empty-listbox-sync', '.chosen-results', context).forEach((resultsEl) => { 95 syncChosenResultsAccessibility(resultsEl); 96 }); 97 }, 98 }; 99 100 $(document).on('ajaxComplete', function () { 101 document.querySelectorAll('.chosen-results').forEach(syncChosenResultsAccessibility); 102 }); 103 104 // #1458: Remove aria-label from divs without role. 105 Drupal.behaviors.chosenAriaLabelFix = { 106 attach(context) { 107 once('chosen-aria-label-fix', '.chosen-container div[aria-label]', context).forEach((element) => { 108 if (!element.getAttribute('role')) { 109 element.removeAttribute('aria-label'); 110 } 111 }); 112 }, 113 }; 114 115})(jQuery, Drupal, once);
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.