1 2// GT namespace 3var GT = GT || {}; 4 5GT.wHeight = 0, 6GT.wWidth = 0; 7GT.domContentLoadedFncList = []; 8GT.windowResizeFncList = {}; 9GT.touchMoveFncList = {}; 10GT.bodyWidth = 0; 11GT.disableDetectScrollUpOrDown = false; 12GT.stickyHeaderOffsetTop = 0; 13GT.triggerFnListAfterMainLogoLoaded = {}; 14GT.RecaptchaCallbackFnList = {}; 15GT.isScreenReader = false; 16GT.announceArea = null; 17GT.pathScript = "/dist/v2/js-v2/"; 18GT.pathImages = "/dist/images/"; 19 20GT.resolveAssetUrl = function (logicalPath) { 21 if (!logicalPath) { 22 return logicalPath; 23 } 24 var path = String(logicalPath).split("?")[0]; 25 if (window.__gtAssetMap && window.__gtAssetMap[path]) { 26 return window.__gtAssetMap[path]; 27 } 28 return logicalPath; 29}; 30 31GT.breakPoints = { 32 tablet: 768, 33 tablet_lg: 960, 34 desktop: 1024, 35 desktop_md: 1200, 36 desktop_lg: 1300 37} 38 39GT.isEventFromScreenReader = function(e) { 40 GT.isScreenReader = e.buttons || !e.pointerType; 41 return GT.isScreenReader; 42} 43 44GT.isSmallMobileView = function(){ 45 return GT.wWidth < GT.breakPoints.tablet; 46} 47GT.isMobileView = function(){ 48 return GT.wWidth < GT.breakPoints.tablet_lg; 49} 50GT.isSmallTabletView = function(){ 51 return GT.wWidth >= GT.breakPoints.tablet; 52} 53GT.isTabletView = function(){ 54 return GT.wWidth >= GT.breakPoints.tablet_lg; 55} 56GT.isDesktopView = function(){ 57 return GT.wWidth >= GT.breakPoints.desktop; 58} 59GT.isMediumDesktopView = function(){ 60 return GT.wWidth >= GT.breakPoints.desktop_md; 61} 62GT.isLargeDesktopView = function(){ 63 return GT.wWidth >= GT.breakPoints.desktop_lg; 64} 65 66GT.queryStringFocusableElm = 'a[href]:not([hide-on-desktop]):not([tabindex="-1"]), button:not([disabled]):not([hide-on-desktop]):not([tabindex="-1"]), input:not([disabled]):not([type="hidden"]):not([tabindex="-1"]), textarea:not([tabindex="-1"]), select:not([disabled]):not([tabindex="-1"]), details:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"]), iframe'; 67 68GT.isMobile = function(){return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)}; 69// GT.isMobile = function(){return typeof window.orientation !== 'undefined'}; 70GT.isIOS = function(){return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream}; 71GT.iOSversion = function() { 72 if (/iP(hone|od|ad)/.test(navigator.platform)) { 73 var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/); 74 return parseInt(v[1], 10); 75 } 76}; 77GT.isMac = function(){return /Mac/.test(navigator.userAgent) && !window.MSStream}; 78GT.isIE = function(){return /MSIE|Trident/.test(navigator.userAgent)}; 79GT.isEdge = function(){return /Edge/.test(navigator.userAgent)}; 80GT.isChrome = function(){return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)}; 81GT.isSafari = function(){return /safari/i.test(navigator.userAgent) && /Chrome/.test(navigator.userAgent) == false}; 82GT.isTouchDeviceFn = function(){ 83 return (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0); 84} 85 86GT.isMobileCached = GT.isMobile(); 87 88// Common function to execute a function only once within a 'wait' amount of time 89// The purpose of this is to improve performance during frequently triggered events such as scrolling or window resizing 90GT.debounce = function (func, wait, immediate) { 91 var timeout; 92 return function() { 93 var context = this, args = arguments; 94 var later = function() { 95 timeout = null; 96 if (!immediate) func.apply(context, args); 97 }; 98 var callNow = immediate && !timeout; 99 clearTimeout(timeout); 100 timeout = setTimeout(later, wait); 101 if (callNow) func.apply(context, args); 102 }; 103}; 104 105 106GT.throttleTimerList = []; 107GT.throttle = (func, delay = 150) => { 108 return function(...args) { 109 110 GT.throttleTimerList.forEach((timerItem) => { 111 clearTimeout(timerItem); 112 }) 113 114 let context = this; // store the context of the object that owns this function 115 let timer = setTimeout(function() { 116 func.apply(context,args) // execute the function with the context of the object that owns it 117 }, delay); 118 GT.throttleTimerList.push(timer) 119 } 120} 121 122GT.isFunction = function (functionToCheck) { 123 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]'; 124}; 125GT.isInteger = function (num) { 126 // an alternative to Number.isInteger due to lack of support in IE10, 11 127 // num must be a Number, not a string-number 128 return (num ^ 0) === num; 129}; 130// For scrollTop, $('body') works for IE and Firefox whereas $('html,body') works for Chrome and Safari 131GT.scrollTop = function(top) { 132 if (!top) { 133 return $('body').scrollTop() || $('html,body').scrollTop(); 134 } 135 136 var currentScrollTop = window.pageYOffset; 137 if (currentScrollTop === top) return; 138 $('body').scrollTop(top); 139 if (currentScrollTop === window.pageYOffset); 140 $('html,body').scrollTop(top); 141}; 142 143GT.processSearchKeywordBeforeSubmit = function(_searchKeyword, _userPreviousSearches, _MAX_PREVIOUS_SEARCH_ITEMS) { 144
145 if(!_searchKeyword) return; 146 _searchKeyword = _searchKeyword.replace(/(<([^>]+)>)/gi, ""); 147 if(_userPreviousSearches == null) _userPreviousSearches = []; 148 149 for(let i = 0; i < _userPreviousSearches.length; i++) { 150 let _previousSearch = _userPreviousSearches[i]; 151 if(_searchKeyword.trim().toLowerCase() == _previousSearch.trim().toLowerCase()) { 152 _userPreviousSearches.splice(i, 1); 153 break; 154 } 155 } 156 157 if(_userPreviousSearches.length >= _MAX_PREVIOUS_SEARCH_ITEMS) _userPreviousSearches.pop(); 158 159 _userPreviousSearches.unshift(_searchKeyword.trim().toLowerCase()); 160 161 localStorage.setItem('user_previous_searches', JSON.stringify(_userPreviousSearches)); 162} 163 164//Prevent horizontal scroll in IOS 165 166// //Prevent horizontal scroll in IOS 167// if(GT.isIOS()) { 168// function keepScrollX(){ 169// if(window.scrollX != 0) 170// window.scrollTo(0, window.scrollY); 171// } 172// // window.addEventListener('scroll', keepScrollX, {passive: true}); 173// // window.addEventListener('touchstart', keepScrollX, {passive: true}); 174// // window.addEventListener('touchmove', keepScrollX, {passive: true}); 175// window.addEventListener('touchend', keepScrollX, {passive: true}); 176// } 177 178// if(GT.isIOS()) { 179// let startTouchX, 180// startTouchY; 181// document.addEventListener('touchstart', function(e){ 182// startTouchX = e.touches[0].clientX; 183// startTouchY = e.touches[0].clientY; 184// }, {passive: true}); 185 186// document.addEventListener('touchmove', function(e){ 187// let endTouchX = e.changedTouches[0].clientX; 188// let endTouchY = e.changedTouches[0].clientY; 189// if(Math.abs(startTouchX - endTouchX) / Math.abs(startTouchY - endTouchY) >= 0.4) 190// e.preventDefault(); 191 192// // console.log(Math.abs(startTouchX - endTouchX) / Math.abs(startTouchY - endTouchY)); 193// }, {passive: false}); 194// } 195 196// if(GT.isIE()) { 197// let imgListIE = Array.prototype.slice.call(document.querySelectorAll(".banner-block--photograph .fixed-width__banner picture")); 198// imgListIE = imgListIE.concat(Array.prototype.slice.call(document.querySelectorAll(".event-detail__banner--v2 picture"))); 199// imgListIE.forEach(function(elm) { 200// let imgElm = elm.querySelector("img"); 201// if(!elm.parentElement.dataset.bgset) { 202// elm.parentElement.style.background = "url("+ imgElm.src +") no-repeat center center"; 203// elm.parentElement.style.backgroundSize = "cover"; 204// elm.remove(); 205// } 206// }); 207// } 208 209// if(GT.isIOS()) { 210// let startTouchX; 211// document.addEventListener('touchstart', function(e){ 212// startTouchX = e.touches[0].clientX; 213// }, {passive: true}); 214 215// document.addEventListener('touchmove', function(e){ 216// let endTouchX = e.changedTouches[0].clientX; 217// if (startTouchX !== endTouchX) { 218// window.scrollTo(0, window.scrollY); 219// } 220// }, {passive: false}); 221// } 222 223//Fix issue in Jquery: Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. 224jQuery.event.special.touchstart = { 225 setup: function( _, ns, handle ){ 226 this.addEventListener("touchstart", handle, { passive: true }); 227 } 228}; 229 230(function ($) { 231 $.fn.isBeforeElement = function (elem) { 232 if (typeof(elem) == "string") elem = $(elem); //just in case of bad input 233 return this.add(elem).index(elem) > 0; 234 } 235})(jQuery); 236 237 238GT.listboxKeyboardHandle = function(listbox) { 239 let list = document.querySelector(listbox); // targets the <ul> 240 let first = list.firstChild, // targets the first <li> 241 last = list.lastChild; // targets the first <li> 242 243 if(!list.dataset.initedKeyboardHandler) { 244 document.addEventListener("keydown", function(e){ 245 switch (e.keyCode) { 246 case 38: // if the UP key is pressed 247 if (document.activeElement == first){ break; } // stop the script if the focus is on the input or first element 248 else { document.activeElement.previousSibling.focus(); } // select the element before the current, and focus it 249 break; 250 case 40: // if the DOWN key is pressed 251 if (document.activeElement == last) { break; } // if the currently focused element is the main input -->
251 focus the first <li> 252 else { document.activeElement.nextSibling.focus(); } // target the currently focused element -> <a>, go up a node -> <li>, select the next node, go down a node and focus it 253 break; 254 } 255 }) 256 257 list.dataset.initedKeyboardHandler = true; 258 } 259} 260 261GT.waitForElmRendering = function(selector, timeout) { 262 return new Promise(function(resolve) { 263 if (document.querySelector(selector)) { 264 return resolve(document.querySelector(selector)); 265 } 266 267 if(!timeout) { 268 timeout = 15000; 269 } 270 271 if(timeout) { 272 setTimeout(function(){ 273 if(observer) 274 observer.disconnect(); 275 }, timeout) 276 } 277 278 let observer = new MutationObserver(function() { 279 if (document.querySelector(selector)) { 280 resolve(document.querySelector(selector)); 281 observer.disconnect(); 282 } 283 }); 284 285 observer.observe(document.body, { 286 childList: true, 287 subtree: true 288 }); 289 }); 290} 291 292 293GT.detectElementChange = function(selector, attributesLst = [], callback) { 294 const observer = new MutationObserver((mutationsList) => { 295 for (const mutation of mutationsList) { 296 if (mutation.type === 'attributes' && attributesLst.indexOf(mutation.attributeName) >= 0) { 297 callback(); 298 } 299 } 300 }); 301 302 observer.observe(selector, { 303 attributes: true, // Observe attribute changes 304 attributeFilter: attributesLst // Only watch the attributes inside attributesLst, array 305 }); 306} 307 308// GT.checkElmVisible = function (selector, callback, recheck, threshold, leaveCallback) { 309GT.checkElmVisible = function (selector, options) { 310 let _rootMargin = "0px"; 311 if(options.rootMargin) _rootMargin = options.rootMargin; 312 if(options.rootMarginWithoutHeader) _rootMargin = (((GT.isScrollingUp ? GT.headerElmHeight : GT.mainHeaderElmHeight) || 0) * -1 - 100) + "px 0px 0px 0px"; 313 314 const observer = new IntersectionObserver((entries) => { 315 for (const entry of entries) { 316 if (entry.isIntersecting) { 317 options.callback && options.callback(entry); 318 if(!options.recheck) 319 observer.unobserve(entry.target); 320 } else { 321 options.leaveCallback && options.recheck && options.leaveCallback(entry); 322 } 323 } 324 }, { 325 root: null, 326 rootMargin: _rootMargin, 327 threshold: options.threshold || 0.5, // set offset 0.1 means trigger if atleast 10% of element in viewport 328 }); 329 330 if(typeof selector == "object") 331 observer.observe(selector); 332 else 333 document.querySelectorAll(selector).forEach(function (item) { 334 observer.observe(item); 335 }) 336} 337 338GT.lockScrollBehindOverlay = function(queryStr){ 339 let _overlay = document.querySelector(queryStr), 340 _clientY = null; 341 342 if(_overlay.dataset.lockedScroll) 343 return; 344 345 _overlay.style.webkitOverflowScrolling = "touch"; 346 _overlay.dataset.lockedScroll = true; 347 348 _overlay.addEventListener('touchstart', function (event) { 349 if (event.targetTouches.length === 1) { 350 // detect single touch 351 _clientY = event.targetTouches[0].clientY; 352 } 353 }, false); 354 355 _overlay.addEventListener('touchmove', function (event) { 356 if (event.targetTouches.length === 1) { 357 // detect single touch 358 disableRubberBand(event); 359 } 360 }, false); 361 362 function disableRubberBand(event) { 363 let clientY = event.targetTouches[0].clientY - _clientY; 364 365 if (_overlay.scrollTop === 0 && clientY > 0) { 366 // element is at the top of its scroll 367 event.preventDefault(); 368 } 369 370 if (isOverlayTotallyScrolled() && clientY < 0) { 371 //element is at the top of its scroll 372 event.preventDefault(); 373 } 374 } 375 376 function isOverlayTotallyScrolled() { 377 // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 378 return _overlay.scrollHeight - _overlay.scrollTop <= _overlay.clientHeight; 379 } 380} 381 382//===========Trigger window.onscroll Evt 383GT.listFunctionsTriggerInScrollEvent = {}; 384GT.lastScrollTop = 0; 385GT.isScrollingUp = false; 386 387//waiting for animation is complete 388GT.preventHeaderFromFlickeringWhileBodyHeightIsChanging = function(time){ 389 GT.disableDetectScrollUpOrDown = true; 390 setTimeout(function(){ 391 GT.disableDetectScrollUpOrDown = false; 392 }, time ? time : 500) 393} 394 395GT.preventOpeningMenu = (timeout) => { 396 document.firstElementChild.classList.add("animating"); 397 setTimeout(() => { 398 document.firstElementChild.classList.remove("animating"); 399 }, timeout ? timeout : 600); 400} 401 402window.addEventListener('scroll', function(e) { 403 if(GT.isNav3 && !GT.disableDetectScrollUpOrDown) { 404 if(document.firstElementChild.classList.contains("global-reach--opening")) 405 return; 406 407 if(GT.headerElm && !GT.stickyHeaderOffsetTop) 408 GT.stickyHeaderOffsetTop = GT.headerElm.firstElementChild.offsetHeight - (GT.isMobileView() ? 64 : 88); 409 } 410 411 const st = window.pageYOffset || document.documentElement.scrollTop; 412 let isScrollDown = false; 413 414 if(!GT.disableDetectScrollUpOrDown) { 415 if(st + GT.wHeight < document.body.scrollHeight - 50) { 416 if (st > GT.lastScrollTop){ 417 // downscroll code 418 isScrollDown = true; 419 document.body.classList.remove("scroll-up"); 420 document.body.classList.add("scroll-down"); 421 GT.isScrollingUp = false; 422 423 } else { 424 // upscroll code 425 document.body.classList.remove("scroll-down"); 426 document.body.classList.add("scroll-up"); 427 GT.isScrollingUp = true; 428 } 429 } 430 431 if(st > 10) { 432 document.body.classList.add("scrolling"); 433 } else { 434 document.body.classList.remove("scrolling"); 435 } 436 437 if(st > GT.headerElmHeight) { 438 document.body.classList.add("sticky-header"); 439 } else { 440 document.body.classList.remove("sticky-header"); 441 } 442 443 444 GT.lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling 445 } 446 447 Object.keys(GT.listFunctionsTriggerInScrollEvent).forEach(function(k) { 448 GT.listFunctionsTriggerInScrollEvent[k] && GT.listFunctionsTriggerInScrollEvent[k](e, isScrollDown); 449 }) 450}, {passive: true}) 451 452 453CallbackGReCaptchaInited = function(){ 454 Object.keys(GT.RecaptchaCallbackFnList).forEach(function(key){ 455 let recaptchaItem = GT.RecaptchaCallbackFnList[key]; 456 if(recaptchaItem && !recaptchaItem.isExecuted) { 457 recaptchaItem.fnc(); 458 recaptchaItem.isExecuted = true; 459 } 460 }) 461} 462 463GT.observeElement = function(element, property, callback, delay
463= 0) { 464 let elementPrototype = Object.getPrototypeOf(element); 465 if (elementPrototype.hasOwnProperty(property)) { 466 let descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property); 467 Object.defineProperty(element, property, { 468 get: function() { 469 return descriptor.get.apply(this, arguments); 470 }, 471 set: function () { 472 let oldValue = this[property]; 473 descriptor.set.apply(this, arguments); 474 let newValue = this[property]; 475 if (typeof callback == "function") { 476 setTimeout(callback.bind(this, oldValue, newValue), delay); 477 } 478 return newValue; 479 } 480 }); 481 } 482} 483 484GT.canFocus = function (element) { 485 return element.closest(GT.queryStringFocusableElm); 486} 487 488GT.getListFocusableElms = function (element) { 489 let arrElms = element.querySelectorAll(GT.queryStringFocusableElm), 490 outputLst = []; 491 492 for (const element of arrElms) { 493 if (GT.isNotHidden(element)) { 494 outputLst.push(element); 495 } 496 } 497 return outputLst; 498} 499 500 501GT.getElementOffset = function(element){ 502 let offsetLeft = 0; 503 let offsetTop = 0; 504 505 while (element) 506 { 507 offsetLeft += element.offsetLeft; 508 offsetTop += element.offsetTop; 509 510 element = element.offsetParent; 511 } 512 513 return { 514 top: offsetTop, 515 left: offsetLeft 516 }; 517} 518 519// Avoid Reflow / Performance issue. Instead of using the innerWidth/offsetWidth, use this one 520// return {x, y, top, left, bottom, height, width} 521GT.getElementBounds = (elm) => { 522 return new Promise((resolveInner) => { 523 const observer = new IntersectionObserver((entries) => { 524 for (const entry of entries) { 525 resolveInner(entry.boundingClientRect); 526 } 527 // Disconnect the observer to stop from running in the background 528 observer.disconnect(); 529 }); 530 observer.observe(elm); 531 }) 532} 533 534GT.firstFocusableElmKeyHandler = function(e) { 535 if(e.key == "Tab" && !e.shiftKey) { 536 e.preventDefault(); 537 GT.firstFocusableElm.focus(); 538 } 539} 540 541GT.lastFocusableElmKeyHandler = function(e) { 542 if(e.key == "Tab" && e.shiftKey) { 543 e.preventDefault(); 544 GT.lastFocusableElm.focus(); 545 } 546} 547 548GT.unloopFocus = function(listFocusableElms) { 549 listFocusableElms.forEach((item, indx) => { 550 item.removeEventListener("keydown", GT.firstFocusableElmKeyHandler); 551 item.removeEventListener("keydown", GT.lastFocusableElmKeyHandler); 552 }) 553} 554 555GT.loopFocus = function(targetPanel, listFocusableElms, onlyLoopFocus) { 556 let tmpListFocusableElms = listFocusableElms ? listFocusableElms : GT.getListFocusableElms(targetPanel), 557 tmpListFocusableElmsLength = tmpListFocusableElms.length; 558 559 if(tmpListFocusableElmsLength) { 560 tmpListFocusableElms.forEach((item, indx) => { 561 tmpListFocusableElms[indx]?.removeEventListener("keydown", GT.firstFocusableElmKeyHandler); 562 tmpListFocusableElms[indx]?.removeEventListener("keydown", GT.lastFocusableElmKeyHandler); 563 }) 564 565 GT.firstFocusableElm = tmpListFocusableElms[0], 566 GT.lastFocusableElm = tmpListFocusableElmsLength > 1 ? tmpListFocusableElms[tmpListFocusableElmsLength - 1] : GT.firstFocusableElm; 567 568 if(!onlyLoopFocus) 569 GT.firstFocusableElm.focus(); 570 571 if(GT.firstFocusableElm) { 572 GT.lastFocusableElm.addEventListener("keydown", GT.firstFocusableElmKeyHandler); 573 GT.firstFocusableElm.addEventListener("keydown", GT.lastFocusableElmKeyHandler); 574 } 575 576 return { 577 firstFocusableElm: GT.firstFocusableElm, 578 lastFocusableElm: GT.lastFocusableElm 579 } 580 } 581} 582 583GT.loopFocusByArrowKey = function(listFocusableElms) { 584 const lstLength = listFocusableElms.length; 585 if(!lstLength) return; 586 587 function handleArrowKey(e) { 588 if(e.key == "ArrowDown" || e.key == "ArrowUp") { 589 let nextIndex = parseInt(e.target.dataset.idx) + (e.key == "ArrowDown" ? 1 : -1); 590 if(nextIndex < 0) nextIndex = lstLength - 1; 591 if(nextIndex >= lstLength) nextIndex = 0; 592 const currentItem = listFocusableElms[nextIndex]; 593 currentItem.focus(); 594 } 595 } 596
597 listFocusableElms.forEach((item, indx) => { 598 item.dataset.idx = indx; 599 item.removeEventListener("keydown", handleArrowKey); 600 item.addEventListener("keydown", handleArrowKey); 601 }); 602} 603 604GT.pageAccessedByReload = () => { 605 return ( 606 (window.performance.navigation && window.performance.navigation.type === 1) || 607 window.performance 608 .getEntriesByType('navigation') 609 .map((nav) => nav.type) 610 .includes('reload') 611)}; 612 613GT.domContentLoadedFncList.push(() => { 614 window.addEventListener("keydown", e => { 615 if(e.key == "Tab") { 616 GT.isScreenReader = true; 617 } 618 }) 619 620 if(GT.isMobileView()) 621 GT.isScreenReader = true; 622 623 //This solution cannot resolve the case: cursor in the middle page after loading page completely 624 // document.querySelector(".desktop-logo")?.addEventListener("focus", () => { 625 // GT.isScreenReader = true; 626 // alert("OK"); 627 // }) 628 629 //isReload 630 if(GT.pageAccessedByReload()) { 631 const lastScrollTop = window.scrollY; 632 GT.lastScrollTop = lastScrollTop; 633 if(lastScrollTop >= 10) document.body.classList.add("scrolling"); 634 if(lastScrollTop > 150) document.body.classList.add("sticky-header"); 635 console.log("Access by reload function"); 636 } 637 638 document.querySelectorAll('a[href*="#"]').forEach(anchorLinkItem => { 639 anchorLinkItem.addEventListener("click", function(e) { 640 GT.preventHeaderFromFlickeringWhileBodyHeightIsChanging(1000); 641 }) 642 }) 643 644 document.querySelectorAll('[media="none"]').forEach(item => { 645 item.setAttribute("media", "all"); 646 }) 647}) 648 649GT.clickEvt = (elm, callback) => { 650 ["click", "keydown"].forEach(evt => { 651 elm.addEventListener(evt, (e) => { 652 if(e.type == "click" || e.key == "Enter") { 653 callback.call(e.target || e.currentTarget, e); 654 } 655 }) 656 }) 657} 658 659GT.isNotHidden = (element, forceInMainPage) => { 660 //Element.checkVisibility() => still not support Safari 661 //https://caniuse.com/mdn-api_element_checkvisibility 662 let isVisible; 663 if(element.checkVisibility) 664 isVisible = element.checkVisibility({checkOpacity: true, checkVisibilityCSS: true}) && element.offsetWidth > 0 && element.offsetHeight > 0; 665 else { 666 const style = window.getComputedStyle(element); 667 isVisible = style.visibility !== 'hidden' && style.display !== 'none' && style.opacity != 0 && element.offsetWidth > 0 && element.offsetHeight > 0; 668 } 669 670 let isInMainPage = true; 671 if(forceInMainPage) { 672 const computed = element.getBoundingClientRect(); 673 if(computed.left < 0 || computed.right < 0) 674 isInMainPage = false; 675 } 676 677 return isVisible && isInMainPage; 678} 679 680GT.getFirstFocusableVisible = (container) => { 681 function getFirstFocusableVisibleElement() { 682 const focusableElements = (container || document.querySelector(".gt-wrapper")).querySelectorAll(GT.queryStringFocusableElm); 683 for (const element of focusableElements) { 684 if (GT.isNotHidden(element, true)) { 685 return element; 686 } 687 } 688 return null; 689 } 690 691 return getFirstFocusableVisibleElement(); 692} 693 694GT.setInertWhileOpeningDialog = function(isLock) { 695 let wrapperElm = document.querySelector(".gt-wrapper"); 696 let shortcutElm = document.querySelector(".shortcuts"); 697 if(isLock) { 698 GT.headerElm?.setAttribute("inert", true); 699 wrapperElm?.setAttribute("inert", true); 700 shortcutElm?.setAttribute("inert", true); 701 } else { 702 GT.headerElm?.removeAttribute("inert"); 703 wrapperElm?.removeAttribute("inert"); 704 shortcutElm?.removeAttribute("inert"); 705 } 706} 707 708GT.loadScript = async function(url) { 709 const script = document.createElement('script'); 710 script.src = GT.resolveAssetUrl(url); 711 script.defer = true; 712 document.head.appendChild(script); 713 return new Promise((resolve, reject) => { 714 script.onload = () => { 715 resolve(); 716 }; 717 script.onerror = () => { 718 reject(); 719 }; 720 }); 721} 722 723GT.generateHexCode = (str) => { 724 return Array.from(str) 725 .map(c => c.charCodeAt(0).toString(16).padStart(2, '0')) 726 .join(''); 727}
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.