PageSourceSearch

https://www.tuzoltokeszulek.com/!common_packages/jquery/own/shop_c…_accessibility_focus.js?mod_time=1759314983

js tuzoltokeszulek.com collected 2026-10-08 09:47:05 UTC 6,708 bytes, 217 lines download raw bytes

1$(document).on('popupOpen popupClosed', function(e, array) {
2    let temp_popup_id;
3
4    if (e.type === 'popupOpen') {
5        temp_popup_id = (array.popupId === 'tooltip')
6            ? '#page_PopupContainer_tooltip'
7            : '.shop_popup_' + array.popupId;
8
9        manageFocus(temp_popup_id, {
10            trigger:        array.trigger,
11            ajaxContext:    array.ajax
12        });
13    } else if (e.type === 'popupClosed') {
14        temp_popup_id = (array.id === 'tooltip')
15            ? '#page_PopupContainer_tooltip'
16            : '.shop_popup_' + array.id;
17
18        restoreFocus(temp_popup_id);
19    }
20});
21
22$(document).on('onLoad onClose', function(e) {
23    const { target, type } = e;
24
25    if (!isValidTarget(target)) return;
26
27    const overlayId = `#${target.id}`;
28
29    if (type === 'onLoad') {
30        handleOverlayLoad(overlayId);
31    } else if (type === 'onClose') {
32        restoreFocus(overlayId);
33    }
34
35    function isValidTarget(target) {
36        return target.id && target.getAttribute('data-inited') !== '1';
37    }
38
39    function handleOverlayLoad(overlayId) {
40        if (overlayId === '#overlay_login_outer') {
41            const trigger = getLoginTrigger();
42            manageFocus(overlayId, { trigger });
43        } else {
44            manageFocus(overlayId);
45        }
46    }
47
48    function getLoginTrigger() {
49        return document.querySelector('.js-btn-next') ||
50            document.querySelector('.orderflow-main-btn');
51    }
52});
53
54function manageFocus(id, options = {}) {
55    const { trigger, ajaxContext = null, focusTo = null } = options;
56    const $overlay = $(id);
57    const $title = $overlay.find('.overlay_title');
58    const $text = $overlay.find('.overlay_text');
59
60    $overlay.data('trigger', trigger);
61
62    setupAccessibility();
63    setupFocusManagement();
64    setupFocusTrap();
65
66    function setupAccessibility() {
67        const titleId = `${id.substring(1)}_title`;
68        const descId = `${id.substring(1)}_desc`;
69
70        if ($title.length > 0) {
71            const finalId = $title.attr('id') || titleId;
72            $title.attr('id', finalId);
73            $overlay.attr('aria-labelledby', finalId);
74        }
75
76        if ($text.length > 0) {
77            $overlay.attr('aria-describedby', descId);
78            $text.attr('id', descId);
79        }
80
81        $overlay.attr({
82            role: 'dialog',
83            tabindex: '-1'
84        });
85
86        if (typeof announceToScreenReader === 'function' && !focusTo) {
87            announceToScreenReader('modalOpen');
88        }
89    }
90
91    function setupFocusManagement() {
92        let $focusableElements, $firstFocusable, $lastFocusable;
93
94        const updateFocusableElements = () => {
95            const selector = [
96                'input:not([disabled])',
97                'a[href]:not([href="#"]):not(.close)',
98                'button',
99                '[tabindex]:not([tabindex="-1"])',
100                'a.close:not(:has(+ button.btn-close))'
101            ].join(', ');
102
103            $focusableElements = $overlay.find(selector).filter(':visible');
104            $firstFocusable = $focusableElements.first();
105            $lastFocusable = $focusableElements.last();
106        };
107
108        const setInitialFocus = () => {
109            updateFocusableElements();
110
111            if (focusTo) {
112                const $focusEl = $(focusTo);
113                if ($focusEl.length) {
114                    $focusEl.focus();
115                    return;
116                }
117            }
118
119            setTimeout(() => $overlay.focus(), 50);
120        };
121
122        if (ajaxContext) {
123            const observer = new MutationObserver((mutations, obs) => {
124                const focusableCount = $overlay.find('input, button, a[href]:not([href="#"])').length;
125                const hasContent = $overlay.find('.overlay-content-placeholder').children().length > 0;
126
127                if (focusableCount > 1 || hasContent) {
128                    setInitialFocus();
129                    obs.disconnect();
130                }
131            });
132
133            observer.observe($overlay[0], { childList: true, subtree: true });
134        } else {
135            setInitialFocus();
136        }
137
138        // Expose updateFocusableElements for the focus trap
139        $overlay.data('updateFocusableElements', updateFocusableElements);
140        $overlay.data('getFocusableElements', () =>
140 ({ $firstFocusable, $lastFocusable }));
141    }
142
143    function setupFocusTrap() {
144        $overlay.on('keydown', function(event) {
145            if (event.key !== 'Tab') return;
146
147            const updateFocusableElements = $overlay.data('updateFocusableElements');
148            const getFocusableElements = $overlay.data('getFocusableElements');
149
150            updateFocusableElements();
151            const { $firstFocusable, $lastFocusable } = getFocusableElements();
152
153            const isFirstOrOverlay = document.activeElement === $firstFocusable?.[0] ||
154                document.activeElement === $overlay[0];
155            const isLast = document.activeElement === $lastFocusable?.[0];
156
157            if (event.shiftKey && isFirstOrOverlay) {
158                event.preventDefault();
159                $lastFocusable.focus();
160            } else if (!event.shiftKey && isLast) {
161                event.preventDefault();
162                $firstFocusable.focus();
163            }
164        });
165    }
166}
167
168function restoreFocus(id) {
169    const $overlay = $(id);
170    const trigger = $overlay.data('trigger');
171
172    announceModalClose();
173    handleFocusRestoration();
174
175    function announceModalClose() {
176        const isCookieAlert = id.includes('overlay_cookie_alert');
177        if (typeof announceToScreenReader === 'function' && !isCookieAlert) {
178            announceToScreenReader('modalClose');
179        }
180    }
181
182    function setTriggerFocus() {
183        if (trigger === document.body) {
184            $(document.activeElement).blur();
185            $('body').attr('tabindex', '-1').focus().removeAttr('tabindex');
186        } else {
187            const element = trigger?.[0] ?? trigger;
188            element?.focus?.();
189            $overlay.removeData('trigger');
190        }
191    }
192
193    function handleErrorOverlayFocus() {
194        const isOrderCouponError = $overlay[0].classList.contains('overlay_order_coupon_error_no');
195
196        if (isOrderCouponError) {
197            $('#coupon_id').focus();
198            return true;
199        }
200
201        if (add_focus_to_first_invalid('.js-validation')) {
202            return true;
203        }
204
205        return false;
206    }
207
208    function handleFocusRestoration() {
209        const isErrorOverlay = $overlay[0].classList.contains('overlay_error');
210
211        if (isErrorOverlay && handleErrorOverlayFocus()) {
212            return;
213        }
214
215        setTimeout(setTriggerFocus, 100);
216    }
217}

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.