PageSourceSearch

https://taumart.ru/bitrix/js/ui/a11y/dist/a11y.bundle.js?178426380660327

js taumart.ru collected 2026-10-01 23:58:38 UTC 60,327 bytes, 2,019 lines download raw bytes

1/* eslint-disable */
2this.BX = this.BX || {};
3this.BX.UI = this.BX.UI || {};
4(function (exports, main_core) {
5	'use strict';
6
7	const focusableElements = ['a[href]', 'input:not([disabled]):not([type=hidden])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'area[href]', 'summary', 'iframe', 'object', 'embed', 'audio[controls]', 'video[controls]', '[contenteditable]:not([contenteditable="false"])', '[tabindex]:not([disabled]):not([hidden])'];
8	const FOCUSABLE_SELECTOR = focusableElements.join(',');
9
10	const supportsCheckVisibility = !main_core.Type.isUndefined(window.Element) && 'checkVisibility' in window.Element.prototype;
11	class InteractivityChecker {
12		static isDisabled(element) {
13			return main_core.Type.isElementNode(element) && (element.hasAttribute('disabled') || element.getAttribute('aria-disabled') === 'true');
14		}
15		static isVisible(element) {
16			if (!main_core.Type.isElementNode(element) || !element.isConnected) {
17				return false;
18			}
19			if (supportsCheckVisibility) {
20				return element.checkVisibility({
21					visibilityProperty: true,
22					opacityProperty: true
23				});
24			}
25			const hasGeometry = element.offsetWidth > 0 || element.offsetHeight > 0 || element.getClientRects().length > 0;
26			return hasGeometry && getComputedStyle(element).visibility === 'visible';
27		}
28		static isTabbable(element) {
29			if (!this.isFocusable(element)) {
30				return false;
31			}
32			return !this.hasNegativeTabIndex(element);
33		}
34		static hasNegativeTabIndex(element) {
35			const tabindex = element?.getAttribute('tabindex');
36			if (tabindex === null) {
37				return false;
38			}
39			return Number.parseInt(tabindex, 10) < 0;
40		}
41		static isFocusable(element) {
42			if (!main_core.Type.isElementNode(element) || element.closest('[inert]') || !element.isConnected) {
43				return false;
44			}
45			return element.matches(FOCUSABLE_SELECTOR) && this.isVisible(element);
46		}
47	}
48
49	const STORAGE_KEY = 'bx:a11y:debug';
50	const ALL = '*';
51	const CATEGORY_LABELS = {
52		'focus-monitor': 'FocusMonitor',
53		'focus-trap': 'FocusTrap',
54		'focus-zone': 'FocusZone',
55		'input-modality': 'InputModality',
56		'live-announcer': 'LiveAnnouncer'
57	};
58	class AccessibilityLogger {
59		static #categories = new Set();
60		static #all = false;
61		static #initialized = false;
62		static #init() {
63			if (this.#initialized) {
64				return;
65			}
66			this.#initialized = true;
67			try {
68				const raw = localStorage.getItem(STORAGE_KEY);
69				if (raw === ALL) {
70					this.#all = true;
71				} else if (raw) {
72					const parsed = JSON.parse(raw);
73					if (Array.isArray(parsed)) {
74						for (const category of parsed) {
75							this.#categories.add(category);
76						}
77					}
78				}
79			} catch {
80			}
81		}
82		static enable(category) {
83			this.#init();
84			if (category) {
85				this.#categories.add(category);
86			} else {
87				this.#all = true;
88			}
89			this.#save();
90		}
91		static disable(category) {
92			this.#init();
93			if (category) {
94				this.#categories.delete(category);
95			} else {
96				this.#all = false;
97				this.#categories.clear();
98			}
99			this.#save();
100		}
101		static isEnabled(category) {
102			this.#init();
103			return this.#all || this.#categories.has(category);
104		}
105		static log(category, message, ...args) {
106			if (!this.isEnabled(category)) {
107				return;
108			}
109			const label = CATEGORY_LABELS[category] || category;
110			console.log(`%c[${label}]%c ${message}`, 'color: #0075ff; font-weight: bold;', 'color: inherit; font-weight: normal;', ...args);
111		}
112		static logNode(category, message, node) {
113			if (!this.isEnabled(category) || !main_core.Type.isElementNode(node)) {
114				return;
115			}
116			const tag = node.tagName.toLowerCase();
117			const classes = node.className ? `.${[...node.classList].join('.')}` : '';
118			const id = node.id ? `#${node.id}` : '';
119			const selector = `${tag}${classes}${id}`;
120			const role = node.getAttribute('role');
121			const labelledBy = node.getAttribute('aria-labelledby');
122			const labelledByText = labelledBy ? labelledBy.split(/\s+/).map(labelId => document.getElementById(labelId)?.textContent?.trim()).filter(Boolean).join(' ') : '';
123			const ariaLabel = (node.getAttribute('aria-label') || labelledByText || node.textContent.trim().slice(0, 30
123) || '—').replaceAll(/\s+/g, ' ');
124			const attrs = [role && `role="${role}"`, node.getAttribute('aria-expanded') !== null && `aria-expanded="${node.getAttribute('aria-expanded')}"`, node.getAttribute('href') && `href="${node.getAttribute('href')}"`, node.getAttribute('tabindex') && `tabindex="${node.getAttribute('tabindex')}"`].filter(Boolean).join(' ');
125			const label = CATEGORY_LABELS[category] || category;
126			const parts = [`%c[${label}]%c ${message}`, `%c${selector}%c`, `"%c${ariaLabel}%c"`, attrs && `%c${attrs}%c`].filter(Boolean).join(' ');
127			console.log(parts, 'color: #0075ff; font-weight: bold;',
128			'color: inherit; font-weight: normal;',
129			'color: #0075ff; font-weight: bold;',
130			'color: inherit; font-weight: normal;',
131			'color: #e36209; font-style: italic;',
132			'color: inherit; font-style: normal;',
133			...(attrs ? ['color: #9b59b6;',
134			'color: inherit;'
135			] : []));
136		}
137		static warn(category, message, ...args) {
138			if (!this.isEnabled(category)) {
139				return;
140			}
141			const label = CATEGORY_LABELS[category] || category;
142			console.warn(`[${label}] ${message}`, ...args);
143		}
144		static #save() {
145			try {
146				if (this.#all) {
147					localStorage.setItem(STORAGE_KEY, ALL);
148				} else if (this.#categories.size > 0) {
149					localStorage.setItem(STORAGE_KEY, JSON.stringify([...this.#categories]));
150				} else {
151					localStorage.removeItem(STORAGE_KEY);
152				}
153			} catch {
154			}
155		}
156	}
157
158	const RESTORE_FOCUS_EVENT = 'a11y:restore-focus';
159	class FocusNavigator {
160		static get FOCUSABLE_SELECTOR() {
161			return FOCUSABLE_SELECTOR;
162		}
163		static getFirst(container, options) {
164			return this.#traverse(container, 'first', options);
165		}
166		static getLast(container, options) {
167			return this.#traverse(container, 'last', options);
168		}
169		static getNext(container, options) {
170			return this.#traverse(container, 'next', options);
171		}
172		static getPrevious(container, options) {
173			return this.#traverse(container, 'previous', options);
174		}
175		static focusFirst(container, options) {
176			return this.focusTarget(this.#traverse(container, 'first', options), options);
177		}
178		static focusLast(container, options) {
179			return this.focusTarget(this.#traverse(container, 'last', options), options);
180		}
181		static focusNext(container, options) {
182			return this.focusTarget(this.#traverse(container, 'next', options), options);
183		}
184		static focusPrevious(container, options) {
185			return this.focusTarget(this.#traverse(container, 'previous', options), options);
186		}
187		static focusContainer(container, options) {
188			if (!container.hasAttribute('tabindex')) {
189				container.setAttribute('tabindex', '-1');
190			}
191			this.focusTarget(container, options);
192			return container;
193		}
194		static getActiveElement(node) {
195			const {
196				activeElement
197			} = this.#getDocument(node);
198			if (activeElement?.tagName === 'IFRAME' && activeElement.contentDocument?.body) {
199				const iframe = activeElement;
200				return this.getActiveElement(iframe.contentDocument?.body);
201			}
202			if (main_core.Type.isElementNode(activeElement)) {
203				return activeElement;
204			}
205			return null;
206		}
207		static createWalker(container, options) {
208			const {
209				tabbableOnly = false,
210				accept
211			} = options ?? {};
212			const ownerDocument = container.ownerDocument ?? document;
213			return ownerDocument.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
214				acceptNode(node) {
215					const el = node;
216					if (!InteractivityChecker.isFocusable(el)) {
217						return NodeFilter.FILTER_SKIP;
218					}
219					if (tabbableOnly && el.tabIndex < 0) {
220						return NodeFilter.FILTER_SKIP;
221					}
222					if (accept && !accept(el) && el.tagName !== 'IFRAME') {
223						return NodeFilter.FILTER_SKIP;
224					}
225					return NodeFilter.FILTER_ACCEPT;
226				}
227			});
228		}
229		static *#walk(container, options) {
230			const walker = this.createWalker(container, options);
231			for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
232				const el = node;
233				if (el.tagName === 'IFRAME') {
234					try {
235						const iframe = el;
236						const body = iframe.contentDocument?.body;
237						if (body) {
238							yield* this.#walk(body, options);
239							continue;
240						}
241					} catch {
242					}
243				}
244				yield el;
245			}
246		}
247		static #traverse(container, direction, options) {
248			const walkerOptions = {
249				tabbableOnly: options?.tabbableOnly ?? true,
250				accept: options?.accept
251			};
252			switch (direction) {
253				case 'first':
254					{
255						return this.#walk(container, walkerOptions).next().value ?? null;
256					}
257				case 'last':
258					{
259						let last = null;
260						for (const el of this.#walk(container, walkerOptions)) {
261							last = el;
262						}
263						return last;
264					}
265				case 'next':
266					{
267						const from = this.#resolveFrom(container, options);
268						let found = from === null;
269						for (const el of this.#walk(container, walkerOptions)) {
270							if (found) {
271								return el;
272							}
273							if (el === from) {
274								found = true;
275							}
276						}
277						if (options?.wrap) {
278							return this.#walk(container, walkerOptions).next().value ?? null;
279						}
280						return null;
281					}
282				case 'previous':
283					{
284						const from = this.#resolveFrom(container, options);
285						let prev = null;
286						for (const el of this.#walk(container, walkerOptions)) {
287							if (el === from) {
288								break;
289							}
290							prev = el;
291						}
292						if (prev) {
293							return prev;
294						}
295						if (options?.wrap) {
296							return this.#traverse(container, 'last', options);
297						}
298						return null;
299					}
300			}
301			return null;
302		}
303		static focusBySelector(container, selector, options) {
304			const {
305				accept: outerAccept,
306				...rest
307			} = options ?? {};
308			const accept = el => {
309				return el.matches(selector) && (outerAccept ? outerAccept(el) : true);
310			};
311			const from = this.#resolveFrom(container, options);
312			let found = from === null;
313			for (const el of this.#walk(container, {
314				...rest,
315				accept
316			})) {
317				if (found) {
318					return this.focusTarget(el, options);
319				}
320				if (el === from) {
321					found = true;
322				}
323			}
324			if (options?.wrap) {
325				const el = this.#walk(container, {
326					...rest,
327					accept
328				}).next().value;
329				if (el) {
330					return this.focusTarget(el, options);
331				}
332			}
333			return null;
334		}
335		static #resolveFrom(container, options) {
336			const from = options?.from ?? this.getActiveElement(container);
337			if (!from || container === from) {
338				return null;
339			}
340			if (container.contains(from)) {
341				return from;
342			}
343			let doc = from.ownerDocument;
344			while (doc !== null) {
345				const frame = doc.defaultView?.frameElement;
346				if (!frame) {
347					return null;
348				}
349				if (container.contains(frame)) {
350					return from;
351				}
352				doc = frame.ownerDocument;
353			}
354			return null;
355		}
356		static focusTarget(target, options) {
357			if (!target) {
358				return null;
359			}
360			const {
361				preventScroll,
362				focusVisible
363			} = options ?? {};
364			target.focus({
365				preventScroll,
366				focusVisible
367			});
368			return target;
369		}
370		static restoreFocus(target, options) {
371			if (!main_core.Type.isElementNode(target)) {
372				return null;
373			}
374			try {
375				const customEvent = new CustomEvent(RESTORE_FOCUS_EVENT, {
376					bubbles: true,
377					cancelable: true
378				});
379				const dispatchResult = target.dispatchEvent(customEvent);
380				if (dispatchResult) {
381					this.focusTarget(target, options);
382					AccessibilityLogger.log('focus-monitor', 'restored focus to', target);
383					return target;
384				}
385			} catch {
386				AccessibilityLogger.warn('focus-monitor', 'failed to restore focus to', target);
387			}
388			return null;
389		}
390		static #getDocument(node) {
391			if (!node) {
392				return document;
393			}
394			if (node.window === node) {
395				return node.document;
396			}
397			return node.ownerDocument ?? document;
398		}
399	}
400
401	class FocusHistory {
402		#limit;
403		#stack = [];
404		constructor(limit = 25) {
405			this.#limit = limit;
406		}
407		record(el) {
408			if (!InteractivityChecker.isFocusable(el)) {
409				return;
410			}
411			if (this.#stack[this.#stack.length - 1]?.deref() === el) {
412				return;
413			}
414			for (let i = this.#stack.length - 1; i >= 0; i--) {
415				const current = this.#stack[i].deref();
416				if (!current) {
417					this.#stack.splice(i, 1);
418					continue;
419				}
420				if (current === el) {
421					this.#stack.splice(i, 1);
422					break;
423				}
424			}
425			AccessibilityLogger.logNode('focus-monitor', 'recorded', el);
426			this.#stack.push(new WeakRef(el));
427			this.#trimByWindow(el.ownerDocument);
428		}
429		getLastValid() {
430			for (let i = this.#stack.length - 1; i >= 0; i--) {
431				const el = this.#stack[i].deref();
432				if (!el) {
433					this.#stack.splice(i, 1);
434					continue;
435				}
436				if (InteractivityChecker.isFocusable(el)) {
437					this.#stack.length = i + 1;
438					return el;
439				}
440				this.#stack.splice(i, 1);
441			}
442			return null;
443		}
444		#trimByWindow(doc) {
445			const docIndexes = [];
446			for (let i = this.#stack.length - 1; i >= 0; i--) {
447				const current = this.#stack[i].deref();
448				if (!current) {
449					this.#stack.splice(i, 1);
450					continue;
451				}
452				if (current.ownerDocument === doc) {
453					docIndexes.push(i);
454				}
455			}
456			const excess = docIndexes.length - this.#limit;
457			if (excess <= 0) {
458				return;
459			}
460			for (let j = docIndexes.length - excess; j < docIndexes.length; j++) {
461				this.#stack.splice(docIndexes[j], 1);
462			}
463		}
464	}
465
466	const NAV_KEYS = new Set(['Tab', 'Escape', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End']);
467	const POINTER_TYPES = new Set(['mouse', 'pen', 'touch']);
468	class InputModalityTracker {
469		#modality = 'unknown';
470		#pointerType = null;
471		#lastNavKey = null;
472		#lastNavShift = false;
473		#attachedDocs = [];
474		#detachHandlers = new WeakMap();
475		attach(doc) {
476			if (this.#detachHandlers.has(doc)) {
477				return;
478			}
479			const onKeyDown = event => {
480				if (NAV_KEYS.has(event.key)) {
481					this.#lastNavKey = event.key;
482					this.#lastNavShift = event.shiftKey;
483					this.#setModality('keyboard');
484				}
485			};
486			const onPointer = event => {
487				this.#pointerType = POINTER_TYPES.has(event.pointerType) ? event.pointerType : 'mouse';
488				this.#setModality('pointer');
489			};
490			main_core.Event.bind(doc, 'keydown', onKeyDown, true);
491			main_core.Event.bind(doc, 'pointerdown', onPointer, true);
492			const detach = () => {
493				main_core.Event.unbind(doc, 'keydown', onKeyDown, true);
494				main_core.Event.unbind(doc, 'pointerdown', onPointer, true);
495			};
496			this.#attachedDocs.push(new WeakRef(doc));
497			this.#detachHandlers.set(doc, detach);
498			doc.documentElement.dataset.inputModality = this.#modality;
499		}
500		detach(doc) {
501			const detach = this.#detachHandlers.get(doc);
502			if (detach) {
503				detach();
504				this.#detachHandlers.delete(doc);
505				this.#cleanupWeakRefs();
506			}
507		}
508		static enableDebug() {
509			AccessibilityLogger.enable('input-modality');
510		}
511		static disableDebug() {
512			AccessibilityLogger.disable('input-modality');
513		}
514		#setModality(modality) {
515			if (this.#modality !== modality) {
516				AccessibilityLogger.log('input-modality', `${this.#modality} -> ${modality}`);
517				this.#modality = modality;
518				this.#updateAllDocuments();
519			}
520		}
521		#updateAllDocuments() {
522			const aliveRefs = [];
523			for (const weakRef of this.#attachedDocs) {
524				const doc = weakRef.deref();
525				if (doc) {
526					doc.documentElement.dataset.inputModality = this.#modality;
527					aliveRefs.push(weakRef);
528				}
529			}
530			this.#attachedDocs = aliveRefs;
531		}
532		#cleanupWeakRefs() {
533			this.#attachedDocs = this.#attachedDocs.filter(weakRef => {
534				const doc = weakRef.deref();
535				return doc && this.#detachHandlers.has(doc);
536			});
537		}
538		getLastModality() {
539			return this.#modality;
540		}
541		getLastPointerType() {
542			return this.#modality === 'pointer' ? this.#pointerType : null;
543		}
544		getLastNavigationKey() {
545			return this.#modality === 'keyboard' ? this.#lastNavKey : null;
546		}
547		isLastNavigationReversed() {
548			return this.#modality === 'keyboard' && this.#lastNavKey === 'Tab' && this.#lastNavShift;
549		}
550	}
551
552	class AccessibilitySettings {
553		static useFocusTrapInDialogs() {
554			const settings = main_core.Extension.getSettings('ui.a11y');
555			return settings.get('useFocusTrapInDialogs') === true;
556		}
557		static restoreLostFocus() {
558			const settings = main_core.Extension.getSettings('ui.a11y');
559			return settings.get('restoreLostFocus') === true;
560		}
561		static suppressFocusOnRestore() {
562			const settings = main_core.Extension.getSettings('ui.a11y');
563			return settings.get('suppressFocusOnRestore') === true;
564		}
565	}
566
567	class FocusMonitor {
568		static #instance = null;
569		#history;
570		#modality;
571		#documents = new WeakMap();
572		constructor() {
573			this.#history = new FocusHistory(25);
574			this.#modality = new InputModalityTracker();
575			this.#attachDocument(document);
576		}
577		static get Instance() {
578			return this.initialize();
579		}
580		static initialize() {
581			const topWindow = main_core.Page.getRootWindow();
582			if (topWindow !== window) {
583				const monitor = main_core.Reflection.getClass('top.BX.UI.Accessibility.FocusMonitor');
584				if (monitor !== null) {
585					return monitor.Instance;
586				}
587			}
588			if (this.#instance === null) {
589				this.#instance = new FocusMonitor();
590			}
591			return this.#instance;
592		}
593		static enableDebug() {
594			AccessibilityLogger.enable('focus-monitor');
595		}
596		static disableDebug() {
597			AccessibilityLogger.disable('focus-monitor');
598		}
599		static shouldRestoreLostFocus() {
600			return AccessibilitySettings.restoreLostFocus();
601		}
602		#attachDocument(doc) {
603			if (this.#documents.has(doc)) {
604				return;
605			}
606			let pendingRestore = null;
607			const onFocusIn = event => {
608				if (pendingRestore !== null) {
609					clearTimeout(pendingRestore);
610					pendingRestore = null;
611				}
612				const target = event.target;
613				if (main_core.Type.isElementNode(target)) {
614					this.#history.record(target);
615					if (target.tagName === 'IFRAME') {
616						this.attachIframe(target);
617					}
618				}
619			};
620			const onFocusOut = () => {
621				if (pendingRestore !== null) {
622					return;
623				}
624				pendingRestore = window.setTimeout(() => {
625					pendingRestore = null;
626					this.#tryRestoreLostFocus(doc);
627				}, 0);
628			};
629			doc.addEventListener('focusin', onFocusIn, true);
630			doc.addEventListener('focusout', onFocusOut, true);
631			const observer = this.#attachObserver(doc);
632			this.#modality.attach(doc);
633			this.#documents.set(doc, {
634				detach: () => {
635					if (pendingRestore !== null) {
636						clearTimeout(pendingRestore);
637					}
638					doc.removeEventListener('focusin', onFocusIn, true);
639					doc.removeEventListener('focusout', onFocusOut, true);
640					this.#modality.detach(doc);
641					observer?.disconnect();
642				}
643			});
644		}
645		attachIframe(iframe) {
646			if (!main_core.Type.isElementNode(iframe) || iframe.tagName !== 'IFRAME') {
647				return;
648			}
649			try {
650				const doc = iframe.contentDocument;
651				if (!doc) {
652					return;
653				}
654				const state = this.#documents.get(doc);
655				if (state) {
656					return;
657				}
658				this.#attachDocument(doc);
659			} catch {
660			}
661		}
662		detachIframe(iframe) {
663			try {
664				const doc = iframe.contentDocument;
665				if (!doc) {
666					return;
667				}
668				const state = this.#documents.get(doc);
669				if (state) {
670					state.detach();
671					this.#documents.delete(doc);
672				}
673			} catch {
674			}
675		}
676		#tryRestoreLostFocus(doc) {
677			if (!FocusMonitor.shouldRestoreLostFocus()) {
678				return;
679			}
680			const active = doc.activeElement;
681			if (active && active !== doc.body) {
682				return;
683			}
684			if (this.#modality.getLastModality() !== 'keyboard') {
685				return;
686			}
687			const target = this.#history.getLastValid();
688			if (target) {
689				this.#restoreElementFocus(target);
690				return;
691			}
692			this.#restoreFocusToRoot(doc);
693		}
694		restoreFocus() {
695			const target = this.#history.getLastValid();
696			if (target) {
697				this.#restoreElementFocus(target);
698				return;
699			}
700			this.#restoreFocusToRoot(document);
701		}
702		#restoreFocusToRoot(doc) {
703			AccessibilityLogger.log('focus-monitor', 'restoring focus to root');
704			FocusNavigator.restoreFocus(this.getRoot(doc), {
705				preventScroll: true
706			});
707		}
708		getRoot(doc = document) {
709			return doc.querySelector('[data-focus-root]') || doc.querySelector('main') || doc.body;
710		}
711		#restoreElementFocus(el) {
712			if (!el.isConnected) {
713				return;
714			}
715			const ownerWindow = el.ownerDocument.defaultView;
716			if (!ownerWindow) {
717				return;
718			}
719			const activeElement = FocusNavigator.getActiveElement(el);
720			if (activeElement === el) {
721				return;
722			}
723			if (ownerWindow === window) {
724				FocusNavigator.restoreFocus(el, {
725					preventScroll: true
726				});
727				return;
728			}
729			ownerWindow.requestAnimationFrame(() => {
730				FocusNavigator.restoreFocus(el, {
731					preventScroll: true
732				});
733			});
734		}
735		getLastInputModality() {
736			return this.#modality.getLastModality();
737		}
738		getModalityTracker() {
739			return this.#modality;
740		}
741		#attachObserver(doc) {
742			if (!FocusMonitor.shouldRestoreLostFocus()) {
743				return null;
744			}
745			if (main_core.Browser.isFirefox() || main_core.Browser.isSafari()) {
746				const observer = new MutationObserver(mutations => {
747					this.#tryRestoreLostFocus(doc);
748				});
749				const observe = () => observer.observe(doc.body, {
750					childList: true,
751					subtree: true,
752					characterData: false
753				});
754				if (doc.readyState === 'loading') {
755					doc.addEventListener('DOMContentLoaded', observe);
756				} else {
757					observe();
758				}
759				return observer;
760			}
761			return null;
762		}
763	}
764
765	const PRECEDING = Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS;
766	const FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY;
767	class FocusTrap {
768		#id = `focus-trap-${main_core.Text.getRandom()}`;
769		#container;
770		#options;
771		#active = false;
772		#looped = true;
773		#initialized = false;
774		#restoreFocus = null;
775		#preventScroll = true;
776		#startFocusBoundary = null;
777		#endFocusBoundary = null;
778		#lastFocusedElement = null;
779		#alreadyInert = new WeakSet();
780		#startFocusHandler = this.#handleStartBoundaryFocus.bind(this);
781		#endFocusHandler = this.#handleEndBoundaryFocus.bind(this);
782		constructor(container, options = {}) {
783			this.#container = container;
784			this.#options = main_core.Type.isPlainObject(options) ? options : {};
785			this.setRestoreFocus(this.#options.restoreFocus ?? null);
786			if (this.#options.looped !== undefined) {
787				this.setLooped(this.#options.looped);
788			}
789			if (this.#options.preventScroll !== undefined) {
790				this.setPreventScroll(this.#options.preventScroll);
791			}
792		}
793		static enableDebug() {
794			AccessibilityLogger.enable('focus-trap');
795		}
796		static disableDebug() {
797			AccessibilityLogger.disable('focus-trap');
798		}
799		activate(options = {}) {
800			if (this.#active) {
801				return;
802			}
803			if (!this.#initialized) {
804				this.#init();
805			}
806			if (this.#lastFocusedElement === null) {
807				this.captureActiveElement();
808			}
809			this.#active = true;
810			if (this.#looped) {
811				this.#setBoundariesFocusable(true);
812			}
813			const {
814				initialFocus
815			} = {
816				initialFocus: true,
817				...options
818			};
819			if (initialFocus) {
820				this.applyInitialFocus();
821			}
822			this.#setOutsideIsolation(true);
823		}
824		deactivate() {
825			if (!this.#active) {
826				return;
827			}
828			this.#active = false;
829			this.#setBoundariesFocusable(false);
830			this.#setOutsideIsolation(false);
831			this.restoreFocus();
832		}
833		destroy() {
834			this.deactivate();
835			if (!this.#initialized) {
836				return;
837			}
838			main_core.Event.unbind(this.#startFocusBoundary, 'focus', this.#startFocusHandler);
839			main_core.Event.unbind(this.#endFocusBoundary, 'focus', this.#endFocusHandler);
840			main_core.Dom.remove(this.#startFocusBoundary);
841			main_core.Dom.remove(this.#endFocusBoundary);
842			this.#initialized = false;
843		}
844		setLooped(flag) {
845			if (!main_core.Type.isBoolean(flag)) {
846				return;
847			}
848			this.#looped = flag;
849			if (this.#active) {
850				this.#setBoundariesFocusable(flag);
851			}
852		}
853		setPreventScroll(flag) {
854			this.#preventScroll = main_core.Type.isBoolean(flag) ? flag : true;
855		}
856		isLooped() {
857			return this.#looped;
858		}
859		isActive() {
860			return this.#active;
861		}
862		setLastFocusedElement(el) {
863			if (main_core.Type.isElementNode(el) && el.tagName !== 'BODY' && !this.contains(el)) {
864				this.#lastFocusedElement = el;
865				AccessibilityLogger.logNode('focus-trap', 'set last focus', el);
866			}
867		}
868		captureActiveElement() {
869			const activeElement = FocusNavigator.getActiveElement();
870			if (activeElement !== null) {
871				this.setLastFocusedElement(activeElement);
872			}
873			return activeElement;
874		}
875		contains(el) {
876			return this.#container.contains(el);
877		}
878		focusFirst(options) {
879			return FocusNavigator.focusFirst(this.#container, this.#prepareFocusOptions(options));
880		}
881		focusLast(options) {
882			return FocusNavigator.focusLast(this.#container, this.#prepareFocusOptions(options));
883		}
884		focusNext(options) {
885			return FocusNavigator.focusNext(this.#container, this.#prepareFocusOptions(options));
886		}
887		focusPrevious(options) {
888			return FocusNavigator.focusPrevious(this.#container, this.#prepareFocusOptions(options));
889		}
890		focusContainer(options) {
891			return FocusNavigator.focusContainer(this.#container, this.#prepareFocusOptions(options));
892		}
893		focusBySelector(selector, options) {
894			return FocusNavigator.focusBySelector(this.#container, selector, this.#prepareFocusOptions(options));
895		}
896		#prepareFocusOptions(options) {
897			const preparedOptions = main_core.Type.isPlainObject(options) ? {
898				...options
899			} : {};
900			if (main_core.Type.isNil(preparedOptions.preventScroll)) {
901				preparedOptions.preventScroll = this.#preventScroll;
902			}
903			return preparedOptions;
904		}
905		getId() {
906			return this.#id;
907		}
908		#init() {
909			this.#startFocusBoundary = this.#createFocusBoundary();
910			this.#endFocusBoundary = this.#createFocusBoundary();
911			main_core.Event.bind(this.#startFocusBoundary, 'focus', this.#startFocusHandler);
912			main_core.Event.bind(this.#endFocusBoundary, 'focus', this.#endFocusHandler);
913			this.#container.insertAdjacentElement('beforebegin', this.#startFocusBoundary);
914			this.#container.insertAdjacentElement('afterend', this.#endFocusBoundary);
915			this.#initialized = true;
916		}
917		#createFocusBoundary() {
918			const el = document.createElement('div');
919			el.setAttribute('aria-hidden', 'true');
920			el.setAttribute('data-focus-trap', this.getId());
921			main_core.Dom.style(el, {
922				position: 'fixed',
923				width: 0,
924				height: 0,
925				opacity: '0',
926				pointerEvents: 'none',
927				outline: 'none'
928			});
929			return el;
930		}
931		#handleStartBoundaryFocus(event) {
932			if (!this.#active || !this.#startFocusBoundary) {
933				return;
934			}
935			const position = event.relatedTarget ? this.#startFocusBoundary.compareDocumentPosition(event.relatedTarget) : 0;
936			const isBackward = position & FOLLOWING;
937			if (!isBackward) {
938				if (!this.focusFirst()) {
939					this.focusContainer();
940				}
941				return;
942			}
943			const target = this.#options.startBoundary ? this.#resolveBoundary(this.#options.startBoundary) : null;
944			if (target) {
945				FocusNavigator.focusTarget(target, {
946					preventScroll: this.#preventScroll
947				});
948				return;
949			}
950			if (!this.focusLast()) {
951				this.focusContainer();
952			}
953		}
954		#handleEndBoundaryFocus(event) {
955			if (!this.#active || !this.#endFocusBoundary) {
956				return;
957			}
958			const position = event.relatedTarget ? this.#endFocusBoundary.compareDocumentPosition(event.relatedTarget) : 0;
959			const isBackward = position & PRECEDING;
960			if (!isBackward) {
961				if (!this.focusLast()) {
962					this.focusContainer();
963				}
964				return;
965			}
966			const target = this.#options.endBoundary ? this.#resolveBoundary(this.#options.endBoundary) : null;
967			if (target) {
968				FocusNavigator.focusTarget(target, {
969					preventScroll: this.#preventScroll
970				});
971				return;
972			}
973			if (!this.focusFirst()) {
974				this.focusContainer();
975			}
976		}
977		#resolveBoundary(boundary) {
978			if (main_core.Type.isStringFilled(boundary)) {
979				const el = document.querySelector(boundary);
980				return el !== null && main_core.Type.isElementNode(el) && InteractivityChecker.isFocusable(el) ? el : null;
981			}
982			if (main_core.Type.isElementNode(boundary) && InteractivityChecker.isFocusable(boundary)) {
983				return boundary;
984			}
985			if (main_core.Type.isFunction(boundary)) {
986				const el = boundary();
987				return main_core.Type.isElementNode(el) && InteractivityChecker.isFocusable(el) ? el : null;
988			}
989			return null;
990		}
991		#setBoundariesFocusable(enabled) {
992			const tabindex = enabled ? '0' : '-1';
993			this.#startFocusBoundary?.setAttribute('tabindex', tabindex);
994			this.#endFocusBoundary?.setAttribute('tabindex', tabindex);
995		}
996		applyInitialFocus() {
997			const initialFocus = this.#options.initialFocus ?? true;
998			if (initialFocus === false) {
999				return;
1000			}
1001			if (!this.#options.forceInitialFocus) {
1002				const activeElement = FocusNavigator.getActiveElement(this.#container);
1003				if (activeElement && this.#container.contains(activeElement) && activeElement !== this.#container) {
1004					return;
1005				}
1006			}
1007			const candidates = Array.isArray(initialFocus) ? initialFocus : [initialFocus];
1008			for (const candidate of candidates) {
1009				if (this.#tryInitialFocus(candidate)) {
1010					return;
1011				}
1012			}
1013			this.focusContainer();
1014		}
1015		#tryInitialFocus(initialFocus) {
1016			if (initialFocus === false) {
1017				return false;
1018			}
1019			if (initialFocus === 'container') {
1020				this.focusContainer();
1021				return true;
1022			}
1023			if (main_core.Type.isStringFilled(initialFocus)) {
1024				return this.focusBySelector(initialFocus) !== null;
1025			}
1026			if (main_core.Type.isFunction(initialFocus)) {
1027				const el = initialFocus();
1028				if (InteractivityChecker.isFocusable(el)) {
1029					FocusNavigator.focusTarget(el, {
1030						preventScroll: this.#preventScroll
1031					});
1032					return true;
1033				}
1034				return false;
1035			}
1036			if (initialFocus === true || initialFocus === 'first-tabbable' || main_core.Type.isNil(initialFocus)) {
1037				return Boolean(this.focusFirst());
1038			}
1039			return false;
1040		}
1041		setRestoreFocus(restore) {
1042			if (main_core.Type.isBoolean(restore) || main_core.Type.isNull(restore) || main_core.Type.isStringFilled(restore) || main_core.Type.isFunction(restore) || main_core.Type.isElementNode(restore)) {
1043				this.#restoreFocus = restore;
1044			}
1045		}
1046		restoreFocus() {
1047			try {
1048				const restoreFocus = this.#restoreFocus ?? true;
1049				if (restoreFocus === false) {
1050					return;
1051				}
1052				if (main_core.Type.isStringFilled(restoreFocus)) {
1053					const doc = this.#container.ownerDocument ?? document;
1054					let el = null;
1055					try {
1056						el = doc.querySelector(restoreFocus);
1057					} catch {
1058						console.error('FocusTrap: invalid restoreFocus selector provided');
1059					}
1060					if (el !== null && InteractivityChecker.isFocusable(el)) {
1061						FocusNavigator.restoreFocus(el, {
1062							preventScroll: true
1063						});
1064						return;
1065					}
1066				}
1067				if (main_core.Type.isElementNode(restoreFocus) && InteractivityChecker.isFocusable(restoreFocus)) {
1068					FocusNavigator.restoreFocus(restoreFocus, {
1069						preventScroll: true
1070					});
1071					return;
1072				}
1073				if (main_core.Type.isFunction(restoreFocus)) {
1074					const el = restoreFocus();
1075					if (InteractivityChecker.isFocusable(el)) {
1076						FocusNavigator.restoreFocus(el, {
1077							preventScroll: true
1078						});
1079						return;
1080					}
1081				}
1082				if (this.#lastFocusedElement !== null && InteractivityChecker.isFocusable(this.#lastFocusedElement)) {
1083					let eventListeners = [];
1084					const suppressFocusOnRestore = main_core.Type.isBoolean(this.#options.suppressFocusOnRestore) ? this.#options.suppressFocusOnRestore : AccessibilitySettings.suppressFocusOnRestore();
1085					try {
1086						if (suppressFocusOnRestore) {
1087							eventListeners = main_core.Event.getEventListeners(this.#lastFocusedElement, 'focus');
1088							if (eventListeners.length > 0) {
1089								main_core.Event.unbindAll(this.#lastFocusedElement, 'focus');
1090							}
1091						}
1092						AccessibilityLogger.logNode('focus-trap', 'back to last focus', this.#lastFocusedElement);
1093						FocusNavigator.restoreFocus(this.#lastFocusedElement, {
1094							preventScroll: true
1095						});
1096					} finally {
1097						if (suppressFocusOnRestore) {
1098							for (const eventListener of eventListeners) {
1099								main_core.Event.bind(this.#lastFocusedElement, 'focus', eventListener.listener);
1100							}
1101						}
1102					}
1103					return;
1104				}
1105				FocusMonitor.Instance.restoreFocus();
1106			} finally {
1107				this.#lastFocusedElement = null;
1108			}
1109		}
1110		#setOutsideIsolation(enable) {
1111			if (this.#options.isolateOutside !== true) {
1112				return;
1113			}
1114			const containers = [this.#container, ...this.#getOutsideExceptionElements()];
1115			const topLevelContainers = containers.filter(el => {
1116				return !containers.some(other => other !== el && other.contains(el));
1117			});
1118			const adjacentElements = this.#getAdjacentElements(topLevelContainers
1118);
1119			if (enable) {
1120				this.#alreadyInert = new WeakSet();
1121				for (const el of adjacentElements) {
1122					if (el === this.#container || this.#container.contains(el) || el.getAttribute('data-focus-trap') === this.getId()) {
1123						continue;
1124					}
1125					if (el.parentNode?.nodeName === 'BODY' && el.offsetWidth === 0 && el.offsetHeight === 0) {
1126						continue;
1127					}
1128					if (el.inert || el.hasAttribute('inert')) {
1129						this.#alreadyInert.add(el);
1130					}
1131					el.setAttribute('inert', 'true');
1132				}
1133			} else {
1134				for (const el of adjacentElements) {
1135					if (this.#alreadyInert.has(el)) {
1136						continue;
1137					}
1138					el.removeAttribute('inert');
1139				}
1140				this.#alreadyInert = new WeakSet();
1141			}
1142		}
1143		#getAdjacentElements(containers) {
1144			const adjacentElements = new Set();
1145			const containerAncestors = new Set();
1146			for (const container of containers) {
1147				let currentElement = container;
1148				while (currentElement && currentElement.tagName !== 'BODY') {
1149					containerAncestors.add(currentElement);
1150					const parent = currentElement.parentElement;
1151					const siblings = parent ? parent.children : [];
1152					for (const sibling of siblings) {
1153						if (sibling.tagName !== 'SCRIPT' && sibling.tagName !== 'STYLE' && !sibling.getAttribute('data-a11y-ignore-inert') && main_core.Type.isElementNode(sibling)) {
1154							adjacentElements.add(sibling);
1155						}
1156					}
1157					currentElement = parent;
1158				}
1159			}
1160			containerAncestors.forEach(el => {
1161				adjacentElements.delete(el);
1162			});
1163			return adjacentElements;
1164		}
1165		#getOutsideExceptionElements() {
1166			if (!main_core.Type.isArrayFilled(this.#options.outsideExceptionSelectors)) {
1167				return [];
1168			}
1169			try {
1170				const selectors = this.#options.outsideExceptionSelectors.join(',');
1171				return [...document.querySelectorAll(selectors)];
1172			} catch {
1173				console.error('FocusTrap: invalid outsideExceptionSelectors provided');
1174				return [];
1175			}
1176		}
1177	}
1178
1179	const FocusTrapDirective = {
1180		mounted(el, binding) {
1181			const {
1182				active,
1183				options
1184			} = normalizeValue(binding.value);
1185			el.__focusTrap = new FocusTrap(el, options);
1186			if (active) {
1187				el.__focusTrap.activate();
1188			}
1189		},
1190		updated(el, binding) {
1191			const {
1192				active,
1193				options
1194			} = normalizeValue(binding.value);
1195			const {
1196				active: prevActive,
1197				options: prevOptions
1198			} = normalizeValue(binding.oldValue);
1199			if (options !== prevOptions && options) {
1200				el.__focusTrap?.destroy();
1201				el.__focusTrap = new FocusTrap(el, options);
1202			}
1203			const trap = el.__focusTrap;
1204			if (!trap) {
1205				return;
1206			}
1207			if (active) {
1208				trap.activate();
1209			} else {
1210				trap.deactivate();
1211			}
1212		},
1213		unmounted(el) {
1214			el.__focusTrap?.destroy();
1215			delete el.__focusTrap;
1216		}
1217	};
1218	function normalizeValue(value) {
1219		if (main_core.Type.isBoolean(value)) {
1220			return {
1221				active: value
1222			};
1223		}
1224		if (main_core.Type.isPlainObject(value)) {
1225			return {
1226				active: value.active !== false,
1227				options: value.options
1228			};
1229		}
1230		return {
1231			active: false
1232		};
1233	}
1234
1235	class LiveAnnouncer {
1236		static #instance = null;
1237		#el;
1238		#queue = [];
1239		#isSpeaking = false;
1240		#frameId = null;
1241		#timerId = null;
1242		#ready = false;
1243		#readyFrameId = null;
1244		#baseDelay = 500;
1245		#charDelay = 30;
1246		#maxDelay = 4000;
1247		#maxMessageLength = 160;
1248		static enableDebug() {
1249			AccessibilityLogger.enable('live-announcer');
1250		}
1251		static disableDebug() {
1252			AccessibilityLogger.disable('live-announcer');
1253		}
1254		static announce(message, politeness = 'polite') {
1255			if (!LiveAnnouncer.#instance) {
1256				LiveAnnouncer.#instance = new LiveAnnouncer();
1257			}
1258			LiveAnnouncer.#instance.announce(message, politeness);
1259		}
1260		static destroy() {
1261			LiveAnnouncer.#instance?.destroy();
1262			LiveAnnouncer.#instance = null;
1263		}
1264		constructor(options) {
1265			const {
1266				politeness = 'polite',
1267				container = document.body || document.documentElement,
1268				baseDelay,
1269				charDelay,
1270				maxDelay,
1271				maxMessageLength
1272			} = options ?? {};
1273			if (main_core.Type.isNumber(baseDelay)) {
1274				this.#baseDelay = baseDelay;
1275			}
1276			if (main_core.Type.isNumber(charDelay)) {
1277				this.#charDelay = charDelay;
1278			}
1279			if (main_core.Type.isNumber(maxDelay)) {
1280				this.#maxDelay = maxDelay;
1281			}
1282			if (main_core.Type.isNumber(maxMessageLength)) {
1283				this.#maxMessageLength = maxMessageLength;
1284			}
1285			this.#el = this.#createLiveRegion(politeness);
1286			main_core.Dom.append(this.#el, container);
1287			this.#readyFrameId = requestAnimationFrame(() => {
1288				this.#readyFrameId = null;
1289				this.#ready = true;
1290				this.#process();
1291			});
1292		}
1293		announce(message, politeness = 'polite') {
1294			const normalized = this.#normalizeMessage(message);
1295			if (!normalized) {
1296				return;
1297			}
1298			AccessibilityLogger.log('live-announcer', `[${politeness}] ${normalized}`);
1299			const last = this.#queue[this.#queue.length - 1];
1300			if (last && last.message === normalized && last.politeness === politeness) {
1301				return;
1302			}
1303			if (politeness === 'assertive') {
1304				this.#clearPendingAnnouncement();
1305				this.#queue.unshift({
1306					message: normalized,
1307					politeness
1308				});
1309			} else {
1310				this.#queue.push({
1311					message: normalized,
1312					politeness
1313				});
1314			}
1315			this.#process();
1316		}
1317		destroy() {
1318			if (this.#readyFrameId !== null) {
1319				cancelAnimationFrame(this.#readyFrameId);
1320				this.#readyFrameId = null;
1321			}
1322			this.#queue.length = 0;
1323			this.#clearPendingAnnouncement();
1324			this.#el.remove();
1325		}
1326		#process() {
1327			if (!this.#ready || this.#isSpeaking || this.#queue.length === 0) {
1328				return;
1329			}
1330			const item = this.#queue.shift();
1331			if (!item) {
1332				return;
1333			}
1334			this.#isSpeaking = true;
1335			const {
1336				message,
1337				politeness
1338			} = item;
1339			const delay = this.#getDelay(message);
1340			this.#el.setAttribute('aria-live', politeness);
1341			this.#el.textContent = '';
1342			this.#frameId = requestAnimationFrame(() => {
1343				this.#frameId = null;
1344				this.#el.textContent = message;
1345				this.#timerId = setTimeout(() => {
1346					this.#timerId = null;
1347					this.#isSpeaking = false;
1348					if (this.#queue.length === 0) {
1349						this.#el.textContent = '';
1350					}
1351					this.#process();
1352				}, delay);
1353			});
1354		}
1355		#clearPendingAnnouncement() {
1356			this.#isSpeaking = false;
1357			if (this.#frameId !== null) {
1358				cancelAnimationFrame(this.#frameId);
1359				this.#frameId = null;
1360			}
1361			if (this.#timerId !== null) {
1362				clearTimeout(this.#timerId);
1363				this.#timerId = null;
1364			}
1365			this.#el.textContent = '';
1366		}
1367		#getDelay(message) {
1368			const delay = this.#baseDelay + message.length * this.#charDelay;
1369			return Math.min(delay, this.#maxDelay);
1370		}
1371		#normalizeMessage(message) {
1372			if (!main_core.Type.isStringFilled(message)) {
1373				return '';
1374			}
1375			const text = message.trim();
1376			if (!text) {
1377				return '';
1378			}
1379			if (text.length <= this.#maxMessageLength) {
1380				return text;
1381			}
1382			const truncated = text.slice(0, this.#maxMessageLength - 1);
1383			const trimmedToWord = truncated.replace(/\s+\S*$/, '');
1384			return `${trimmedToWord || truncated}\u2026`;
1385		}
1386		#createLiveRegion(politeness) {
1387			const el = document.createElement('div');
1388			el.setAttribute('aria-live', politeness);
1389			el.setAttribute('aria-atomic', 'true');
1390			el.setAttribute('data-a11y-ignore-inert', 'true');
1391			main_core.Dom.style(el, {
1392				position: 'absolute',
1393				width: '1px',
1394				height: '1px',
1395				margin: '-1px',
1396				padding: '0',
1397				overflow: 'hidden',
1398				clip: 'rect(0 0 0 0)',
1399				whiteSpace: 'nowrap',
1400				border: '0'
1401			});
1402			return el;
1403		}
1404	}
1405
1406	const ARROW_HORIZONTAL = 0b0000000001;
1407	const ARROW_VERTICAL = 0b0000000010;
1408	const JK_BITS = 0b0000000100;
1409	const HL_BITS = 0b0000001000;
1410	const HOME_AND_END = 0b0000010000;
1411	const WS_BITS = 0b0000100000;
1412	const AD_BITS = 0b0001000000;
1413	const TAB_BIT = 0b0010000000;
1414	const PAGE_UP_DOWN = 0b0100000000;
1415	const BACKSPACE_BIT = 0b1000000000;
1416	const FocusKeys = {
1417		ArrowHorizontal: ARROW_HORIZONTAL,
1418		ArrowVertical: ARROW_VERTICAL,
1419		JK: JK_BITS,
1420		HL: HL_BITS,
1421		HomeAndEnd: HOME_AND_END,
1422		WS: WS_BITS,
1423		AD: AD_BITS,
1424		Tab: TAB_BIT,
1425		PageUpDown: PAGE_UP_DOWN,
1426		Backspace: BACKSPACE_BIT,
1427		ArrowAll: ARROW_HORIZONTAL | ARROW_VERTICAL,
1428		HJKL: HL_BITS | JK_BITS,
1429		WASD: WS_BITS | AD_BITS,
1430		All: ARROW_HORIZONTAL | ARROW_VERTICAL | JK_BITS | HL_BITS | HOME_AND_END | WS_BITS | AD_BITS | TAB_BIT | PAGE_UP_DOWN | BACKSPACE_BIT
1431	};
1432
1433	const ActiveDescendant = {
1434		ELEMENT_ATTR: 'data-active-descendant',
1435		CONTAINER_ATTR: 'data-has-active-descendant',
1436		EXPLICIT: 'activated-by-keyboard',
1437		IMPLICIT: 'activated-by-other'
1438	};
1439	const KEY_TO_BIT = {
1440		ArrowLeft: FocusKeys.ArrowHorizontal,
1441		ArrowDown: FocusKeys.ArrowVertical,
1442		ArrowUp: FocusKeys.ArrowVertical,
1443		ArrowRight: FocusKeys.ArrowHorizontal,
1444		h: FocusKeys.HL,
1445		j: FocusKeys.JK,
1446		k: FocusKeys.JK,
1447		l: FocusKeys.HL,
1448		a: FocusKeys.AD,
1449		s: FocusKeys.WS,
1450		w: FocusKeys.WS,
1451		d: FocusKeys.AD,
1452		Tab: FocusKeys.Tab,
1453		Home: FocusKeys.HomeAndEnd,
1454		End: FocusKeys.HomeAndEnd,
1455		PageUp: FocusKeys.PageUpDown,
1456		PageDown: FocusKeys.PageUpDown,
1457		Backspace: FocusKeys.Backspace
1458	};
1459	const KEY_TO_DIRECTION = {
1460		ArrowLeft: 'previous',
1461		ArrowDown: 'next',
1462		ArrowUp: 'previous',
1463		ArrowRight: 'next',
1464		h: 'previous',
1465		j: 'next',
1466		k: 'previous',
1467		l: 'next',
1468		a: 'previous',
1469		s: 'next',
1470		w: 'previous',
1471		d: 'next',
1472		Tab: 'next',
1473		Home: 'start',
1474		End: 'end',
1475		PageUp: 'start',
1476		PageDown: 'end',
1477		Backspace: 'previous'
1478	};
1479	const NON_EDITABLE_INPUT_TYPES = new Set(['button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'range', 'reset', 'submit']);
1480	class FocusZone {
1481		#container;
1482		#options;
1483		#active = false;
1484		#bindKeys;
1485		#focusOutBehavior;
1486		#focusInStrategy;
1487		#preventScroll;
1488		#ignoreHoverEvents;
1489		#tabbableOnly;
1490		#elementSet = new Set();
1491		#savedTabIndex = new WeakMap();
1492		#currentFocusedElement = null;
1493		#activeDescendantControl = null;
1494		#observer = null;
1495		#syncFrameId = null;
1496		#bindings = [];
1497		#generatedContainerId = false;
1498		#generatedElementIds = new WeakSet();
1499		#keyDownHandler = this.#handleKeyDown.bind(this);
1500		#focusInHandler = this.#handleFocusIn.bind(this);
1501		#mouseMoveHandler = this.#handleMouseMove.bind(this);
1502		#controlFocusInHandler = this.#handleControlFocusIn.bind(this);
1503		#controlFocusOutHandler = this.#handleControlFocusOut.bind(this);
1504		constructor(container, options = {}) {
1505			this.#container = container;
1506			this.#options = main_core.Type.isPlainObject(options) ? options : {};
1507			this.#activeDescendantControl = this.#options.activeDescendantControl ?? null;
1508			this.#focusOutBehavior = this.#options.focusOutBehavior ?? 'stop';
1509			this.#focusInStrategy = this.#options.focusInStrategy ?? 'previous';
1510			this.#preventScroll = this.#options.preventScroll ?? false;
1511			this.#ignoreHoverEvents = this.#options.ignoreHoverEvents ?? false;
1512			this.#tabbableOnly = this.#options.tabbableOnly ?? false;
1513			this.#bindKeys = this.#options.bindKeys ?? (this.#options.getNextFocusable ? FocusKeys.ArrowAll : FocusKeys.ArrowVertical) | FocusKeys.HomeAndEnd;
1514		}
1515		static enableDebug() {
1516			AccessibilityLogger.enable('focus-zone');
1517		}
1518		static disableDebug() {
1519			AccessibilityLogger.disable('focus-zone');
1520		}
1521		activate() {
1522			if (this.#active) {
1523				return;
1524			}
1525			this.#active = true;
1526			this.#elementSet = this.#collectFocusableSet();
1527			for (const element of this.#elementSet) {
1528				this.#saveAndSetTabIndex(element);
1529			}
1530			if (this.#activeDescendantControl) {
1531				if (!this.#container.id) {
1532					this.#container.setAttribute('id', `fz-${main_core.Text.getRandom()}`);
1533					this.#generatedContainerId = true;
1534				}
1535				this.#activeDescendantControl.setAttribute('aria-controls', this.#container.id);
1536			}
1537			const preventInitialFocus = this.#focusInStrategy === 'initial' && this.#activeDescendantControl;
1538			if (!preventInitialFocus) {
1539				this.#updateFocusedElement(this.#resolveInitialFocusedElement());
1540			}
1541			this.#attachEventListeners();
1542			this.#attachObserver();
1543			AccessibilityLogger.log('focus-zone', `activated with ${this.#elementSet.size} elements`);
1544		}
1545		deactivate() {
1546			if (!this.#active) {
1547				return;
1548			}
1549			this.#active = false;
1550			if (this.#observer) {
1551				this.#observer.disconnect();
1552				this.#observer = null;
1553			}
1554			if (this.#syncFrameId !== null) {
1555				cancelAnimationFrame(this.#syncFrameId);
1556				this.#syncFrameId = null;
1557			}
1558			this.#detachEventListeners();
1559			for (const element of this.#elementSet) {
1560				this.#restoreTabIndex(element);
1561				if (this.#generatedElementIds.has(element)) {
1562					element.removeAttribute('id');
1563					this.#generatedElementIds.delete(element);
1564				}
1565			}
1566			if (this.#activeDescendantControl) {
1567				this.#clearActiveDescendant();
1568				this.#activeDescendantControl.removeAttribute('aria-controls');
1569			}
1570			if (this.#generatedContainerId) {
1571				this.#container.removeAttribute('id');
1572				this.#generatedContainerId = false;
1573			}
1574			this.#elementSet.clear();
1575			this.#currentFocusedElement = null;
1576			AccessibilityLogger.log('focus-zone', 'deactivated');
1577		}
1578		isActive() {
1579			return this.#active;
1580		}
1581		getCurrentFocusedElement() {
1582			return this.#currentFocusedElement;
1583		}
1584		refreshElements() {
1585			if (this.#active) {
1586				this.#syncFocusableElements();
1587			}
1588		}
1589		#collectFocusableSet() {
1590			const elements = new Set();
1591			const walker = FocusNavigator.createWalker(this.#container, {
1592				tabbableOnly: this.#tabbableOnly,
1593				accept: this.#options.focusableElementFilter
1594			});
1595			for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
1596				elements.add(node);
1597			}
1598			return elements;
1599		}
1600		#getNavigatorOptions(from = null, wrap = false) {
1601			return {
1602				from: from ?? undefined,
1603				tabbableOnly: false,
1604				accept: el => this.#elementSet.has(el),
1605				wrap
1606			};
1607		}
1608		#findManagedAncestor(node) {
1609			let el = main_core.Type.isElementNode(node) ? node : node.parentElement;
1610			while (el && el !== this.#container) {
1611				if (this.#elementSet.has(el)) {
1612					return el;
1613				}
1614				el = el.parentElement;
1615			}
1616			return null;
1617		}
1618		#saveAndSetTabIndex(element) {
1619			if (!this.#savedTabIndex.has(element)) {
1620				this.#savedTabIndex.set(element, element.getAttribute('tabindex'));
1621			}
1622			element.setAttribute('tabindex', '-1');
1623		}
1624		#restoreTabIndex(element) {
1625			const savedIndex = this.#savedTabIndex.get(element);
1626			if (savedIndex !== undefined) {
1627				if (savedIndex === null) {
1628					element.removeAttribute('tabindex');
1629				} else {
1630					element.setAttribute('tabindex', savedIndex);
1631				}
1632				this.#savedTabIndex.delete(element);
1633			}
1634		}
1635		#updateFocusedElement(to = null, directlyActivated = false) {
1636			const from = this.#currentFocusedElement;
1637			this.#currentFocusedElement = to;
1638			if (this.#activeDescendantControl) {
1639				if (to && FocusNavigator.getActiveElement() === this.#activeDescendantControl) {
1640					this.#setActiveDescendant(from, to, directlyActivated);
1641				} else {
1642					this.#clearActiveDescendant();
1643				}
1644				return;
1645			}
1646			if (from && from !== to && this.#savedTabIndex.has(from)) {
1647				from.setAttribute('tabindex', '-1');
1648			}
1649			if (to) {
1650				to.setAttribute('tabindex', '0');
1651			}
1652		}
1653		#setActiveDescendant(from, to, directlyActivated = false) {
1654			if (!to.id) {
1655				to.setAttribute('id', `fz-${main_core.Text.getRandom()}`);
1656				this.#generatedElementIds.add(to);
1657			}
1658			if (from && from !== to) {
1659				from.removeAttribute(ActiveDescendant.ELEMENT_ATTR);
1660			}
1661			if (!this.#activeDescendantControl) {
1662				return;
1663			}
1664			const previousId = this.#activeDescendantControl.getAttribute('aria-activedescendant');
1665			const nextActivationMode = directlyActivated ? ActiveDescendant.EXPLICIT : ActiveDescendant.IMPLICIT;
1666			const currentActivationMode = to.getAttribute(ActiveDescendant.ELEMENT_ATTR);
1667			if (previousId === to.id && currentActivationMode === nextActivationMode) {
1668				return;
1669			}
1670			this.#activeDescendantControl.setAttribute('aria-activedescendant', to.id);
1671			this.#container.setAttribute(ActiveDescendant.CONTAINER_ATTR, to.id);
1672			to.setAttribute(ActiveDescendant.ELEMENT_ATTR, nextActivationMode);
1673			AccessibilityLogger.logNode('focus-zone', 'active descendant ->', to);
1674			this.#options.onActiveDescendantChanged?.(to, from, directlyActivated);
1675		}
1676		#clearActiveDescendant(previousElement = this.#currentFocusedElement) {
1677			if (this.#focusInStrategy === 'first') {
1678				this.#currentFocusedElement = null;
1679			}
1680			this.#activeDescendantControl?.removeAttribute('aria-activedescendant');
1681			this.#container.removeAttribute(ActiveDescendant.CONTAINER_ATTR);
1682			if (previousElement) {
1683				previousElement.removeAttribute(ActiveDescendant.ELEMENT_ATTR);
1684			}
1685			const stale = this.#container.querySelectorAll(`[${ActiveDescendant.ELEMENT_ATTR}]`);
1686			for (const element of stale) {
1687				element.removeAttribute(ActiveDescendant.ELEMENT_ATTR);
1688			}
1689			this.#options.onActiveDescendantChanged?.(null, previousElement, false);
1690		}
1691		#handleKeyDown(event) {
1692			const keyBit = KEY_TO_BIT[event.key];
1693			if (keyBit === undefined || this.#elementSet.size === 0) {
1694				return;
1695			}
1696			if (event.defaultPrevented || (keyBit & this.#bindKeys) === 0 || FocusZone.#isNativeInputKey(event, FocusNavigator.getActiveElement())) {
1697				return;
1698			}
1699			const direction = FocusZone.#getDirection(event);
1700			let nextElement = null;
1701			if (this.#options.getNextFocusable) {
1702				nextElement = this.#options.getNextFocusable(direction, FocusNavigator.getActiveElement() ?? null, event);
1703			}
1704			if (!nextElement) {
1705				const wrap = this.#focusOutBehavior === 'wrap' && event.key !== 'Tab';
1706				const navOptions = this.#getNavigatorOptions(this.#currentFocusedElement, wrap);
1707				switch (direction) {
1708					case 'previous':
1709						{
1710							nextElement = FocusNavigator.getPrevious(this.#container, navOptions);
1711							break;
1712						}
1713					case 'next':
1714						{
1715							nextElement = FocusNavigator.getNext(this.#container, navOptions);
1716							break;
1717						}
1718					case 'start':
1719						{
1720							nextElement = FocusNavigator.getFirst(this.#container, navOptions);
1721							break;
1722						}
1723					default:
1724						{
1725							nextElement = FocusNavigator.getLast(this.#container, navOptions);
1726						}
1727				}
1728			}
1729			if (this.#activeDescendantControl) {
1730				this.#updateFocusedElement(nextElement || this.#currentFocusedElement, true);
1731			} else if (nextElement) {
1732				nextElement.focus({
1733					preventScroll: this.#preventScroll
1734				});
1735			}
1736			if (event.key !== 'Tab' || nextElement) {
1737				event.preventDefault();
1738			}
1739		}
1740		#handleFocusIn(event) {
1741			if (!main_core.Type.isElementNode(event.target)) {
1742				return;
1743			}
1744			if (this.#activeDescendantControl) {
1745				this.#handleFocusInActiveDescendant(event);
1746			} else {
1747				this.#handleFocusInRovingTabindex(event);
1748			}
1749		}
1750		#handleFocusInActiveDescendant(event) {
1751			const target = event.target;
1752			if (this.#elementSet.has(target)) {
1753				this.#activeDescendantControl?.focus({
1754					preventScroll: this.#preventScroll
1755				});
1756				this.#updateFocusedElement(target);
1757			}
1758		}
1759		#handleFocusInRovingTabindex(event) {
1760			const target = event.target;
1761			const tracker = FocusMonitor.Instance.getModalityTracker();
1762			const isPointerFocus = tracker.getLastModality() === 'pointer';
1763			if (isPointerFocus) {
1764				if (this.#elementSet.has(target)) {
1765					this.#updateFocusedElement(target);
1766				}
1767				return;
1768			}
1769			if (!this.#elementSet.has(target)) {
1770				return;
1771			}
1772			if (this.#focusInStrategy === 'previous') {
1773				this.#updateFocusedElement(target);
1774			} else if (this.#focusInStrategy === 'closest' || this.#focusInStrategy === 'first') {
1775				if (this.#isFocusEnteringFromOutside(event.relatedTarget)) {
1776					const targetElement = this.#getEntryFocusedElement();
1777					if (targetElement) {
1778						targetElement.focus({
1779							preventScroll: this.#preventScroll
1780						});
1781					}
1782					return;
1783				}
1784				this.#updateFocusedElement(target);
1785			} else if (main_core.Type.isFunction(this.#focusInStrategy)) {
1786				if (this.#isFocusEnteringFromOutside(event.relatedTarget)) {
1787					const elementToFocus = this.#focusInStrategy(event.relatedTarget);
1788					if (elementToFocus && this.#elementSet.has(elementToFocus)) {
1789						elementToFocus.focus({
1790							preventScroll: this.#preventScroll
1791						});
1792						return;
1793					}
1794					AccessibilityLogger.warn('focus-zone', 'focusInStrategy returned an element outside the managed focus set.');
1795				}
1796				this.#updateFocusedElement(target);
1797			}
1798		}
1799		#resolveInitialFocusedElement() {
1800			if (main_core.Type.isFunction(this.#focusInStrategy)) {
1801				const element = this.#focusInStrategy(this.#currentFocusedElement);
1802				return element && this.#elementSet.has(element) ? element : null;
1803			}
1804			return FocusNavigator.getFirst(this.#container, this.#getNavigatorOptions());
1805		}
1806		#getEntryFocusedElement() {
1807			const navOptions = this.#getNavigatorOptions();
1808			return FocusMonitor.Instance.getModalityTracker().isLastNavigationReversed() ? FocusNavigator.getLast(this.#container, navOptions) : FocusNavigator.getFirst(this.#container, navOptions);
1809		}
1810		#isFocusEnteringFromOutside(relatedTarget) {
1811			return main_core.Type.isElementNode(relatedTarget) && !this.#container.contains(relatedTarget);
1812		}
1813		#handleMouseMove(event) {
1814			const {
1815				target
1816			} = event;
1817			if (!main_core.Type.isElementNode(target)) {
1818				return;
1819			}
1820			const managedElement = this.#findManagedAncestor(target);
1821			if (managedElement) {
1822				this.#updateFocusedElement(managedElement);
1823			}
1824		}
1825		#handleControlFocusIn() {
1826			const preventInitialFocus = this.#focusInStrategy === 'initial' && this.#activeDescendantControl;
1827			if (this.#currentFocusedElement) {
1828				this.#setActiveDescendant(null, this.#currentFocusedElement);
1829			} else if (!preventInitialFocus) {
1830				this.#updateFocusedElement(FocusNavigator.getFirst(this.#container, this.#getNavigatorOptions()));
1831			}
1832		}
1833		#handleControlFocusOut() {
1834			this.#clearActiveDescendant();
1835		}
1836		#listen(target, event, handler, capture = false) {
1837			main_core.Event.bind(target, event, handler, capture);
1838			this.#bindings.push([target, event, handler, capture]);
1839		}
1840		#attachEventListeners() {
1841			const keyboardTarget = this.#activeDescendantControl ?? this.#container;
1842			this.#listen(keyboardTarget, 'keydown', this.#keyDownHandler);
1843			this.#listen(this.#container, 'focusin', this.#focusInHandler);
1844			if (this.#activeDescendantControl) {
1845				if (!this.#ignoreHoverEvents) {
1846					this.#listen(this.#container, 'mousemove', this.#mouseMoveHandler, true);
1847				}
1848				this.#listen(this.#activeDescendantControl, 'focusin', this.#controlFocusInHandler);
1849				this.#listen(this.#activeDescendantControl, 'focusout', this.#controlFocusOutHandler);
1850			}
1851		}
1852		#detachEventListeners() {
1853			for (const [target, event, handler, capture] of this.#bindings) {
1854				main_core.Event.unbind(target, event, handler, capture);
1855			}
1856			this.#bindings = [];
1857		}
1858		#attachObserver() {
1859			this.#observer = new MutationObserver(() => {
1860				this.#scheduleSyncFocusableElements();
1861			});
1862			this.#observer.observe(this.#container, {
1863				attributes: true,
1864				subtree: true,
1865				childList: true,
1866				attributeFilter: ['hidden', 'disabled', 'tabindex', 'inert', 'contenteditable', 'aria-disabled']
1867			});
1868		}
1869		#scheduleSyncFocusableElements() {
1870			if (!this.#active || this.#syncFrameId !== null) {
1871				return;
1872			}
1873			this.#syncFrameId = requestAnimationFrame(() => {
1874				this.#syncFrameId = null;
1875				this.#syncFocusableElements();
1876			});
1877		}
1878		#syncFocusableElements() {
1879			if (!this.#active) {
1880				return;
1881			}
1882			const newSet = this.#collectFocusableSet();
1883			const oldSet = this.#elementSet;
1884			for (const element of oldSet) {
1885				if (!newSet.has(element)) {
1886					this.#restoreTabIndex(element);
1887				}
1888			}
1889			for (const element of newSet) {
1890				if (!oldSet.has(element)) {
1891					this.#saveAndSetTabIndex(element);
1892				}
1893			}
1894			this.#elementSet = newSet;
1895			if (this.#currentFocusedElement && !newSet.has(this.#currentFocusedElement)) {
1896				this.#updateFocusedElement(FocusNavigator.getFirst(this.#container, this.#getNavigatorOptions()));
1897			} else if (!this.#currentFocusedElement && newSet.size > 0) {
1898				const preventInitialFocus = this.#focusInStrategy === 'initial' && this.#activeDescendantControl;
1899				if (!preventInitialFocus) {
1900					this.#updateFocusedElement(FocusNavigator.getFirst(this.#container, this.#getNavigatorOptions()));
1901				}
1902			}
1903		}
1904		static #getDirection(event) {
1905			const direction = KEY_TO_DIRECTION[event.key];
1906			if (event.key === 'Tab' && event.shiftKey) {
1907				return 'previous';
1908			}
1909			const isMac = main_core.Browser.isMac();
1910			if (isMac && event.metaKey || !isMac && event.ctrlKey) {
1911				if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
1912					return 'start';
1913				}
1914				if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
1915					return 'end';
1916				}
1917			}
1918			return direction;
1919		}
1920		static #isEditableElement(element) {
1921			if (!main_core.Type.isElementNode(element)) {
1922				return false;
1923			}
1924			const el = element;
1925			if (el.tagName === 'INPUT') {
1926				return !NON_EDITABLE_INPUT_TYPES.has(element.type);
1927			}
1928			if (el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {
1929				return true;
1930			}
1931			return el.isContentEditable;
1932		}
1933		static #isNativeInputKey(event, activeElement) {
1934			const {
1935				key,
1936				metaKey,
1937				altKey
1938			} = event;
1939			const codePoint = key.codePointAt(0) ?? 0;
1940			const isSingleChar = key.length === 1 || key.length === 2 && codePoint >= 0xD800 && codePoint <= 0xDBFF;
1941			const isEditable = FocusZone.#isEditableElement(activeElement);
1942			const isSelect = activeElement?.tagName === 'SELECT';
1943			if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
1944				return true;
1945			}
1946			if (isSelect) {
1947				const isMac = main_core.Browser.isMac();
1948				if (key === 'ArrowDown' && isMac && !metaKey) {
1949					return true;
1950				}
1951				return key === 'ArrowDown' && !isMac && altKey;
1952			}
1953			if (isEditable) {
1954				const isInputElement = activeElement?.tagName === 'TEXTAREA' || activeElement?.tagName === 'INPUT';
1955				const inputEl = activeElement;
1956				const cursorAtStart = isInputElement && inputEl.selectionStart === 0 && inputEl.selectionEnd === 0;
1957				const cursorAtEnd = isInputElement && inputEl.selectionStart === inputEl.value.length && inputEl.selectionEnd === inputEl.value.length;
1958				if (key === 'ArrowLeft' && !cursorAtStart) {
1959					return true;
1960				}
1961				if (key === 'ArrowRight' && !cursorAtEnd) {
1962					return true;
1963				}
1964				const isContentEditable = main_core.Type.isElementNode(activeElement) && activeElement.isContentEditable;
1965				if (activeElement?.tagName === 'TEXTAREA' || isContentEditable) {
1966					if (key === 'PageUp' || key === 'PageDown') {
1967						return true;
1968					}
1969					if (key === 'ArrowUp' && !cursorAtStart) {
1970						return true;
1971					}
1972					if (key === 'ArrowDown' && !cursorAtEnd) {
1973						return true;
1974					}
1975				}
1976			}
1977			return false;
1978		}
1979	}
1980
1981	class VisuallyHidden extends HTMLElement {
1982		connectedCallback() {
1983			Object.assign(this.style, {
1984				position: 'absolute',
1985				width: '1px',
1986				height: '1px',
1987				padding: '0',
1988				margin: '-1px',
1989				overflow: 'hidden',
1990				clip: 'rect(0, 0, 0, 0)',
1991				whiteSpace: 'nowrap',
1992				border: '0'
1993			});
1994		}
1995	}
1996	if (!customElements.get('visually-hidden'))
1997	{
1998		customElements.define('visually-hidden', VisuallyHidden);
1999	}
2000
2001	FocusMonitor.initialize();
2002
2003	exports.AccessibilityLogger = AccessibilityLogger;
2004	exports.AccessibilitySettings = AccessibilitySettings;
2005	exports.ActiveDescendant = ActiveDescendant;
2006	exports.FocusKeys = FocusKeys;
2007	exports.FocusMonitor = FocusMonitor;
2008	exports.FocusNavigator = FocusNavigator;
2009	exports.FocusTrap = FocusTrap;
2010	exports.FocusTrapDirective = FocusTrapDirective;
2011	exports.FocusZone = FocusZone;
2012	exports.InputModalityTracker = InputModalityTracker;
2013	exports.InteractivityChecker = InteractivityChecker;
2014	exports.LiveAnnouncer = LiveAnnouncer;
2015	exports.RESTORE_FOCUS_EVENT = RESTORE_FOCUS_EVENT;
2016	exports.VisuallyHidden = VisuallyHidden;
2017
2018})(this.BX.UI.Accessibility = this.BX.UI.Accessibility || {}, BX);
2019//# sourceMappingURL=a11y.bundle.js.map

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.