1/** 2 * Translate Switcher â front-end (vanilla JS, no dependencies). 3 */ 4( function () { 5 'use strict'; 6 7 var data = window.TSData || {}; 8 var mobileBreakpoint = parseInt( data.mobileBreakpoint, 10 ) || 782; 9 var observer = null; 10 var scheduled = false; 11 var dropdownSequence = 0; 12 13 /* ââ Viewport mode âââââââââââââââââââââââââââââââââââââââââ */ 14 function syncViewportMode() { 15 var mobile = window.innerWidth <= mobileBreakpoint; 16 document.documentElement.classList.toggle( 'ts-mobile-active', mobile ); 17 return mobile; 18 } 19 20 /* ââ Floating visibility / dismissal âââââââââââââââââââââââ */ 21 function safeStorage( type ) { 22 try { 23 var storage = type === 'local' ? window.localStorage : window.sessionStorage; 24 var probe = '__ts_probe__'; 25 storage.setItem( probe, '1' ); 26 storage.removeItem( probe ); 27 return storage; 28 } catch ( e ) { 29 return null; 30 } 31 } 32 33 function initFloatingDismiss() { 34 var floats = document.querySelectorAll( '.ts-floating[data-ts-dismiss-key]' ); 35 for ( var i = 0; i < floats.length; i++ ) { 36 ( function ( floating ) { 37 var key = floating.getAttribute( 'data-ts-dismiss-key' ) || ''; 38 var days = parseInt( floating.getAttribute( 'data-ts-dismiss-days' ), 10 ) || 0; 39 var storage = safeStorage( days > 0 ? 'local' : 'session' ); 40 var stored = storage && key ? storage.getItem( key ) : null; 41 var dismissed = false; 42 43 if ( days > 0 && stored ) { 44 var expires = parseInt( stored, 10 ) || 0; 45 dismissed = expires > Date.now(); 46 if ( ! dismissed && storage ) { storage.removeItem( key ); } 47 } else if ( days === 0 && stored === '1' ) { 48 dismissed = true; 49 } 50 51 if ( dismissed ) { floating.hidden = true; } 52 53 var button = floating.querySelector( '.ts-floating-dismiss' ); 54 if ( ! button ) { return; } 55 button.addEventListener( 'click', function () { 56 floating.hidden = true; 57 if ( ! storage || ! key ) { return; } 58 if ( days > 0 ) { 59 storage.setItem( key, String( Date.now() + days * 86400000 ) ); 60 } else { 61 storage.setItem( key, '1' ); 62 } 63 } ); 64 } )( floats[ i ] ); 65 } 66 } 67 68 /* Keep top floating controls below visible sticky/fixed headers. */ 69 function resolveFloatingHeaderAvoidance() { 70 var floats = document.querySelectorAll( '.ts-floating[data-ts-avoid-header="1"]' ); 71 if ( ! floats.length ) { return; } 72 73 var selectors = '#wpadminbar, header, #masthead, .site-header, .sticky-header, .header-sticky, .elementor-location-header, [data-elementor-type="header"]'; 74 var headers; 75 try { headers = document.querySelectorAll( selectors ); } catch ( e ) { headers = []; } 76 77 for ( var i = 0; i < floats.length; i++ ) { 78 var floating = floats[ i ]; 79 floating.style.removeProperty( '--ts-header-avoid-y' ); 80 if ( floating.hidden || ! isVisible( floating ) ) { continue; } 81 82 var floatStyle = window.getComputedStyle ? window.getComputedStyle( floating ) : null; 83 if ( ! floatStyle || floatStyle.top === 'auto' ) { continue; } 84 var baseTop = parseFloat( floatStyle.top ); 85 if ( isNaN( baseTop ) || baseTop > Math.max( 220, window.innerHeight * 0.3 ) ) { continue; } 86 87 var candidates = []; 88 for ( var h = 0; h < headers.length; h++ ) { 89 var header = headers[ h ]; 90 if ( header === floating || header.contains( floating ) || ! isVisible( header ) ) { continue; } 91 var headerStyle = window.getComputedStyle ? window.getComputedStyle( header ) : null; 92 if ( ! headerStyle || ( headerStyle.position !== 'fixed' && headerStyle.position !== 'sticky' ) ) { continue; } 93 var rect = header.getBoundingClientRect(); 94 if ( rect.bottom <= 0 || rect.height > window.innerHeight * 0.5 || rect.top > Math.max( 120, window.innerHeight * 0.15 ) ) { continue; } 95 candidates.push( { top: rect.top, bottom: rect.bottom } ); 96 } 97 98 // Resolve stacked bars (for example wp-admin bar + sticky site header). 99 candidates.sort( function ( a, b ) { return a.top - b.top; } ); 100 var obstruction = 0; 101 for ( var c = 0; c < candidates.length; c++ ) { 102 if ( candidates[ c ].top <= obstruction + 8 ) { 103 obstruction = Math.max( obstruction, Math.ceil( candidates[ c ].bottom ) ); 104 } 105 } 106 if ( obstruction > 0 ) { 107 floating.style.setProperty( '--ts-header-avoid-y', obstruction + 'px' ); 108 } 109 } 110 } 111 112 /* ââ Emoji flag support detection ââââââââââââââââââââââââââ */ 113 function supportsFlagEmoji() { 114 try { 115 var canvas = document.createElement( 'canvas' ); 116 if ( ! canvas.getContext ) { return false; } 117 var ctx = canvas.getContext( '2d' ); 118 if ( ! ctx ) { return false; } 119 canvas.width = 24; 120 canvas.height = 16; 121 ctx.font = '16px sans-serif'; 122 ctx.textBaseline = 'top'; 123 ctx.fillText( '\uD83C\uDDEC\uD83C\uDDE7', 0, 0 ); // ð¬ð§ 124 var px = ctx.getImageData( 6, 6, 1, 1 ).data; 125 return ! ( px[ 0 ] === px[ 1 ] && px[ 1 ] === px[ 2 ] ); 126 } catch ( e ) { 127 return false;
128 } 129 } 130 131 function applyFlagFallback( root ) { 132 if ( data.flagMode && data.flagMode !== 'emoji' ) { return; } 133 if ( supportsFlagEmoji() ) { return; } 134 root = root || document; 135 var flags = root.querySelectorAll( '.ts-flag-emoji[data-chip]' ); 136 for ( var i = 0; i < flags.length; i++ ) { 137 var chip = flags[ i ].getAttribute( 'data-chip' ) || ''; 138 flags[ i ].textContent = chip; 139 flags[ i ].classList.remove( 'ts-flag-emoji' ); 140 flags[ i ].classList.add( 'ts-flag-chip' ); 141 } 142 } 143 144 /* ââ Active state ââââââââââââââââââââââââââââââââââââââââââ */ 145 function markActive( root ) { 146 var cur = data.current || ''; 147 if ( ! cur ) { return; } 148 root = root || document; 149 var items = root.querySelectorAll( '.ts-pill, .ts-flag-item, .ts-minimal-item, .ts-option-link' ); 150 for ( var i = 0; i < items.length; i++ ) { 151 var parent = items[ i ].parentNode; 152 var lang = items[ i ].getAttribute( 'data-lang' ) || ( parent && parent.getAttribute ? parent.getAttribute( 'data-lang' ) : '' ); 153 if ( lang === cur ) { 154 items[ i ].classList.add( 'ts-active' ); 155 items[ i ].setAttribute( 'aria-current', 'page' ); 156 } 157 } 158 } 159 160 /* ââ Dropdown / theme-isolated menu disclosure âââââââââââââ */ 161 function dropdownButton( dd ) { 162 return dd ? dd.querySelector( '.ts-selected' ) : null; 163 } 164 165 function dropdownMenu( dd ) { 166 if ( ! dd ) { return null; } 167 return dd._tsMenu || dd.querySelector( '.ts-menu' ); 168 } 169 170 function switchingState( switcher ) { 171 if ( ! switcher ) { return; } 172 switcher.classList.add( 'ts-switching' ); 173 window.setTimeout( function () { 174 if ( switcher && switcher.classList ) { switcher.classList.remove( 'ts-switching' ); } 175 }, 2500 ); 176 } 177 178 function positionPortalMenu( dd ) { 179 var menu = dropdownMenu( dd ); 180 var button = dropdownButton( dd ); 181 if ( ! menu || ! button || ! menu.classList.contains( 'ts-menu-portal' ) ) { return; } 182 183 var rect = button.getBoundingClientRect(); 184 var viewportWidth = document.documentElement.clientWidth || window.innerWidth; 185 var viewportHeight = document.documentElement.clientHeight || window.innerHeight; 186 var gap = 4; 187 var edge = 8; 188 189 menu.style.minWidth = Math.max( 1, Math.ceil( rect.width ) ) + 'px'; 190 menu.style.maxWidth = Math.max( 1, viewportWidth - edge * 2 ) + 'px'; 191 menu.style.visibility = 'hidden'; 192 menu.classList.add( 'ts-portal-open' ); 193 194 var menuRect = menu.getBoundingClientRect(); 195 var width = Math.min( menuRect.width || rect.width, viewportWidth - edge * 2 ); 196 var height = Math.min( menuRect.height || 0, viewportHeight - edge * 2 ); 197 var left = Math.max( edge, Math.min( rect.left, viewportWidth - edge - width ) ); 198 var roomBelow = viewportHeight - rect.bottom - edge; 199 var roomAbove = rect.top - edge; 200 var openAbove = height > roomBelow && roomAbove > roomBelow; 201 var top = openAbove ? Math.max( edge, rect.top - height - gap ) : Math.min( viewportHeight - edge - height, rect.bottom + gap ); 202 203 menu.style.left = Math.round( left ) + 'px'; 204 menu.style.top = Math.max( edge, Math.round( top ) ) + 'px'; 205 menu.style.visibility = ''; 206 } 207 208 function portalMenu( dd ) { 209 var menu = dropdownMenu( dd ); 210 if ( ! menu || ! document.body ) { return; } 211 dd._tsMenu = menu; 212 if ( menu.parentNode !== document.body ) { 213 document.body.appendChild( menu ); 214 } 215 menu.classList.add( 'ts-menu-portal', 'ts-portal-open' ); 216 positionPortalMenu( dd ); 217 } 218 219 function restoreMenu( dd ) { 220 var menu = dropdownMenu( dd ); 221 if ( ! menu ) { return; } 222 menu.classList.remove( 'ts-portal-open', 'ts-menu-portal' ); 223 menu.style.removeProperty( 'left' ); 224 menu.style.removeProperty( 'top' ); 225 menu.style.removeProperty( 'min-width' ); 226 menu.style.removeProperty( 'max-width' ); 227 menu.style.removeProperty( 'visibility' ); 228 if ( menu.parentNode !== dd ) { dd.appendChild( menu ); } 229 } 230 231 function closeDropdown( dd, restoreFocus ) { 232 if ( ! dd ) { return; } 233 dd.classList.remove( 'ts-open' ); 234 var button = dropdownButton( dd ); 235 if ( button ) { button.setAttribute( 'aria-expanded', 'false' ); } 236 restoreMenu( dd ); 237 if ( restoreFocus && button ) { button.focus(); } 238 } 239 240 function closeAll( except ) { 241 var dds = document.querySelectorAll( '.ts-dropdown.ts-open' ); 242 for ( var i = 0; i < dds.length; i++ ) { 243 if ( dds[ i ] !== except ) { closeDropdown( dds[ i ], false ); } 244 } 245 } 246 247 function openDropdown( dd, focusEdge ) { 248 if ( ! dd ) { return; } 249 closeAll( dd ); 250 dd.classList.add( 'ts-open' ); 251 var button = dropdownButton( dd ); 252 if ( button ) { button.setAttribute( 'aria-expanded', 'true' ); } 253 portalMenu( dd ); 254
255 if ( focusEdge ) { 256 var menu = dropdownMenu( dd ); 257 var links = menu ? menu.querySelectorAll( '.ts-option-link' ) : []; 258 if ( links.length ) { links[ focusEdge === 'last' ? links.length - 1 : 0 ].focus(); } 259 } 260 } 261 262 function bindDropdown( dd, switcher ) { 263 if ( ! dd || dd.getAttribute( 'data-ts-bound' ) === '1' ) { return; } 264 dd.setAttribute( 'data-ts-bound', '1' ); 265 var button = dropdownButton( dd ); 266 var menu = dropdownMenu( dd ); 267 if ( ! button || ! menu ) { return; } 268 dd._tsMenu = menu; 269 dropdownSequence++; 270 var menuId = 'ts-language-menu-js-' + dropdownSequence; 271 menu.id = menuId; 272 button.setAttribute( 'aria-controls', menuId ); 273 274 button.addEventListener( 'click', function ( e ) { 275 e.preventDefault(); 276 e.stopPropagation(); 277 if ( dd.classList.contains( 'ts-open' ) ) { closeDropdown( dd, false ); } 278 else { openDropdown( dd, '' ); } 279 } ); 280 281 button.addEventListener( 'keydown', function ( e ) { 282 if ( e.key === 'ArrowDown' || e.key === 'ArrowUp' ) { 283 e.preventDefault(); 284 e.stopPropagation(); 285 openDropdown( dd, e.key === 'ArrowUp' ? 'last' : 'first' ); 286 } else if ( e.key === 'Escape' ) { 287 e.preventDefault(); 288 e.stopPropagation(); 289 closeDropdown( dd, true ); 290 } 291 } ); 292 293 menu.addEventListener( 'click', function ( e ) { 294 var link = e.target.closest ? e.target.closest( '.ts-option-link' ) : null; 295 if ( ! link ) { return; } 296 e.stopPropagation(); 297 switchingState( switcher ); 298 } ); 299 300 menu.addEventListener( 'keydown', function ( e ) { 301 var links = menu.querySelectorAll( '.ts-option-link' ); 302 if ( ! links.length ) { return; } 303 var idx = -1; 304 for ( var i = 0; i < links.length; i++ ) { 305 if ( links[ i ] === document.activeElement ) { idx = i; break; } 306 } 307 if ( e.key === 'ArrowDown' ) { 308 e.preventDefault(); e.stopPropagation(); links[ ( idx + 1 + links.length ) % links.length ].focus(); 309 } else if ( e.key === 'ArrowUp' ) { 310 e.preventDefault(); e.stopPropagation(); links[ ( idx - 1 + links.length ) % links.length ].focus(); 311 } else if ( e.key === 'Home' ) { 312 e.preventDefault(); e.stopPropagation(); links[ 0 ].focus(); 313 } else if ( e.key === 'End' ) { 314 e.preventDefault(); e.stopPropagation(); links[ links.length - 1 ].focus(); 315 } else if ( e.key === 'Escape' ) { 316 e.preventDefault(); e.stopPropagation(); closeDropdown( dd, true ); 317 } 318 } ); 319 } 320 321 function bindSwitcher( switcher ) { 322 if ( ! switcher || switcher.getAttribute( 'data-ts-interactions' ) === '1' ) { return; } 323 switcher.setAttribute( 'data-ts-interactions', '1' ); 324 bindDropdown( switcher.querySelector( '.ts-dropdown' ), switcher ); 325 switcher.addEventListener( 'click', function ( e ) { 326 var link = e.target.closest ? e.target.closest( '.ts-pill, .ts-flag-item, .ts-minimal-item' ) : null; 327 if ( ! link || ! switcher.contains( link ) ) { return; } 328 e.stopPropagation(); 329 switchingState( switcher ); 330 } ); 331 } 332 333 function bindSwitchers( root ) { 334 root = root || document; 335 if ( root.matches && root.matches( '.ts-switcher' ) ) { bindSwitcher( root ); } 336 var switchers = root.querySelectorAll ? root.querySelectorAll( '.ts-switcher' ) : []; 337 for ( var i = 0; i < switchers.length; i++ ) { bindSwitcher( switchers[ i ] ); } 338 } 339 340 function bindInteractions() { 341 bindSwitchers( document ); 342 document.addEventListener( 'click', function ( e ) { 343 if ( ! e.target.closest || ! e.target.closest( '.ts-dropdown, .ts-menu-portal' ) ) { closeAll( null ); } 344 } ); 345 } 346 347 /* ââ Universal header mount ââââââââââââââââââââââââââââââââ */ 348 function mountHeaderSwitchers() { 349 var template = document.getElementById( 'ts-header-mount-template' ); 350 if ( ! template || ! template.content ) { return false; } 351 352 var selector = template.getAttribute( 'data-ts-selector' ) || ''; 353 var placement = template.getAttribute( 'data-ts-placement' ) || 'append'; 354 var targets; 355 try { 356 targets = document.querySelectorAll( selector ); 357 } catch ( e ) { 358 return false; 359 } 360 361 var mounted = false; 362 for ( var i = 0; i < targets.length; i++ ) { 363 var target = targets[ i ]; 364 if ( target.getAttribute( 'data-ts-header-mounted' ) === '1' ) { continue; } 365 366 // Avoid duplicating an existing switcher already placed in the target. 367 if ( ( placement === 'append' || placement === 'prepend' ) && target.querySelector( '.ts-switcher' ) ) { 368 target.setAttribute( 'data-ts-header-mounted', '1' ); 369 continue; 370 } 371 372 var node = template.content.firstElementChild.cloneNode( true ); 373 node.setAttribute( 'data-ts-mounted', '1' ); 374 375 if ( placement === 'prepend' ) { 376 target.insertBefore( node, target.firstChild ); 377 } else if ( placement === 'before' && target.parentNode ) { 378 target.parentNode.insertBefore( node, target ); 379 } else if ( placement === 'after' && target.parentNode ) { 380 target.parentNode.insertBefore( node, target.nextSibling ); 381 } else { 382 target.appendChild( node ); 383 } 384 385 target.setAttribute( 'data-ts-header-mounted', '1' ); 386 applyFlagFallback( node ); 387 markActive( node ); 388 bindSwitchers( node );
389 mounted = true; 390 } 391 return mounted; 392 } 393 394 /* ââ Classic-menu vertical alignment guard ââââââââââââââââââ */ 395 function menuItemControl( item ) { 396 if ( ! item || ! item.querySelector ) { return null; } 397 return item.querySelector( '.ts-selected, .ts-pill, .ts-flag-item, .ts-minimal-item' ) || item.querySelector( '.ts-switcher' ) || item; 398 } 399 400 function adjacentMenuReference( item, controlRect ) { 401 var candidates = [ item.previousElementSibling, item.nextElementSibling ]; 402 var best = null; 403 var bestScore = -1; 404 for ( var i = 0; i < candidates.length; i++ ) { 405 var sibling = candidates[ i ]; 406 if ( ! sibling || sibling.classList.contains( 'ts-menu-switcher' ) || sibling.classList.contains( 'ts-menu-shortcode-item' ) || ! isVisible( sibling ) ) { continue; } 407 var ref = sibling.querySelector ? sibling.querySelector( 'a, button, [role="button"]' ) : null; 408 if ( ! ref || ! isVisible( ref ) ) { ref = sibling; } 409 var rect = ref.getBoundingClientRect(); 410 if ( rect.width <= 0 || rect.height <= 0 ) { continue; } 411 412 // Same-row controls overlap vertically even when their centres differ. 413 // Stacked/mobile menu rows do not, so they are intentionally ignored. 414 var overlap = Math.min( controlRect.bottom, rect.bottom ) - Math.max( controlRect.top, rect.top ); 415 var minOverlap = Math.min( controlRect.height, rect.height ) * 0.35; 416 if ( overlap < minOverlap ) { continue; } 417 var delta = ( rect.top + rect.height / 2 ) - ( controlRect.top + controlRect.height / 2 ); 418 if ( Math.abs( delta ) > 24 ) { continue; } 419 var score = overlap - Math.abs( delta ) * 0.1; 420 if ( score > bestScore ) { 421 best = { delta: delta }; 422 bestScore = score; 423 } 424 } 425 return best; 426 } 427 428 function alignClassicMenuSwitchers() { 429 var items = document.querySelectorAll( '.ts-menu-switcher, .ts-menu-shortcode-item' ); 430 for ( var i = 0; i < items.length; i++ ) { 431 items[ i ].classList.remove( 'ts-menu-align-adjusted' ); 432 items[ i ].style.removeProperty( '--ts-menu-shift-y' ); 433 } 434 if ( ! items.length || window.innerWidth <= mobileBreakpoint ) { return; } 435 436 for ( var j = 0; j < items.length; j++ ) { 437 var item = items[ j ]; 438 if ( ! isVisible( item ) ) { continue; } 439 var control = menuItemControl( item ); 440 if ( ! control || ! isVisible( control ) ) { continue; } 441 var rect = control.getBoundingClientRect(); 442 var ref = adjacentMenuReference( item, rect ); 443 if ( ! ref || Math.abs( ref.delta ) < 0.75 ) { continue; } 444 item.style.setProperty( '--ts-menu-shift-y', Math.round( ref.delta * 10 ) / 10 + 'px' ); 445 item.classList.add( 'ts-menu-align-adjusted' ); 446 } 447 } 448 449 /* ââ Mobile header collision guard âââââââââââââââââââââââââ */ 450 function isVisible( el ) { 451 if ( ! el ) { return false; } 452 var style = window.getComputedStyle ? window.getComputedStyle( el ) : null; 453 if ( style && ( style.display === 'none' || style.visibility === 'hidden' ) ) { return false; } 454 var rect = el.getBoundingClientRect(); 455 return rect.width > 0 && rect.height > 0; 456 } 457 458 function intersects( a, b ) { 459 return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top; 460 } 461 462 function collectMenuToggles() { 463 var selectors = [ 464 'button.menu-toggle', 465 '.menu-toggle', 466 '.navbar-toggler', 467 '.elementor-menu-toggle', 468 '.mobile-menu-toggle', 469 '.mobile-nav-toggle', 470 '.ast-mobile-menu-trigger-fill', 471 '.ast-button-wrap .menu-toggle', 472 '[data-toggle="collapse"][data-target*="menu"]', 473 '[aria-controls*="menu"]' 474 ]; 475 var found = []; 476 for ( var i = 0; i < selectors.length; i++ ) { 477 var nodes; 478 try { nodes = document.querySelectorAll( selectors[ i ] ); } catch ( e ) { nodes = []; } 479 for ( var j = 0; j < nodes.length; j++ ) { 480 var candidate = nodes[ j ]; 481 // Never treat Translate Switcher's own disclosure controls as a 482 // site navigation toggle. The dropdown button intentionally uses 483 // aria-controls="ts-language-menu-*", which matches the generic 484 // [aria-controls*="menu"] fallback used for unknown themes. 485 // Including it here makes a centred mobile floating switcher 486 // collide with itself and shift away from the requested centre. 487 if ( candidate.closest && candidate.closest( '.ts-switcher, .ts-menu-portal' ) ) { continue; } 488 if ( found.indexOf( candidate ) === -1 && isVisible( candidate ) ) { found.push( candidate ); } 489 } 490 } 491 return found; 492 } 493 494 function resolveMobileHeaderCollisions() { 495 var switchers = document.querySelectorAll( '.ts-context-menu, .ts-context-header-mount, .ts-context-floating' ); 496 for ( var i = 0; i < switchers.length; i++ ) { 497 switchers[ i ].classList.remove( 'ts-auto-shift', 'ts-collision-compact' ); 498 switchers[ i ].style.removeProperty( '--ts-auto-shift-x' ); 499 } 500 501 if ( ! syncViewportMode() || ! switchers.length ) { return; } 502 var toggles = collectMenuToggles(); 503 if ( ! toggles.length ) { return; } 504 505 var gap = 10; 506 var viewportWidth = document.documentElement.clientWidth || window.innerWidth; 507 for ( var s = 0; s < switchers.length; s++ ) { 508 var sw = switchers[ s ]; 509 if ( ! isVisible( sw ) ) { continue; } 510 511 var swRect = sw.getBoundingClientRect(); 512 var hit = null; 513 for ( var t = 0; t < toggles.length; t++ ) { 514 var toggle = toggles[ t ]; 515 // Defensive second guard: even if a future selector broadens the 516 // toggle list, a switcher must never resolve a collision against a 517 // control inside itself (or an ancestor wrapping itself). 518 if ( sw.contains( toggle ) || toggle.contains( sw ) ) { continue; } 519 var toggleRect = toggle.getBoundingClientRect(); 520 if ( intersects( swRect, toggleRect ) ) { 521 hit = toggleRect; 522 break; 523 } 524 } 525 if ( ! hit ) { continue; } 526 527 // First reduce only the colliding switcher to a flag/code-sized control. 528 sw.classList.add( 'ts-collision-compact' ); 529 swRect = sw.getBoundingClientRect(); 530 if ( ! intersects( swRect, hit ) ) { continue; } 531 532 var shift = ( ( hit.left + hit.right ) / 2 ) >= ( ( swRect.left + swRect.right ) / 2 ) 533 ? -( swRect.right - hit.left + gap ) 534 : ( hit.right - swRect.left + gap ); 535 536 var minShift = 8 - swRect.left; 537 var maxShift = viewportWidth - 8 - swRect.right; 538 shift = Math.max( minShift, Math.min( maxShift, shift ) ); 539 540 if ( Math.abs( shift ) >= 1 ) { 541 sw.style.setProperty( '--ts-auto-shift-x', Math.round( shift ) + 'px' ); 542 sw.classList.add( 'ts-auto-shift' ); 543 } 544 } 545 } 546 547 function scheduleLayoutPass() { 548 if ( scheduled ) { return; } 549 scheduled = true; 550 window.requestAnimationFrame( function () { 551 scheduled = false;
552 mountHeaderSwitchers(); 553 alignClassicMenuSwitchers(); 554 resolveFloatingHeaderAvoidance(); 555 resolveMobileHeaderCollisions(); 556 var open = document.querySelectorAll( '.ts-dropdown.ts-open' ); 557 for ( var i = 0; i < open.length; i++ ) { positionPortalMenu( open[ i ] ); } 558 } ); 559 } 560 561 function observeDynamicHeaders() { 562 if ( ! window.MutationObserver || ! document.body ) { return; } 563 observer = new MutationObserver( function ( mutations ) { 564 var relevant = false; 565 for ( var i = 0; i < mutations.length; i++ ) { 566 for ( var j = 0; j < mutations[ i ].addedNodes.length; j++ ) { 567 var node = mutations[ i ].addedNodes[ j ]; 568 if ( ! node || node.nodeType !== 1 ) { continue; } 569 bindSwitchers( node ); 570 if ( ( node.matches && node.matches( '.ts-switcher, header, #masthead, .site-header' ) ) || ( node.querySelector && node.querySelector( '.ts-switcher' ) ) ) { relevant = true; } 571 } 572 } 573 if ( relevant || document.getElementById( 'ts-header-mount-template' ) ) { scheduleLayoutPass(); } 574 } ); 575 observer.observe( document.body, { childList: true, subtree: true } ); 576 } 577 578 function init() { 579 syncViewportMode(); 580 applyFlagFallback( document ); 581 markActive( document ); 582 bindInteractions(); 583 initFloatingDismiss(); 584 mountHeaderSwitchers(); 585 alignClassicMenuSwitchers(); 586 resolveFloatingHeaderAvoidance(); 587 resolveMobileHeaderCollisions(); 588 observeDynamicHeaders(); 589 590 window.addEventListener( 'resize', scheduleLayoutPass ); 591 window.addEventListener( 'orientationchange', scheduleLayoutPass ); 592 window.addEventListener( 'load', scheduleLayoutPass ); 593 window.addEventListener( 'scroll', scheduleLayoutPass, { passive: true } ); 594 } 595 596 if ( document.readyState === 'loading' ) { 597 document.addEventListener( 'DOMContentLoaded', init ); 598 } else { 599 init(); 600 } 601} )();
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.