1(function () { 2 'use strict'; 3 4 document.querySelectorAll('.sort-dropdown').forEach(function (el) { 5 var trigger = el.querySelector('.sort-dropdown__trigger'); 6 var list = el.querySelector('.sort-dropdown__list'); 7 var input = el.querySelector('input[data-filter-select]'); 8 9 if (!trigger || !list || !input) { 10 return; 11 } 12 13 trigger.addEventListener('click', function () { 14 var isOpen = !list.hidden; 15 list.hidden = isOpen; 16 trigger.setAttribute('aria-expanded', String(!isOpen)); 17 }); 18 19 el.addEventListener('focusout', function (e) { 20 if (!el.contains(e.relatedTarget)) { 21 list.hidden = true; 22 trigger.setAttribute('aria-expanded', 'false'); 23 } 24 }); 25 26 document.addEventListener('keydown', function (e) { 27 if (e.key === 'Escape' && !list.hidden) { 28 list.hidden = true; 29 trigger.setAttribute('aria-expanded', 'false'); 30 trigger.focus(); 31 } 32 }); 33 34 function getOptions() { 35 return Array.prototype.slice.call(list.querySelectorAll('.sort-dropdown__option')); 36 } 37 38 // Arrow Up/Down on the closed trigger opens the list and moves focus into it. 39 trigger.addEventListener('keydown', function (e) { 40 if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') { 41 return; 42 } 43 e.preventDefault(); 44 list.hidden = false; 45 trigger.setAttribute('aria-expanded', 'true'); 46 var options = getOptions(); 47 var selected = list.querySelector('[aria-selected="true"]'); 48 var target = selected || (e.key === 'ArrowUp' ? options[options.length - 1] : options[0]); 49 if (target) { 50 target.focus(); 51 } 52 }); 53 54 // Enter/Space selects the focused option; arrows, Home and End move between options. 55 list.addEventListener('keydown', function (e) { 56 var option = e.target.closest('.sort-dropdown__option'); 57 if (!option) { 58 return; 59 } 60 var options = getOptions(); 61 var index = options.indexOf(option); 62 var next = null; 63 64 if (e.key === 'Enter' || e.key === ' ') { 65 e.preventDefault(); 66 option.click(); 67 trigger.focus(); 68 return; 69 } 70 if (e.key === 'ArrowDown') { 71 next = options[Math.min(index + 1, options.length - 1)]; 72 } else if (e.key === 'ArrowUp') { 73 next = options[Math.max(index - 1, 0)]; 74 } else if (e.key === 'Home') { 75 next = options[0]; 76 } else if (e.key === 'End') { 77 next = options[options.length - 1]; 78 } 79 if (next) { 80 e.preventDefault(); 81 next.focus(); 82 } 83 }); 84 85 list.addEventListener('click', function (e) { 86 var option = e.target.closest('.sort-dropdown__option'); 87 if (!option) { 88 return; 89 } 90 91 list.hidden = true; 92 trigger.setAttribute('aria-expanded', 'false'); 93 94 list.querySelectorAll('.sort-dropdown__option').forEach(function (otherOption) { 95 otherOption.setAttribute('aria-selected', String(otherOption === option)); 96 }); 97 98 trigger.firstChild.textContent = option.textContent.trim() + ' '; 99 100 var value = option.dataset.value; 101 if (value && value !== input.value) { 102 input.value = value; 103 input.dispatchEvent(new Event('change', { bubbles: true })); 104 } 105 }); 106 }); 107})();
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.