1'use strict'; 2 3/** 4 * The script is designed to collect interdependent scripts 5 * that need to be executed in a specific order. Scripts in the queue are executed strictly one after another 6 * to avoid conflicts between them in case elements are added to the queue during queue processing. 7 * Queues are classified by a key, and operations are performed exclusively by a key. 8 * 9 * The bus() script should be used in cases 10 * when initialization of certain functionality takes some time, 11 * during which dependent scripts manage to load and start. 12 * 13 * Example reason for using the bus() script: 14 * When initializing gtag() you need to pass user ID to the configuration, 15 * however, this ID needs to be loaded via URL call for certain reasons. 16 * In this case any gtag events must wait for initialization before being called. 17 * 18 * The examples of a script using: 19 * 20 * bus('key').register(fn, <priority>); 21 * bus('key').dispatch(value); 22 * 23 */ 24 25/* If the file is included again - reuse the existing bus and its registry. */ 26/* eslint-disable no-var */ 27 28var bus; 29 30/** 31 * Single registry for all queues: 32 * Map<key, { 33 * buckets: Map<priority, Set<fn>>, 34 * pending: Array<{priority, fn, gen}>, 35 * processing: boolean, 36 * currentGen: number, 37 * dispatched: boolean, 38 * value: any, 39 * chain: Promise<void>, 40 * drainingPromise?: Promise<void> | null 41 * }> 42 */ 43var __BUS_REG__ = (bus && bus.__reg__) ? bus.__reg__ : new Map(); 44 45/* ---------- helpers ---------- */ 46function ensureRecord(key) { 47 var rec = __BUS_REG__.get(key); 48 if (!rec) { 49 rec = { 50 buckets: new Map(), 51 pending: [], 52 processing: false, 53 currentGen: 0, 54 dispatched: false, 55 value: undefined, 56 chain: Promise.resolve(), 57 drainingPromise: null, 58 }; 59 __BUS_REG__.set(key, rec); 60 } 61 return rec; 62} 63 64function nextPriority(rec) { 65 var max = -1; 66 rec.buckets.forEach(function (_set, p) { 67 if (p > max) max = p; 68 }); 69 return max + 1; 70} 71 72function enqueue(rec, fn, priority, gen) { 73 rec.pending.push({priority: priority, fn: fn, gen: gen}); 74} 75 76async function drain(key) { 77 var rec = ensureRecord(key); 78 if (rec.processing) return rec.drainingPromise; 79 80 rec.processing = true; 81 rec.drainingPromise = (async function () { 82 try { 83 var genToProcess = rec.currentGen; 84 85 // Process by "generations": everything that was at the moment of snapshot, 86 // then what was registered during execution (gen+1), and so on. 87 /* eslint-disable no-constant-condition */ 88 while (true) { 89 var batch = []; 90 for (var i = rec.pending.length - 1; i >= 0; i--) { 91 var t = rec.pending[i]; 92 if (t.gen === genToProcess) { 93 batch.push(t); 94 rec.pending.splice(i, 1); 95 } 96 } 97 if (batch.length === 0) break; 98 99 batch.sort(function (a, b) { 100 return a.priority - b.priority; 101 }); 102 103 for (var j = 0; j < batch.length; j++) { 104 (function(task){ 105 rec.chain = rec.chain 106 .then(function () { 107 return task.fn(rec.value); 108 }) 109 .catch(function (e) { 110 console.error('BUS: handler error (key', key, ', priority', task.priority, '):', e); 111 }); 112 })(batch[j]); 113 } 114 115 await rec.chain; 116 117 genToProcess++; 118 } 119 120 rec.currentGen = genToProcess; 121 } finally { 122 rec.processing = false; 123 rec.drainingPromise = null; 124 } 125 })(); 126 127 return rec.drainingPromise; 128} 129 130/* ---------- API for specific key ---------- */ 131function register(key, fn, priority) { 132 if (typeof fn !== 'function') throw new TypeError('bus("' + key + '").register(fn): fn must be a function'); 133 var rec = ensureRecord(key); 134 135 var p = (typeof priority === 'number') ? priority : nextPriority(rec); 136 137 var set = rec.buckets.get(p); 138 if (!set) { 139 set = new Set(); 140 rec.buckets.set(p, set); 141 } 142 set.add(fn); 143 144 // If dispatch has already started/is in progress - put in queue. 145 if (rec.dispatched) { 146 var gen = rec.processing ? rec.currentGen + 1 : rec.currentGen; 147 enqueue(rec, fn, p, gen); 148 if (!rec.processing) { 149 void drain(key); 150 } 151 } 152 153 // Return unsubscribe function. 154 return function unsubscribe() { 155 set.delete(fn); 156 }; 157} 158 159async function dispatch(key, value) { 160 var rec = ensureRecord(key); 161 rec.dispatched = true; 162 rec.value = value; 163 164 // Snapshot of current handlers as the initial batch.
165 rec.buckets.forEach(function (set, p) { 166 set.forEach(function (fn) { 167 enqueue(rec, fn, p, rec.currentGen); 168 }); 169 }); 170 171 return drain(key); 172} 173 174/* ---------- Global function without wrappers ---------- */ 175if (typeof bus !== 'function') { 176 bus = function (key) { 177 if (!key) throw new TypeError('bus(key): key is required'); 178 return { 179 register: function (fn, priority) { 180 return register(key, fn, priority); 181 }, 182 dispatch: function (value) { 183 return dispatch(key, value); 184 } 185 }; 186 }; 187} 188 189/* Keep a single registry between possible multiple includes. */ 190bus.__reg__ = __BUS_REG__; 191window.bus = bus; 192 193// Push a named event to the GTM dataLayer. GTM Custom Event triggers match on the 194// `event` key, so all analytics events use this shape (the old gtag('event',â¦) 195// arguments form has no consumer now that GA loads inside GTM). 196function giPushEvent(name, params) { 197 window.dataLayer = window.dataLayer || []; 198 window.dataLayer.push(Object.assign({event: name}, params || {})); 199} 200window.giPushEvent = giPushEvent; 201 202/** 203 * Runs `callback` only for a visitor the persistent-login check says is signed in. 204 * 205 * Per-pageview beacons that only mean something for an authenticated visitor 206 * (Sentry user context, the post-login GTM auth event) used to fire on every 207 * page, uncacheable, for the anonymous majority too. Every frontend page 208 * already calls the uncacheable /async-load/persistent-login endpoint and 209 * dispatches its JSON on bus('gi_persistent_login'); that answer is the only 210 * trustworthy one. drupalSettings.user.uid is baked into CDN-cacheable HTML and 211 * must not be used for this. 212 * 213 * Ordering: the bus stores the dispatched value, so a caller registering after 214 * the endpoint has already answered is served that stored value. 215 * 216 * Timeout: if no answer arrives within `timeoutMs` (the request failed, which 217 * dispatches nothing, or is very slow), the callback runs anyway -- fail open. 218 * Unknown means "maybe authenticated"; skipping would silently drop a signed-in 219 * visitor's analytics event and Sentry identity, while running costs one 220 * request, i.e. exactly the pre-gating behaviour. A late answer after the 221 * timeout is ignored so the callback never runs twice. 222 * 223 * @param {Function} callback 224 * Invoked at most once, with no arguments. 225 * @param {number} [timeoutMs] 226 * How long to wait for the persistent-login answer. Defaults to 5000. 227 */ 228function giWhenAuthenticated(callback, timeoutMs) { 229 var limit = (typeof timeoutMs === 'number') ? timeoutMs : 5000; 230 var settled = false; 231 var timer = null; 232 var unsubscribe = null; 233 234 function settle(run) { 235 if (settled) return; 236 settled = true; 237 if (timer !== null) clearTimeout(timer); 238 if (unsubscribe) unsubscribe(); 239 if (!run) return; 240 try { 241 callback(); 242 } 243 catch (e) { 244 console.error('giWhenAuthenticated: callback error:', e); 245 } 246 } 247 248 timer = setTimeout(function () { 249 settle(true); 250 }, limit); 251 252 // Registered handlers run from a promise chain, never synchronously, so 253 // `unsubscribe` is assigned before the handler can call settle(). 254 unsubscribe = bus('gi_persistent_login').register(function (data) { 255 settle(Boolean(data && data.authenticated)); 256 }); 257} 258window.giWhenAuthenticated = giWhenAuthenticated;
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.