1(function($) { 2 // Cookie Helpers 3 function setCookie(name, value, days) { 4 let expires = ""; 5 if (days) { 6 let date = new Date(); 7 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 8 expires = "; expires=" + date.toUTCString(); 9 } 10 document.cookie = name + "=" + (value || "") + expires + "; path=/"; 11 } 12 13 function getCookie(name) { 14 let nameEQ = name + "="; 15 let ca = document.cookie.split(';'); 16 for (let i = 0; i < ca.length; i++) { 17 let c = ca[i]; 18 while (c.charAt(0) == ' ') c = c.substring(1, c.length); 19 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); 20 } 21 return null; 22 } 23 24 const debounce = window.DAPlacement ? window.DAPlacement.debounce : function(fn) { return fn; }; 25 26 const decoder = $('<div/>'); 27 function decodeEntities(html) { 28 return decoder.html(html).text(); 29 } 30 31 $(document).ready(function() { 32 if (typeof daTranslationConfig === 'undefined' || !window.DAPlacement) return; 33 34 const container = $('#da-translation-switcher-container'); 35 if (!container.length) return; 36 37 let lastDevice = null; 38 let isSuccessfullyMoved = false; 39 40 function moveSwitcher(force = false) { 41 const isMobile = window.DAPlacement.isMobile(); 42 const currentDevice = window.DAPlacement.getDevice(); 43 44 // Avoid redundant work if device hasn't changed and we're already in the DOM 45 if (!force && lastDevice === currentDevice && isSuccessfullyMoved && container.parent().length > 0) { 46 return true; 47 } 48 49 const position = isMobile ? daTranslationConfig.mobilePosition : daTranslationConfig.desktopPosition; 50 const selector = isMobile ? daTranslationConfig.mobileCustomSelector : daTranslationConfig.desktopCustomSelector; 51 const injType = isMobile ? daTranslationConfig.mobileInjectionType : daTranslationConfig.desktopInjectionType; 52 53 if (position === 'none') { 54 container.hide(); 55 lastDevice = currentDevice; 56 isSuccessfullyMoved = true; 57 return true; 58 } 59 60 // Remove previous classes 61 container.removeClass('da-in-menu'); 62 63 if (position === 'floater') { 64 const floaterConfig = (isMobile ? daTranslationConfig.mobileFloater : daTranslationConfig.desktopFloater) || {corner: 'br', offsetX: '20px', offsetY: '20px'}; 65 window.DAPlacement.applyFloater(container, floaterConfig); 66 } else { 67 // Special handling for mobile menu to ensure it's inside the UL if possible 68 let injectSelector = selector; 69 let needsLiWrap = false; 70 if (position === 'menu' && selector) {
71 const menuUl = $(selector).first().find('ul').first(); 72 if (menuUl.length) { 73 injectSelector = null; 74 needsLiWrap = true; 75 switch (injType) { 76 case 'before': container.insertBefore(menuUl); break; 77 case 'prepend': container.prependTo(menuUl); break; 78 case 'append': container.appendTo(menuUl); break; 79 default: container.insertAfter(menuUl); break; 80 } 81 } 82 } 83 84 if (injectSelector !== null) { 85 if (!window.DAPlacement.inject(container, injectSelector, injType)) { 86 return false; // Target not found or no selector 87 } 88 } 89 90 if (needsLiWrap && !container.parent().is('li')) { 91 container.wrap('<li class="da-menu-switcher-wrapper" style="list-style:none;"></li>'); 92 } 93 94 if (position === 'menu') { 95 container.addClass('da-in-menu'); 96 } else { 97 // Reset fixed/absolute positioning if moved from floater 98 window.DAPlacement.clearFloater(container); 99 } 100 } 101 102 // Update before/after markup based on device 103 const beforeMarkup = isMobile ? daTranslationConfig.mobileBefore : daTranslationConfig.desktopBefore; 104 const afterMarkup = isMobile ? daTranslationConfig.mobileAfter : daTranslationConfig.desktopAfter; 105 106 container.find('.da-before-markup').html(beforeMarkup || ''); 107 container.find('.da-after-markup').html(afterMarkup || ''); 108 109 // Update appropriate style and labels based on device 110 const displayType = isMobile ? daTranslationConfig.mobileDisplayType : daTranslationConfig.desktopDisplayType; 111 112 container.find('.lang-option, #languageDropdown option').each(function() { 113 const el = $(this); 114 const showFlag = displayType.indexOf('flag') !== -1; 115 const isCode = displayType.indexOf('code') !== -1; 116 const isPill = el.is('.lang-option'); 117 118 let text = isCode ? el.data('label-code') : el.data('label-name'); 119 let label = text; 120 121 // Flags are always <img> tags; only pills render them inline. 122 // Dropdown options stay text-only â Select2's templates show 123 // the flag markup from data-label-flag instead. 124 if (showFlag && isPill) { 125 const imgMatch = (el.data('label-flag') || '').match(/<img[^>]+>/); 126 if (imgMatch) { 127 label = imgMatch[0] + ' ' + text; 128 } 129 } 130 131 if (el.is('option')) { 132 const decoded = decodeEntities(label); 133 if (el.text() !== decoded) el.text(decoded); 134 } else { 135 if (el.html() !== label) el.html(label); 136 } 137 }); 138 139 // Decide style based on device and position 140 let style = (isMobile ? daTranslationConfig.mobileStyle : daTranslationConfig.desktopStyle) || 'pill'; 141 142 // Show appropriate style container 143 container.find('.da-switcher-style').hide(); 144 container.find('.da-style-' + style).show(); 145 146 // Initialize Select2 if dropdown style is used and Select2 is available 147 if (style === 'dropdown' && typeof $.fn.select2 !== 'undefined') { 148 const dropdown = container.find('#languageDropdown'); 149 if (!dropdown.data('select2')) { 150 dropdown.select2({ 151 minimumResultsForSearch: Infinity, 152 templateResult: function(state) { 153 if (!state.id) return state.text; 154 const labelFlag = $(state.element).data('label-flag'); 155 if (labelFlag && labelFlag.indexOf('<img') !== -1) { 156 return $('<span data-notranslate>' + labelFlag + '</span>'); 157 } 158 return state.text; 159 }, 160 templateSelection: function(state) { 161 if (!state.id) return state.text; 162 const labelFlag = $(state.element).data('label-flag'); 163 if (labelFlag && labelFlag.indexOf('<img') !== -1) { 164 return $('<span data-notranslate>' + labelFlag + '</span>'); 165 } 166 return state.text; 167 }, 168 width: 'auto', 169 dropdownAutoWidth: true, 170 containerCssClass: 'da-select2-container', 171 dropdownCssClass: 'da-select2-dropdown' 172 }); 173 } 174 } 175 176 container.show(); 177 lastDevice = currentDevice; 178 isSuccessfullyMoved = true; 179 return true; 180 } 181 182 // Watcher for injection selector (tries every 100ms for up to 5s) 183 window.DAPlacement.watch(function() { 184 return moveSwitcher(); 185 }); 186 187 // Apply saved language on load 188 const savedLang = getCookie('da_lang'); 189 if (savedLang) { 190 let attempts = 0; 191 const interval = setInterval(function() { 192 attempts++; 193 if (typeof lzSwitchLanguage === 'function') { 194 lzSwitchLanguage(savedLang); 195 196 // Sync UI (in case PHP was cached) 197 container.find('.lang-option').removeClass('active'); 198 container.find('.lang-option[data-lang="' + s
198avedLang + '"]').addClass('active'); 199 container.find('#languageDropdown option').each(function() { 200 if ($(this).data('lang-code') === savedLang) { 201 $(this).prop('selected', true); 202 } 203 }); 204 205 clearInterval(interval); 206 } 207 if (attempts > 100) clearInterval(interval); // 10 seconds max 208 }, 100); 209 } 210 211 // Dropdown change handler 212 container.on('change', '#languageDropdown', function() { 213 const selectedLanguage = $(this).find('option:selected').data('lang-code'); 214 setCookie('da_lang', selectedLanguage, 30); 215 if (typeof lzSwitchLanguage === 'function') { 216 lzSwitchLanguage(selectedLanguage); 217 } 218 }); 219 220 // Pill Style change handler 221 container.on('click', '.lang-option', function(e) { 222 e.preventDefault(); 223 const selectedLang = $(this).data('lang'); 224 setCookie('da_lang', selectedLang, 30); 225 226 container.find('.lang-option').removeClass('active'); 227 $(this).addClass('active'); 228 229 if (typeof lzSwitchLanguage === 'function') { 230 lzSwitchLanguage(selectedLang); 231 } 232 }); 233 234 $(window).on('resize', debounce(function() { 235 moveSwitcher(); 236 }, 150)); 237 }); 238})(jQuery);
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.