1/** 2 * Loads the third-party tags after the page is usable, instead of during first paint. 3 * 4 * Analytics, live chat and reCAPTCHA together pulled well over half a megabyte and 5 * several seconds of main-thread work while the browser was still trying to render 6 * the hero. None of the three is needed before the page is interactive, so each now 7 * waits for its own trigger. Every feature still works â the chat bubble appears, the 8 * captcha renders before anyone reaches it, pageviews are recorded â just after the 9 * content has painted rather than instead of it. 10 * 11 * Load with `defer` and a data-chat attribute on pages that carry the chat widget. 12 */ 13(function () { 14 'use strict'; 15 16 var GA_ID = 'G-SMLWT8Q0R4'; 17 var self = document.currentScript; 18 19 function inject(src, extra) { 20 var s = document.createElement('script'); 21 s.src = src; 22 s.async = true; 23 for (var k in extra || {}) s.setAttribute(k, extra[k]); 24 document.head.appendChild(s); 25 return s; 26 } 27 28 // Idle time after load, with a timeout so a permanently busy page still fires. 29 function whenIdle(fn, timeout) { 30 function schedule() { 31 if (window.requestIdleCallback) requestIdleCallback(fn, { timeout: timeout }); 32 else setTimeout(fn, Math.min(timeout, 1500)); 33 } 34 if (document.readyState === 'complete') schedule(); 35 else addEventListener('load', schedule, { once: true }); 36 } 37 38 // Runs fn once: on the first sign of a real visitor, or after `delay` ms of the 39 // page being loaded and idle, whichever lands first. 40 function afterInteractionOrDelay(fn, delay) { 41 var events = ['pointerdown', 'keydown', 'touchstart', 'scroll']; 42 var done = false; 43 44 function go() { 45 if (done) return; 46 done = true; 47 events.forEach(function (e) { removeEventListener(e, go); }); 48 fn(); 49 } 50 51 events.forEach(function (e) { addEventListener(e, go, { passive: true }); }); 52 whenIdle(function () { setTimeout(go, delay); }, 2000); 53 return go; 54 } 55 56 /* Analytics. gtag.js costs ~400ms of main-thread work on a mid-range phone, all 57 of it inside the window that decides whether the page feels responsive, and 58 none of it useful to the visitor. So it waits for the first sign of a real 59 visitor, or five seconds, whichever comes first â a reader who never touches 60 the page is still counted, just late. 61 62 The queue is primed before the tag loads, so events fired in the meantime are 63 replayed rather than lost. Sessions that end inside those five seconds with no 64 interaction at all are the one thing this gives up; moving the tag server-side 65 (Cloudflare Zaraz) is the way to get those back without the main-thread cost. */ 66 window.dataLayer = window.dataLayer || []; 67 window.gtag = function () { window.dataLayer.push(arguments); }; 68 gtag('js', new Date()); 69 gtag('config', GA_ID); 70 afterInteractionOrDelay(function () { 71 inject('https://www.googletagmanager.com/gtag/js?id=' + GA_ID); 72 }, 5000); 73 74 /* reCAPTCHA â only on pages with a widget to fill, and only once that widget is 75 within a screen of the viewport, so it is rendered before anyone reaches it. */ 76 var box = document.querySelector('.g-recaptcha'); 77 if (box) { 78 var load = function () { inject('https://www.google.com/recaptcha/api.js'); }; 79 if ('IntersectionObserver' in window) { 80 var io = new IntersectionObserver(function (entries) { 81 if (entries[0].isIntersecting) { io.disconnect(); load(); } 82 }, { rootMargin: '100% 0px' }); 83 io.observe(box); 84 } else { 85 whenIdle(load, 5000); 86 } 87 } 88 89 /* Live chat â the heaviest tag by far, and the least urgent. */ 90 var chat = self && self.dataset.chat; 91 if (chat) { 92 afterInteractionOrDelay(function () { 93 window.Tawk_API = window.Tawk_API || {}; 94 window.Tawk_LoadStart = new Date(); 95 inject('https://embed.tawk.to/' + chat, { charset: 'UTF-8',
95crossorigin: '*' }); 96 }, 8000); 97 } 98})();
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.