PageSourceSearch

https://xoigac.top/js/custom-select.js?v=unrE77TQiy-qc-Fun5fVdZBrEj9yh0GaRL5AHtdIOO8

js xoigac.top collected 2026-10-10 02:22:01 UTC 7,621 bytes, 206 lines download raw bytes

1// Dark popover select — same floating menu language as the top-right language selector
2(function () {
3    'use strict';
4
5    var SELECTOR = [
6        'select.js-custom-select',
7        '#scheduleCategorySelect',
8        '#liveSportSelect',
9        '#preferredLanguage',
10        '#favoritesSort',
11        '#expHistoryCategory'
12    ].join(',');
13
14    var openRoot = null;
15
16    function selectedOption(select) {
17        if (!select || !select.options || !select.options.length) return null;
18        return select.options[select.selectedIndex] || select.options[0];
19    }
20
21    function close(root) {
22        if (!root) return;
23        root.classList.remove('is-open');
24        var trigger = root.querySelector('.custom-select-trigger');
25        if (trigger) trigger.setAttribute('aria-expanded', 'false');
26        if (openRoot === root) openRoot = null;
27    }
28
29    function closeAll(except) {
30        document.querySelectorAll('.custom-select.is-open').forEach(function (root) {
31            if (root !== except) close(root);
32        });
33    }
34
35    function open(root) {
36        if (!root) return;
37        closeAll(root);
38        root.classList.add('is-open');
39        var trigger = root.querySelector('.custom-select-trigger');
40        if (trigger) trigger.setAttribute('aria-expanded', 'true');
41        var active = root.querySelector('.custom-select-option.is-active');
42        if (active) active.focus();
43        openRoot = root;
44    }
45
46    function syncUI(select) {
47        var root = select.closest('.custom-select');
48        if (!root) return;
49        var current = selectedOption(select);
50        var label = root.querySelector('.custom-select-label');
51        if (label && current) label.textContent = (current.textContent || '').trim();
52        root.querySelectorAll('.custom-select-option').forEach(function (item) {
53            var on = item.getAttribute('data-value') === select.value;
54            item.classList.toggle('is-active', on);
55            item.setAttribute('aria-selected', on ? 'true' : 'false');
56        });
57    }
58
59    function setValue(select, value) {
60        if (select.value !== value) {
61            select.value = value;
62        }
63        syncUI(select);
64        select.dispatchEvent(new Event('input', { bubbles: true }));
65        select.dispatchEvent(new Event('change', { bubbles: true }));
66    }
67
68    function optionButtons(root) {
69        return Array.prototype.slice.call(root.querySelectorAll('.custom-select-option:not([disabled])'));
70    }
71
72    function enhance(select) {
73        if (!select || select.dataset.customSelectReady === '1' || !select.options.length) return;
74        select.dataset.customSelectReady = '1';
75
76        var root = document.createElement('div');
77        root.className = 'custom-select';
78        if (select.classList.contains('form-control') || select.classList.contains('profile-lang-select')) {
79            root.classList.add('custom-select--block');
80        }
81        if (select.classList.contains('schedule-category-select') || select.dataset.menuAlign === 'end') {
82            root.classList.add('custom-select--end');
83        }
84
85        select.parentNode.insertBefore(root, select);
86        root.appendChild(select);
87        select.classList.add('custom-select-native');
88        select.setAttribute('tabindex', '-1');
89        select.setAttribute('aria-hidden', 'true');
90
91        var trigger = document.createElement('button');
92        trigger.type = 'button';
93        trigger.className = 'custom-select-trigger';
94        select.className.split(/\s+/).forEach(function (cls) {
95            if (cls && cls !== 'js-custom-select' && cls !== 'custom-select-native') {
96                trigger.classList.add(cls);
97            }
98        });
99        trigger.setAttribute('aria-haspopup', 'listbox');
100        trigger.setAttribute('aria-expanded', 'false');
101        if (select.getAttribute('aria-label')) {
102            trigger.setAttribute('aria-label', select.getAttribute('aria-label'));
103        }
104
105        var label = document.createElement('span');
106        label.className = 'custom-select-label';
107        var current = selectedOption(select);
108        label.textContent = current ? (current.textContent || '').trim() : '';
109        trigger.appendChild(label);
110
111        var menu = document.createElement('div');
112        menu.className = 'custom-select-menu';
113        menu.setAttribute('role', 'listbox');
114
115        Array.prototype.forEach.call(select.options, function (opt) {
116            var item = document.createElement('button');
117            item.type = 'button';
118            item.className = 'custom-select-option';
119            item.setAttribute('role', 'option');
120            item.setAttribute('data-value', opt.value);
121            item.textContent = (opt.textContent || '').trim();
122            if (opt.disabled) {
123                item.disabled = true;
124                item.classList.add('is-disabled');
125            }
126            var isSelected = opt.value === select.value;
127            item.classList.toggle('is-active', isSelected);
128            item.setAttribute('aria-selected', isSelected ? 'true' : 'false');
129            item.addEventListener('click', function (e) {
130                e.preventDefault();
131                e.stopPropagation();
132                setValue(select, opt.value);
133                close(root);
134                trigger.focus();
135            });
136            menu.appendChild(item);
137        });
138
139        trigger.addEventListener('click', function (e) {
140            e.preventDefault();
141            e.stopPropagation();
142            if (root.classList.contains('is-open')) close(root);
143            else open(root);
144        });
145
146        trigger.addEventListener('keydown', function (e) {
147            if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
148                e.preventDefault();
149                if (!root.classList.contains('is-open')) open(root);
150            }
151        });
152
153        menu.addEventListener('keydown', function (e) {
154            var items = optionButtons(root);
155            var index = items.indexOf(document.activeElement);
156            if (e.key === 'ArrowDown') {
157                e.preventDefault();
158                items[(index + 1 + items.length) % items.length].focus();
159            } else if (e.key === 'ArrowUp') {
160                e.preventDefault();
161                items[(index - 1 + items.length) % items.length].focus();
162            } else if (e.key === 'Home') {
163                e.preventDefault();
164                items[0].focus();
165            } else if (e.key === 'End') {
166                e.preventDefault();
167                items[items.length - 1].focus();
168            }
169        });
170
171        select.addEventListener('focus', function () {
172            trigger.focus();
173        });
174
175        select.addEventListener('change', function () {
176            syncUI(select);
177        });
178
179        root.appendChild(trigger);
180        root.appendChild(menu);
181    }
182
183    function init(scope) {
184        (scope || document).querySelectorAll(SELECTOR).forEach(enhance);
185    }
186
187    document.addEventListener('click', function (e) {
188        if (openRoot && !openRoot.contains(e.target)) close(openRoot);
189    });
190
191    document.addEventListener('keydown', function (e) {
192        if (e.key === 'Escape' && openRoot) {
193            var trigger = openRoot.querySelector('.custom-select-trigger');
194            close(openRoot);
195            if (trigger) trigger.focus();
196        }
197    });
198
199    window.CustomSelect = { init: init, enhance: enhance };
200
201    if (document.readyState === 'loading') {
202        document.addEventListener('DOMContentLoaded', function () { init(); });
203    } else {
204        init();
205    }
206})();

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.