PageSourceSearch

https://cruelty.farm/wp-content/plugins/neural-translate-pro/assets/language-switcher.js?ver=1787488744

js cruelty.farm collected 2026-10-02 08:40:45 UTC 9,807 bytes, 269 lines download raw bytes

1/**
2 * AI Translate Language Switcher Shortcode JavaScript
3 * Handles dropdown toggle and accessibility features
4 */
5(function() {
6    'use strict';
7
8    // Initialize when DOM is ready
9    document.addEventListener('DOMContentLoaded', function() {
10        initLanguageSwitchers();
11    });
12
13    /**
14     * Initialize all language switcher dropdowns
15     */
16    function initLanguageSwitchers() {
17        const switchers = document.querySelectorAll('.ai-language-switcher-dropdown');
18
19        switchers.forEach(function(switcher) {
20            const button = switcher.querySelector('.ai-language-switcher-btn');
21            const menu = switcher.querySelector('.ai-language-switcher-menu');
22
23            if (!button || !menu) {
24                return;
25            }
26
27            // Toggle dropdown on button click
28            button.addEventListener('click', function(event) {
29                event.preventDefault();
30                event.stopPropagation();
31                toggleDropdown(switcher);
32            });
33
34            // Handle keyboard navigation
35            button.addEventListener('keydown', function(event) {
36                handleButtonKeydown(event, switcher);
37            });
38
39            // Handle menu item keydown
40            const menuItems = menu.querySelectorAll('.ai-language-item');
41            menuItems.forEach(function(item, index) {
42                item.addEventListener('keydown', function(event) {
43                    handleMenuItemKeydown(event, switcher, index, menuItems);
44                });
45            });
46        });
47
48        // Close dropdowns when clicking outside
49        document.addEventListener('click', function(event) {
50            closeAllDropdowns();
51        });
52
53        // Close dropdowns on escape key
54        document.addEventListener('keydown', function(event) {
55            if (event.key === 'Escape') {
56                closeAllDropdowns();
57            }
58        });
59    }
60
61    /**
62     * Toggle dropdown visibility
63     */
64    function toggleDropdown(switcher) {
65        const button = switcher.querySelector('.ai-language-switcher-btn');
66        const menu = switcher.querySelector('.ai-language-switcher-menu');
67        const isExpanded = button.getAttribute('aria-expanded') === 'true';
68
69        if (isExpanded) {
70            closeDropdown(switcher);
71        } else {
72            // Close other dropdowns first
73            closeAllDropdowns();
74            openDropdown(switcher);
75        }
76    }
77
78    /**
79     * Open a dropdown
80     */
81    function openDropdown(switcher) {
82        const button = switcher.querySelector('.ai-language-switcher-btn');
83        const menu = switcher.querySelector('.ai-language-switcher-menu');
84
85        button.setAttribute('aria-expanded', 'true');
86        menu.removeAttribute('hidden');
87
88        // Focus first menu item for keyboard navigation
89        const firstItem = menu.querySelector('.ai-language-item');
90        if (firstItem) {
91            firstItem.focus();
92        }
93    }
94
95    /**
96     * Close a dropdown
97     */
98    function closeDropdown(switcher) {
99        const button = switcher.querySelector('.ai-language-switcher-btn');
100        const menu = switcher.querySelector('.ai-language-switcher-menu');
101
102        button.setAttribute('aria-expanded', 'false');
103        menu.setAttribute('hidden', '');
104
105        // Return focus to button
106        button.focus();
107    }
108
109    /**
110     * Close all dropdowns
111     */
112    function closeAllDropdowns() {
113        const openSwitchers = document.querySelectorAll('.ai-language-switcher-dropdown .ai-language-switcher-btn[aria-expanded="true"]');
114
115        openSwitchers.forEach(function(button) {
116            const switcher = button.closest('.ai-language-switcher-dropdown');
117            if (switcher) {
118                closeDropdown(switcher);
119            }
120        });
121    }
122
123    /**
124     * Handle button keyboard events
125     */
126    function handleButtonKeydown(event, switcher) {
127        const button = switcher.querySelector('.ai-language-switcher-btn');
128
129        switch (event.key) {
130            case 'ArrowDown':
131            case 'Enter':
132            case ' ':
133                event.preventDefault();
134                toggleDropdown(switcher);
135                break;
136            case 'ArrowUp':
137                event.preventDefault();
138                // Open dropdown and focus last item
139                closeAllDropdowns();
140                openDropdown(switcher);
141                const menu = switcher.querySelector('.ai-language-switcher-menu');
142                const lastItem = menu.querySelector('.ai-language-item:last-child');
143                if (lastItem) {
144                    lastItem.focus();
145                }
146                break;
147        }
148    }
149
150    /**
151     * Handle menu item keyboard events
152     */
153    function handleMenuItemKeydown(event, switcher, currentIndex, menuItems) {
154        const menu = switcher.querySelector('.ai-language-switcher-menu');
155
156        switch (event.key) {
157            case 'ArrowDown':
158                event.preventDefault();
159                const nextIndex = Math.min(currentIndex + 1, menuItems.length - 1);
160                menuItems[nextIndex].focus();
161                break;
162            case 'ArrowUp':
163                event.preventDefault();
164                const prevIndex = Math.max(currentIndex - 1, 0);
165                menuItems[prevIndex].focus();
166                break;
167            case 'Home':
168                event.preventDefault();
169                menuItems[0].focus();
170                break;
171            case 'End':
172                event.preventDefault();
173                menuItems[menuItems.length - 1].focus();
174                break;
175            case 'Escape':
176                event.preventDefault();
177                closeDropdown(switcher);
178                break;
179            case 'Enter':
180            case ' ':
181                // Let the link handle the navigation
182                break;
183        }
184    }
185
186    // Mobile menu support - toggle submenus on click/tap
187    initMobileMenuSupport();
188})();
189
190/**
191 * Initialize mobile menu support for language switcher submenus
192 */
193function initMobileMenuSupport() {
194    let attempts = 0;
195    const maxAttempts = 10;
196
197    function bindMobileSwitchers() {
198        // Find language switcher menu items
199        const languageItems = document.querySelectorAll('.menu-item-language-switcher');
200        let boundCount = 0;
201
202        languageItems.forEach(function(item) {
203            const link = item.querySelector('.ai-menu-language-current');
204            const submenu = item.querySelector('.sub-menu');
205
206            // Only handle dropdown items (with submenu), inline items work without JS
207            if (link && submenu) {
208                if (link.getAttribute('data-ai-mobile-bound') === '1') {
209                    return;
210                }
211                link.setAttribute('data-ai-mobile-bound', '1');
212                boundCount++;
213
214                // Add click handler for mobile
215                link.addEventListener('click', function(event) {
216                    // Prevent hash-jumps and theme anchor handlers from hijacking this toggle.
217                    event.preventDefault();
218
219                    // Use click-to-expand when: (a) touch device (no hover) or (b) narrow viewport (no hover rule <769px).
220                    var needsClick = window.matchMedia('(hover: none)').matches || window.innerWidth <= 768;
221                    if (needsClick) {
222                        event.stopPropagation();
223
224                        // Toggle submenu visibility
225                        const isVisible = submenu.classList.contains('ai-force-show');
226
227                        if (isVisible) {
228                            submenu.style.display = '';
229                            submenu.style.removeProperty('max-height');
230                            submenu.classList.remove('ai-force-show');
231                            link.setAttribute('aria-expanded', 'false');
232                        } else {
233                            // Close other language switcher submenus first
234                            document.querySelectorAll('.menu-item-language-switcher .sub-menu').forEach(function(otherSubmenu) {
235                                if (otherSubmenu !== submenu) {
236                                    otherSubmenu.style.display = '';
237                                    otherSubmenu.style.removeProperty('max-height');
238                                    otherSubmenu.classList.remove('ai-force-show');
239                                    const otherLink = otherSubmenu.closest('.menu-item-language-switcher').querySelector('.ai-menu-language-current');
240                                    if (otherLink) {
241                                        otherLink.setAttribute('aria-expanded', 'false');
242                                    }
243                                }
244                            });
245
246                            submenu.style.display = 'block';
247                            submenu.classList.add('ai-force-show');
248                            link.setAttribute('aria-expanded', 'true');
249                            // Override theme's max-height:0 on submenus (many mobile themes use inline max-height:0 !important)
250                            submenu.style.setProperty('max-height', '500px', 'important');
251                        }
252                    }
253                });
254
255                // Add ARIA attributes for accessibility
256                link.setAttribute('aria-expanded', 'false');
257                link.setAttribute('aria-haspopup', 'true');
258            }
259        });
260
261        // Retry shortly for themes that render/replace menus after initial load.
262        if (boundCount === 0 && attempts < maxAttempts) {
263            attempts++;
264            setTimeout(bindMobileSwitchers, 300);
265        }
266    }
267
268    bindMobileSwitchers();
269}

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.