1/** 2 * Campus IL â client analytics library. 3 * 4 * Public API: 5 * window.campusAnalytics.track(eventName, params) 6 * window.campusAnalytics.setConsent({ analytics_storage: 'granted', ... }) 7 * 8 * Contract: 9 * - Validates against window.campusAnalyticsSchema (rendered by PHP). 10 * - Pushes to window.dataLayer; never calls gtag('event', ...) directly. 11 * - Drops events that fail validation (logs to console.warn in dev). 12 * - Inert when schema is missing â pure no-op. 13 * - Optional sugar: any element with [data-track="event_name"] gets a 14 * delegated click listener; data-track-* attributes become parameters. 15 */ 16(function () { 17 'use strict'; 18 19 var DEV = (function () { 20 try { return /(^|\.)localhost$|^127\.|\.test$|next\.|staging\./.test(location.hostname); } 21 catch (e) { return false; } 22 }()); 23 24 function warn() { 25 if (!DEV || typeof console === 'undefined') return; 26 try { console.warn.apply(console, arguments); } catch (e) {} 27 } 28 29 function getSchema() { 30 var s = window.campusAnalyticsSchema; 31 return (s && typeof s === 'object') ? s : null; 32 } 33 34 function validate(eventName, params) { 35 var schema = getSchema(); 36 if (!schema) return null; 37 var entry = schema[eventName]; 38 if (!entry) return 'unknown event: ' + eventName; 39 var i; 40 for (i = 0; i < entry.required.length; i++) { 41 var req = entry.required[i]; 42 if (!Object.prototype.hasOwnProperty.call(params, req)) { 43 return "missing required parameter '" + req + "' for event '" + eventName + "'"; 44 } 45 } 46 var allowed = entry.required.concat(entry.optional); 47 for (var key in params) { 48 if (!Object.prototype.hasOwnProperty.call(params, key)) continue; 49 if (allowed.indexOf(key) === -1) { 50 return "unknown parameter '" + key + "' for event '" + eventName + "'"; 51 } 52 } 53 return null; 54 } 55 56 function track(eventName, params) { 57 params = params || {}; 58 var err = validate(eventName, params); 59 if (err) { warn('[campusAnalytics] ' + err); return false; } 60 window.dataLayer = window.dataLayer || []; 61 var payload = { event: eventName }; 62 for (var k in params) if (Object.prototype.hasOwnProperty.call(params, k)) payload[k] = params[k]; 63 window.dataLayer.push(payload); 64 return true; 65 } 66 67 function setConsent(state) { 68 if (!state || typeof state !== 'object') return false; 69 window.dataLayer = window.dataLayer || []; 70 function gtag() { window.dataLayer.push(arguments); } 71 gtag('consent', 'update', state); 72 return true; 73 } 74 75 // data-track sugar: <a data-track="footer_menu_click" data-track-link_text="About">⦠76 function paramsFromAttrs(el) { 77 var out = {}; 78 for (var i = 0; i < el.attributes.length; i++) { 79 var a = el.attributes[i]; 80 var m = /^data-track-(.+)$/.exec(a.name); 81 if (m) out[m[1]] = a.value; 82 } 83 return out; 84 } 85 86 function delegate(e) { 87 var el = e.target; 88 while (el && el.nodeType === 1 && el !== document.body) { 89 if (el.hasAttribute && el.hasAttribute('data-track')) { 90 var name = el.getAttribute('data-track'); 91 if (name) track(name, paramsFromAttrs(el)); 92 return; 93 } 94 el = el.parentNode; 95 } 96 } 97 98 if (document.addEventListener) { 99 document.addEventListener('click', delegate, true); 100 } 101 102 window.campusAnalytics = { 103 track: track, 104 setConsent: setConsent, 105 _validate: validate, 106 }; 107}());
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.