PageSourceSearch

https://fastpay.bg/wp-content/plugins/translatepress-multilingual/…rp-frontend-language-switcher.js?ver=2.10.6

js fastpay.bg collected 2026-10-09 04:25:45 UTC 9,731 bytes, 268 lines download raw bytes

1class BaseSwitcher {
2    constructor(rootEl) {
3        this.root = rootEl;
4        this.list = rootEl?.querySelector('.trp-switcher-dropdown-list') || null;
5        this.isOpen = false;
6        this._pendingFocusOnOpen = false;
7        if (!this.root || !this.list) return;
8
9        if (!this.list.id) {
10            this.list.id = `trp-list-${Math.random().toString(36).slice(2, 9)}`;
11        }
12
13        // Single transitionend handler to drop .is-transitioning and finalize hidden/inert
14        this._onTe = (e) => {
15            if (e.target !== this.list || e.propertyName !== 'max-height') return;
16
17            this.root.classList.remove('is-transitioning');
18
19            if (!this.isOpen) {
20                this.list.hidden = true;
21                this.list.setAttribute('inert', '');
22            } else if (this._pendingFocusOnOpen) {
23                this._pendingFocusOnOpen = false;
24                const first = this.list.querySelector(
25                    '[role="option"], a, button, [tabindex]:not([tabindex="-1"])'
26                );
27                first?.focus?.({ preventScroll: true });
28            }
29        };
30        this.list.addEventListener('transitionend', this._onTe);
31
32        this.collapse();
33        this.setAutoWidth();
34
35        this.bindKeyboard(this.root);
36    }
37
38    collapse() {
39        this.list.hidden = true;
40        this.list.setAttribute('inert', '');
41        this.setExpanded(false);
42        this.root.classList.remove('is-transitioning');
43    }
44
45    /**
46     * If width is set to auto, fix it to the calculated width + 20px
47     * We do this in order to avoid width shift on hover
48     * */
49    setAutoWidth() {
50        const bonusWidth = 20;
51
52        const cs = getComputedStyle(this.root);
53        const declaredWidth = cs.getPropertyValue('--switcher-width').trim();
54
55        if (declaredWidth === 'auto') {
56            const initialWidth = this.root.getBoundingClientRect().width;
57
58            this.root.style.setProperty('--switcher-width', (initialWidth + bonusWidth) + 'px');
59        }
60    }
61
62    setExpanded(open) {
63        const val = String(!!open);
64        this.root.setAttribute('aria-expanded', val);
65        this.root.classList.toggle('is-open', !!open);
66    }
67
68    setOpen(open, { source = null } = {}) {
69        if (!this.root || !this.list || open === this.isOpen) return;
70
71        // Honor reduced motion: skip the transition entirely (still class-driven)
72        const prefersReduced = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches;
73
74        this.isOpen = open;
75
76        if (open) {
77            // Prepare: must be visible for CSS transition to run
78            this.list.hidden = false;
79            this.list.removeAttribute('inert');
80
81            if (prefersReduced) {
82                this.root.classList.remove('is-transitioning');
83                this.setExpanded(true);
84            } else {
85                this.root.classList.add('is-transitioning');
86                // Next frame so the browser registers the pre-open (max-height:0) state
87                requestAnimationFrame(() => this.setExpanded(true));
88            }
89
90            // keyboard open should move focus after transition completes
91            this._pendingFocusOnOpen = (source?.type === 'keydown');
92
93        } else {
94            if (prefersReduced) {
95                this.setExpanded(false);
96                this.list.hidden = true;
97                this.list.setAttribute('inert', '');
98                this.root.classList.remove('is-transitioning');
99            } else {
100                this.root.classList.add('is-transitioning');
101                // Removing is-open triggers CSS max-height → 0 animation
102                this.setExpanded(false);
103            }
104        }
105    }
106
107    bindKeyboard(target) {
108        target.addEventListener('keydown', (e) => {
109            if (e.key === 'Enter' || e.key === ' ') {
110                e.preventDefault();
111                this.setOpen(!this.isOpen, { source: e });
112            }
113            if (e.key === 'Escape') {
114                this.setOpen(false, { source: e });
115                target.focus?.();
116            }
117        });
118    }
119}
120
121class ShortcodeSwitcher extends BaseSwitcher {
122    constructor(wrapper) {
123        const overlay =
124                  wrapper.querySelector('.trp-language-switcher.trp-shortcode-overlay')
125                  || [...wrapper.querySelectorAll('.trp-language-switcher')]
126                      .find(el => el.classList.contains('trp-shortcode-overlay'));
127
128        // Overlay must be interactable; ensure no accidental hidden/inert from server
129        overlay.hidden = false;
130        overlay.removeAttribute('hidden');
131        overlay.removeAttribute('inert');
132        if ('inert' in overlay) overlay.inert = false;
133
134        super(overlay);
135        if (!this.root || !this.list) return;
136
137        // ARIA on overlay (focusable container)
138        this.root.setAttribute('role', 'listbox');
139        this.root.setAttribute('aria-haspopup', 'listbox');
140        this.root.setAttribute('aria-controls', this.list.id);
141        if (!this.root.hasAttribute('tabindex')) this.root.setAttribute('tabindex', '0');
142
143        const isClickMode =
144                  this.root.classList.contains('trp-open-on-click') ||
145                  wrapper.dataset.openMode === 'click' ||
146                  wrapper.classList.contains('trp-open-on-click');
147
148        if (isClickMode) {
149            // Click anywhere on overlay EXCEPT inside the list
150            this.root.addEventListener('click', (e) => {
151                const inList = e.target.closest('.trp-switcher-dropdown-list');
152                if (!inList) {
153                    e.preventDefault();
154                    e.stopPropagation();
155                    this.setOpen(!this.isOpen, { source: e });
156                }
157            }, true);
158
159            // Outside click closes
160            this.onDocClick = (evt) => {
161                if (!wrapper.contains(evt.target)) this.setOpen(false, { source: evt });
162            };
163            document.addEventListener('click', this.onDocClick, true);
164
165            // Focus leaving wrapper closes
166            wrapper.addEventListener('focusout', () => {
167                setTimeout(() => {
168                    if (!wrapper.contains(document.activeElement)) {
169                        this.setOpen(false, { source: 'keyboard' });
170                    }
171                }, 0);
172            });
173        } else {
174            // Hover mode on overlay
175            this.root.addEventListener('mouseenter', (e) => this.setOpen(true,  { source: e }));
176            this.root.addEventListener('mouseleave', (e) => this.setOpen(false, { source: e }));
177        }
178    }
179}
180
181class FloaterSwitcher extends BaseSwitcher {
182    constructor(el) {
183        super(el);
184
185        el.addEventListener('mouseenter', (e) => this.setOpen(true,  { source: e }));
186        el.addEventListener('mouseleave', (e) => this.setOpen(false, { source: e }));
187
188        this.onDocClick = (evt) => { if (!el.contains(evt.target)) this.setOpen(false, { source: evt }); };
189        document.addEventListener('click', this.onDocClick, true);
190    }
191}
192
193document.addEventListener('DOMContentLoaded', () => {
194    // Front-end or classic admin
195    initLanguageSwitchers(document);
196
197    // If no wrapper and we are in Gutenberg, watch for async SSR insert
198    if (
199        inGutenberg() &&
200        !getEditorDoc().querySelector('.trp-shortcode-switcher__wrapper')
201    ) {
202        observeWrapperUntilFound();
203    }
204});
205
206/** For shortcode switcher
207 *  Mark the shortcodes that were initialized
208 *
209 * */
210const TRP_BOUND = new WeakSet();
211const mark = (el) => TRP_BOUND.add(el);
212const isMarked = (el) => TRP_BOUND.has(el);
213
214function inGutenberg() {
215    return document.body?.classList?.contains('block-editor-page')
216        || !!(window.wp?.data?.select?.('core/block-editor'));
217}
218
219// If editor uses an iframe canvas, work inside it
220function getEditorDoc() {
221    const ifr = document.querySelector('iframe[name="editor-canvas"], .editor-canvas__iframe');
222
223    return (ifr && ifr.contentDocument) ? ifr.contentDocument : document;
224}
225
226function initLanguageSwitchers(root = document) {
227    root.querySelectorAll('.trp-language-switcher.trp-ls-dropdown:not(.trp-shortcode-switcher):not(.trp-opposite-language)')
228        .forEach(el => { if (!isMarked(el)) { mark(el); new FloaterSwitcher(el); } });
229
230    root.querySelectorAll('.trp-shortcode-switcher__wrapper')
231        .forEach(wrapper => {
232            const overlay = wrapper.querySelector('.trp-language-switcher:not(.trp-opposite-button)');
233
234            if (overlay && !isMarked(overlay)) {
235                mark(overlay);
236                new ShortcodeSwitcher(wrapper);
237            }
238        });
239}
240
241function observeWrapperUntilFound() {
242    const edDoc = getEditorDoc();
243
244    // If it already exists, init and stop.
245    if (edDoc.querySelector('.trp-shortcode-switcher__wrapper')) {
246        initLanguageSwitchers(edDoc);
247
248        return;
249    }
250
251    const mo = new MutationObserver((mutations) => {
252        for (const m of mutations) {
253            for (const n of m.addedNodes) {
254                if (!(n instanceof Element)) continue;
255
256                if (
257                    n.matches?.('.trp-shortcode-switcher__wrapper') ||
258                    n.querySelector?.('.trp-shortcode-switcher__wrapper')
259                ) {
260                    initLanguageSwitchers(edDoc);
261                }
262            }
263        }
264    });
265
266    mo.observe(edDoc, { childList: true, subtree: true });
267}
268

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.