PageSourceSearch

https://www.grantthornton.co.uk/dist/javascript/GTBase.3eeb4a54e955c8caaf6edc703399c24e.js

js grantthornton.co.uk collected 2026-10-02 02:37:59 UTC 23,529 bytes, 727 lines download raw bytes

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.