PageSourceSearch

https://coinocular.net/third-party.js

js coinocular.net collected 2026-09-24 15:06:27 UTC 4,207 bytes, 98 lines download raw bytes

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.