PageSourceSearch

https://universidad.io/js/ga4-tag.js

js universidad.io collected 2026-09-24 15:03:55 UTC 2,844 bytes, 82 lines download raw bytes

1'use strict';
2/**
3 * ga4-tag.js — Centralized Google Analytics 4 loader for Universidad.io
4 *
5 * Handles:
6 *  1. Google Consent Mode v2 (default denied for analytics_storage)
7 *  2. gtag.js async load with measurement ID
8 *  3. SPA page_view tracking via router:navigate event
9 *  4. NO PII is ever sent to GA4 (no email, no name, no user id)
10 *
11 * Loaded in <head> of every page (SPA shell + public pages + EJS landings).
12 */
13(function () {
14  // Guard: only install once
15  if (window.__ga4TagInstalled) return;
16  window.__ga4TagInstalled = true;
17
18  var GA4_ID = window.__GA4_ID || 'G-HBMW9NXTZ0';
19
20  // ── 1. Consent Mode v2: default denied ─────────────────────────────
21  window.dataLayer = window.dataLayer || [];
22  function gtag() { window.dataLayer.push(arguments); }
23  window.gtag = gtag;
24
25  gtag('consent', 'default', {
26    analytics_storage: 'denied',
27    ad_storage:        'denied',
28    wait_for_update:   500
29  });
30
31  // ── 2. Load gtag.js async ──────────────────────────────────────────
32  var script = document.createElement('script');
33  script.async = true;
34  script.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA4_ID;
35  var firstScript = document.getElementsByTagName('script')[0];
36  if (firstScript && firstScript.parentNode) {
37    firstScript.parentNode.insertBefore(script, firstScript);
38  } else {
39    document.head.appendChild(script);
40  }
41
42  gtag('js', new Date());
43  gtag('config', GA4_ID, {
44    send_page_view: true,      // send initial page_view on load
45    anonymize_ip: true,
46    cookie_flags: 'SameSite=Lax;Secure'
47  });
48
49  // ── 3. SPA tracking: listen for router:navigate ────────────────────
50  //    The SPA router in /js/router.js fires this CustomEvent on every
51  //    navigation with detail.route = slug (e.g. 'dashboard', 'chat').
52  var _lastTrackedPath = location.pathname;
53
54  document.addEventListener('router:navigate', function (e) {
55    var route = (e.detail && e.detail.route) || '';
56    var path  = '/app/' + route;
57    // Avoid double-firing for the same path
58    if (path === _lastTrackedPath) return;
59    _lastTrackedPath = path;
60
61    gtag('event', 'page_view', {
62      page_path:     path,
63      page_location: location.origin + path,
64      page_title:    document.title
65    });
66  });
67
68  // ── 4. Consent helper: call this when user accepts analytics ───────
69  //    Usage: window.ga4GrantConsent()
70  window.ga4GrantConsent = function () {
71    gtag('consent', 'update', {
72      analytics_storage: 'granted'
73    });
74  };
75
76  // Deny (revoke) helper
77  window.ga4DenyConsent = function () {
78    gtag('consent', 'update', {
79      analytics_storage: 'denied'
80    });
81  };
82})();

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.