1/* âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2 INSTANT CALLBACK REQUEST (W-041) â progressive-enhancement behavior. 3 4 The <form> is server-rendered but JS-ENHANCED: recording the lead needs the 5 async submit to the REST route, so a JS-off visitor cannot submit â they get 6 a <noscript> direct-call fallback to the business phone instead (a callback 7 widget's honest no-JS path). callback.js adds the conversion layer â masked 8 input, inline validation, PARTIAL-lead capture, an async submit with a busy 9 CTA + AbortController timeout, and the success swap â all guarded so a load 10 failure degrades to the same direct-call fallback. 11 12 SETTINGS CONTRACT â data-attributes on the .hzo-callback root (written by the 13 [hzo_callback] shortcode or by hand): 14 data-hzo-callback-id opaque instance id (server resolves the 15 webhook/SMS/form destination from it â 16 creds never reach the client) 17 data-hzo-callback-nonce REST nonce (action hzo_callback_lead) 18 data-hzo-callback-submit form | webhook | sms 19 data-hzo-callback-success countdown | thankyou | tel 20 data-hzo-callback-partial "true"|"false" â partial-lead capture 21 data-hzo-callback-source campaign tag (UTM auto-merged) 22 data-hzo-callback-response-window "5 min" â parsed to countdown seconds 23 data-hzo-callback-countdown int seconds | "off" (overrides the parse) 24 data-hzo-callback-redirect URL â navigate on success (optional) 25 data-hzo-callback-promise / -after-promise copy-swap text for hourschange 26 data-hzo-callback-cta / -after-cta CTA-label swap text 27 data-hzo-callback-success-title / -text success copy ({phone}{name} 28 {response-window} tokens) 29 30 REUSE (guarded, never a hard dep): 31 ⢠hzo.hours (W-002) + `hzo:hourschange` â the ONE hours signal. On flip we 32 swap promise/cta TEXT only; we never re-parse hours. 33 ⢠hzo.announce (W-021) â success/error strings are announced from the 34 persistent live region OUTSIDE the swapped form subtree (G30). 35 36 EVENT â emits `hzo:callback { status:'partial'|'complete', phone }` (bubbles 37 from .hzo-callback). Also mirrors to dataLayer (generate_lead / phone_call). 38 39 Idempotent (guarded by data-hzo-callback-ready); reduced-motion safe (the ring 40 stops to static remaining-time text; the spinner slows, never freezes â CSS). 41 IIFE, no build, no jQuery. Public surface: window.hzo.callback = { init, setup }. 42 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 43(function () { 44 'use strict'; 45 46 window.hzo = window.hzo || {}; 47 48 var REST_URL = '/wp-json/hozio-studio/v1/callback-lead'; 49 50 /* ââ i18n helpers (guarded; fall back to the literal) ââ */ 51 function __(s) { 52 return (window.wp && wp.i18n && wp.i18n.__) ? wp.i18n.__(s, 'hozio-studio') : s; 53 } 54 function sprintf() { 55 var args = arguments, s = String(args[0]); 56 if (window.wp && wp.i18n && wp.i18n.sprintf) { return wp.i18n.sprintf.apply(null, args); } 57 var i = 1; 58 return s.replace(/%(?:\d+\$)?[sd]/g, function () { return args[i++]; }); 59 } 60 function announce(msg, level) { 61 if (window.hzo && typeof window.hzo.announce === 'function') { 62 window.hzo.announce(msg, level || 'polite'); 63 } 64 } 65 66 /* ââ Digits / phone helpers (US 10-digit; strips a leading country 1) ââ */ 67 function digitsOf(v) { 68 var d = String(v == null ? '' : v).replace(/\D/g, ''); 69 if (d.length === 11 && d.charAt(0) === '1') { d = d.slice(1); } 70 return d; 71 } 72 function isValidPhone(v) { return digitsOf(v).length === 10; } 73
74 /* ââ (000) 000-0000 mask applied live to the tel field ââ */ 75 function maskPhone(raw) { 76 var d = String(raw || '').replace(/\D/g, ''); 77 if (d.length === 11 && d.charAt(0) === '1') { d = d.slice(1); } 78 d = d.slice(0, 10); 79 var out = ''; 80 if (d.length > 0) { out = '(' + d.slice(0, 3); } 81 if (d.length >= 4) { out += ') ' + d.slice(3, 6); } 82 if (d.length >= 7) { out += '-' + d.slice(6, 10); } 83 return out; 84 } 85 86 /* ââ "5 min" / "2 minutes" / "30 sec" / "90" â seconds (locale-agnostic) ââ */ 87 function parseSeconds(text) { 88 if (text == null) { return 0; } 89 var s = String(text).toLowerCase().replace(',', '.'); 90 var m = s.match(/(\d+(?:\.\d+)?)/); 91 if (!m) { return 0; } 92 var n = parseFloat(m[1]); 93 if (isNaN(n)) { return 0; } 94 if (/hour|hr/.test(s)) { return Math.round(n * 3600); } 95 if (/\bsec|\bs\b/.test(s)) { return Math.round(n); } 96 // default unit is minutes ("5 min", bare "5") 97 return Math.round(n * 60); 98 } 99 100 /* ââ Read the campaign source + merge UTM params ââ */ 101 function resolveSource(root) { 102 var src = root.getAttribute('data-hzo-callback-source') || ''; 103 try { 104 var q = new URLSearchParams(window.location.search); 105 var utm = []; 106 ['utm_source', 'utm_medium', 'utm_campaign'].forEach(function (k) { 107 var v = q.get(k); 108 if (v) { utm.push(k + '=' + v); } 109 }); 110 if (utm.length) { src = (src ? src + '|' : '') + utm.join('&'); } 111 } catch (e) { /* URLSearchParams absent â source stays as authored */ } 112 return src; 113 } 114 115 /* ââ dataLayer mirror (measurement adapter) ââ */ 116 function pushDataLayer(obj) { 117 window.dataLayer = window.dataLayer || []; 118 window.dataLayer.push(obj); 119 } 120 121 /* ââ Emit the public hzo:callback event ââ */ 122 function emit(root, status, phone) { 123 var detail = { status: status, phone: phone }; 124 var ev; 125 try { ev = new CustomEvent('hzo:callback', { bubbles: true, detail: detail }); } 126 catch (e) { ev = document.createEvent('CustomEvent'); ev.initCustomEvent('hzo:callback', true, false, detail); } 127 root.dispatchEvent(ev); 128 if (status === 'complete') { 129 pushDataLayer({ event: 'generate_lead', lead_status: 'complete', source: 'callback' }); 130 } else if (status === 'partial') { 131 pushDataLayer({ event: 'generate_lead', lead_status: 'partial', source: 'callback' }); 132 } 133 } 134 135 /* ââ Build the lead payload the REST route expects ââ */ 136 function payloadFor(root, status) { 137 var phoneEl = root.querySelector('input[name="phone"]'); 138 var nameEl = root.querySelector('input[name="name"]'); 139 var winEl = root.querySelector('[name="window"]:checked, input[type="hidden"][name="window"]'); 140 // Carry the TCPA consent state to the server so the recorded lead holds 141 // evidence of consent (and the server can enforce consent-required). 142 var consentEl = root.querySelector('input[name="consent"]'); 143 return { 144 id: root.getAttribute('data-hzo-callback-id') || '', 145 nonce: root.getAttribute('data-hzo-callback-nonce') || '', 146 phone: digitsOf(phoneEl ? phoneEl.value : ''), 147 name: nameEl ? nameEl.value : '', 148 window: winEl ? winEl.value : '', 149 consent: (consentEl && consentEl.checked) ? '1' : '', 150 source: resolveSource(root), 151 status: status, 152 token: root.__hzoToken, 153 ts: Date.now() 154 }; 155 } 156 157 /* ââ Fire a PARTIAL lead. Uses sendBeacon on pagehide (fire-and-forget), a 158 normal fetch on blur. De-duped client-side by the last number sent + 159 server-side by token. ââ */ 160 function sendPartial(root, useBeacon) { 161 if (root.getAttribute('data-hzo-callback-partial') === 'false') { return; } 162 var phoneEl = root.querySelector('input[name="phone"]'); 163 if (!phoneEl || !isValidPhone(phoneEl.value)) { return; } 164 var digits = digitsOf(phoneEl.value); 165 // Only once per CHANGED valid number (blur path); pagehide always allowed 166 // to flush a final unsubmitted state. 167 if (!useBeacon && root.__hzoPartialSent === digits) { return; } 168 root.__hzoPartialSent = digits; 169 170 var body = JSON.stringify(payloadFor(root, 'partial')); 171 172 if (useBeacon && navigator.sendBeacon) { 173 try { 174 var blob = new Blob([body], { type: 'application/json' }); 175 navigator.sendBeacon(REST_URL, blob); 176 } catch (e) { /* best-effort */ } 177 } else if (window.fetch) { 178 try { 179 fetch(REST_URL, { 180 method: 'POST', 181 headers: { 'Content-Type': 'application/json' }, 182 body: body, 183 keepalive: true, 184 credentials: 'same-origin' 185 })['catch'](function () { /* partial is best-effort; superseded by complete/pagehide */ }); 186 } catch (e) { /* ignore */ } 187 } 188 emit(root, 'partial', digits); 189 } 190 191 /* ââ Validation UI ââ */ 192 function showError(root, msg) { 193 var phoneEl = root.querySelector('input[name="phone"]'); 194 var errEl = root.querySelector('.hzo-callback__error'); 195 if (phoneEl) { 196 phoneEl.setAttribute('aria-invalid', 'true'); 197 phoneEl.classList.add('hzo-callback__input--error'); 198 } 199 if (errEl) { errEl.textContent = msg; errEl.hidden = false; } 200 announce(msg, 'assertive'); 201 } 202 function clearError(root) { 203 var phoneEl = root.querySelector('input[name="phone"]'); 204 var errEl = root.querySelector('.hzo-callback__error'); 205 if (phoneEl) { phoneEl.removeAttribute('aria-invalid'); phoneEl.classList.remove('hzo-callback__input--error'); } 206 if (errEl) { errEl.hidden = true; errEl.textContent = ''; } 207 } 208 209 /* ââ Loading lock (never `disabled` â aria-busy + a JS re-entrancy flag so 210 focus is never stranded, G22 / PRESERVE-2) ââ */ 211 function setLoading(root, on) { 212 var btn = root.querySelector('.hzo-callback__submit'); 213 root.__hzoBusy = !!on; 214 if (!btn) { return; } 215 if (on) { btn.classList.add('is-loading'); btn.setAttribute('aria-busy', 'true'); } 216 else { btn.classList.remove('is-loading'); btn.removeAttribute('aria-busy'); } 217 } 218 219 /* ââ Success swap: replace the form body with the role=status region, 220 move focus to its heading, announce from the persistent region. ââ */ 221 function showSuccess(root) { 222 var mode = root.getAttribute('data-hzo-callback-success') || 'countdown'; 223 var region = root.querySelector('[data-hzo-callback-success-region]'); 224 if (!region) { return; } 225 226 var phoneEl = root.querySelector('input[name="phone"]'); 227 var nameEl = root.querySelector('input[name="name"]');
228 var phoneDisp = maskPhone(phoneEl ? phoneEl.value : ''); 229 var name = nameEl ? String(nameEl.value || '').trim() : ''; 230 var respWindow = root.getAttribute('data-hzo-callback-response-window') || ''; 231 232 function interp(t) { 233 return String(t || '') 234 .replace(/\{phone\}/g, phoneDisp) 235 .replace(/\{name\}/g, name) 236 .replace(/\{response-window\}/g, respWindow); 237 } 238 239 var headingId = region.getAttribute('data-hzo-callback-heading-id') || ''; 240 var title = interp(root.getAttribute('data-hzo-callback-success-title') || '') || 241 (name ? sprintf(__('Thanks, %s â youâre on the list.'), name) : __('Thanks â youâre on the list.')); 242 var text = interp(root.getAttribute('data-hzo-callback-success-text') || '') || 243 (respWindow 244 ? sprintf(__('Weâll call %1$s within %2$s.'), phoneDisp || __('you'), respWindow) 245 : sprintf(__('Weâll call %s shortly.'), phoneDisp || __('you'))); 246 247 var html = '<h3 class="hzo-callback__success-title" id="' + headingId + '" tabindex="-1">' + escapeHtml(title) + '</h3>'; 248 html += '<p class="hzo-callback__success-text">' + escapeHtml(text) + '</p>'; 249 250 // Hide the input controls (keep them in the DOM for JS-off resubmit safety 251 // if success is reverted; we simply collapse them). 252 var controls = root.querySelectorAll('.hzo-callback__field, .hzo-callback__window, .hzo-callback__consent, .hzo-callback__submit, .hzo-callback__promise'); 253 for (var i = 0; i < controls.length; i++) { controls[i].hidden = true; } 254 255 if (mode === 'countdown') { 256 html += '<div class="hzo-callback__countdown" data-hzo-callback-ring>' + 257 '<span class="hzo-callback__countdown-num" data-hzo-callback-num aria-hidden="true"></span>' + 258 '<span class="hzo-callback__countdown-text" data-hzo-callback-cd-text></span></div>'; 259 } 260 if (mode === 'tel') { 261 var href = region.getAttribute('data-hzo-callback-tel-href') || ''; 262 var disp = region.getAttribute('data-hzo-callback-tel-disp') || ''; 263 if (href) { 264 html += '<a class="hzo-callback__success-tel" href="tel:' + escapeAttr(href) + '" data-hzo-callback-copy="' + escapeAttr(disp) + '">' + 265 sprintf(__('Call us now: %s'), escapeHtml(disp)) + '</a>'; 266 } 267 } 268 269 region.innerHTML = html; 270 region.hidden = false; 271 272 // Announce from the persistent region (G30) BEFORE moving focus. 273 announce(title + '. ' + text, 'polite'); 274 275 // Move focus to the success heading (never <body>, PRESERVE-2). 276 var h = region.querySelector('#' + (window.CSS && CSS.escape ? CSS.escape(headingId) : headingId)) || region.querySelector('.hzo-callback__success-title'); 277 if (h && typeof h.focus === 'function') { try { h.focus(); } catch (e) { } } 278 279 if (mode === 'countdown') { startCountdown(root, respWindow); } 280 } 281 282 /* ââ Countdown: one rAF-throttled clock; the ticking number is NOT announced 283 each second (only the initial title/text was announced above). Reduced 284 motion â the ring is CSS-static; the number still updates as text. ââ */ 285 function startCountdown(root, respWindow) { 286 var explicit = root.getAttribute('data-hzo-callback-countdown'); 287 if (explicit && explicit.toLowerCase() === 'off') { return; } 288 var total = (explicit && /^\d+$/.test(explicit)) ? parseInt(explicit, 10) : parseSeconds(respWindow); 289 if (!total || total <= 0) { return; } 290 291 var numEl = root.querySelector('[data-hzo-callback-num]'); 292 var txtEl = root.querySelector('[data-hzo-callback-cd-text]'); 293 var ring = root.querySelector('[data-hzo-callback-ring]'); 294 var start = (window.performance && performance.now) ? performance.now() : Date.now(); 295 var raf = window.requestAnimationFrame || function (fn) { return setTimeout(function () { fn(Date.now()); }, 250); }; 296 var caf = window.cancelAnimationFrame || clearTimeout; 297 298 function fmt(sec) { 299 var m = Math.floor(sec / 60), s = sec % 60; 300 return m > 0 ? (m + ':' + (s < 10 ? '0' : '') + s) : String(s); 301 } 302 function frame(now) { 303 if (!root.__hzoAlive) { return; }
304 var elapsed = ((now || Date.now()) - start) / 1000; 305 var remain = Math.max(0, Math.ceil(total - elapsed)); 306 if (numEl) { numEl.textContent = fmt(remain); } 307 if (txtEl) { 308 txtEl.textContent = remain > 0 309 ? sprintf(__('Calling you back in %sâ¦'), fmt(remain)) 310 : __('Calling you nowâ¦'); 311 } 312 if (ring) { 313 ring.style.setProperty('--hzo-cb-progress', String(Math.min(1, elapsed / total))); 314 } 315 if (remain > 0) { root.__hzoRaf = raf(frame); } 316 } 317 root.__hzoAlive = true; 318 root.__hzoRaf = raf(frame); 319 // Clean up if the node is removed. 320 root.__hzoCancelCd = function () { root.__hzoAlive = false; if (root.__hzoRaf) { caf(root.__hzoRaf); } }; 321 } 322 323 /* ââ HTML escaping (we build innerHTML from user-entered values) ââ */ 324 function escapeHtml(s) { 325 return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { 326 return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; 327 }); 328 } 329 function escapeAttr(s) { return escapeHtml(s).replace(/`/g, '`'); } 330 331 /* ââ The submit flow ââ */ 332 function onSubmit(root, e) { 333 if (e) { e.preventDefault(); } 334 if (root.__hzoBusy) { return; } // re-entrancy lock (blocks the double-tap â duplicate) 335 336 // Honeypot: a filled hidden field = a bot; silently no-op (pretend success 337 // is unnecessary â just drop). 338 var hp = root.querySelector('input[name="hzo_cb_company"]'); 339 if (hp && hp.value) { return; } 340 341 var phoneEl = root.querySelector('input[name="phone"]'); 342 if (!phoneEl || !isValidPhone(phoneEl.value)) { 343 showError(root, __('Enter a valid 10-digit phone number.')); 344 if (phoneEl && typeof phoneEl.focus === 'function') { phoneEl.focus(); } 345 return; 346 } 347 // consent-required: surface an inline error on click (never a dead button). 348 var consent = root.querySelector('[data-hzo-callback-consent-required]'); 349 if (consent && !consent.checked) { 350 showError(root, __('Please agree to be contacted so we can call you back.')); 351 if (typeof consent.focus === 'function') { consent.focus(); } 352 return; 353 } 354 clearError(root); 355 356 setLoading(root, true); // <100ms, before any network 357 358 var digits = digitsOf(phoneEl.value); 359 var body = JSON.stringify(payloadFor(root, 'complete')); 360 361 // AbortController timeout (~10s) â no engine-wide timeout exists, so state one. 362 var controller = ('AbortController' in window) ? new AbortController() : null; 363 var timer = setTimeout(function () { if (controller) { controller.abort(); } }, 10000); 364 365 function done(ok, offline) { 366 clearTimeout(timer); 367 setLoading(root, false); 368 if (ok) { 369 root.__hzoSubmitted = true; // pagehide must not fire a redundant partial 370 emit(root, 'complete', digits); 371 var redirect = root.getAttribute('data-hzo-callback-redirect'); 372 if (redirect) { window.location.href = redirect; return; } 373 showSuccess(root); 374 } else { 375 var msg = offline 376 ? __('You appear to be offline â reconnect and tap Call me back again.') 377 : __('Couldnât send your request â check your connection and tap Call me back again.'); 378 showError(root, msg); 379 } 380 } 381 382 if (!window.fetch) { 383 // No fetch â let the native form POST proceed (server records it). 384 clearTimeout(timer); 385 setLoading(root, false); 386 if (root.submit) { HTMLFormElement.prototype.submit.call(root); } 387 return; 388 } 389 390 if (navigator && navigator.onLine === false) { done(false, true); return; } 391 392 var opts = { 393 method: 'POST', 394 headers: { 'Content-Type': 'application/json' }, 395 body: body, 396 credentials: 'same-origin' 397 }; 398 if (controller) { opts.signal = controller.signal; } 399 400 fetch(REST_URL, opts).then(function (res) { 401 return res.ok ? res.json()['catch'](function () { return { ok: true }; }) : Promise.reject(res); 402 }).then(function () { 403 done(true); 404 })['catch'](function () { 405 // Reconcile on every catch (G1): release lock, preserve values, show a 406 // recoverable inline error; distinguish offline from server error. 407 done(false, (navigator && navigator.onLine === false)); 408 }); 409 } 410
411 /* ââ hzo:hourschange consumer â swap promise/cta TEXT only (no re-parse) ââ */ 412 function reflectHours(root) { 413 if (!(window.hzo && window.hzo.hours)) { return; } 414 var after; 415 try { after = window.hzo.hours.state() === 'closed'; } catch (e) { return; } 416 417 var promiseEl = root.querySelector('[data-hzo-callback-promise-el]'); 418 var ctaEl = root.querySelector('[data-hzo-callback-cta-el] .hzo-callback__submit-label') || 419 root.querySelector('.hzo-callback__submit-label'); 420 421 var promise = root.getAttribute('data-hzo-callback-promise') || ''; 422 var afterPromise = root.getAttribute('data-hzo-callback-after-promise') || ''; 423 var cta = root.getAttribute('data-hzo-callback-cta') || ''; 424 var afterCta = root.getAttribute('data-hzo-callback-after-cta') || ''; 425 426 root.classList.toggle('is-after-hours', after); 427 if (promiseEl && (after ? afterPromise : promise)) { 428 promiseEl.textContent = after && afterPromise ? afterPromise : promise; 429 } 430 if (ctaEl && (after ? afterCta : cta)) { 431 ctaEl.textContent = after && afterCta ? afterCta : cta; 432 } 433 } 434 function reflectHoursAll() { 435 var roots = document.querySelectorAll('[data-hzo-callback]'); 436 for (var i = 0; i < roots.length; i++) { reflectHours(roots[i]); } 437 } 438 439 /* ââ Per-instance wiring ââ */ 440 function setup(root) { 441 if (!root || root.getAttribute('data-hzo-callback-ready') === 'true') { return; } 442 root.setAttribute('data-hzo-callback-ready', 'true'); 443 root.__hzoToken = 'cb-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8); 444 445 var phoneEl = root.querySelector('input[name="phone"]'); 446 if (phoneEl) { 447 phoneEl.addEventListener('input', function () { 448 var pos = phoneEl.selectionStart, before = phoneEl.value; 449 phoneEl.value = maskPhone(phoneEl.value); 450 // keep caret sane on append 451 if (pos === before.length) { try { phoneEl.setSelectionRange(phoneEl.value.length, phoneEl.value.length); } catch (e) { } } 452 if (isValidPhone(phoneEl.value)) { clearError(root); } 453 }); 454 phoneEl.addEventListener('blur', function () { 455 if (isValidPhone(phoneEl.value)) { sendPartial(root, false); } 456 }); 457 } 458 459 root.addEventListener('submit', function (e) { onSubmit(root, e); }); 460 461 // Initial hours reflect (server already stamped is-after-hours; this 462 // reconciles against the live client model if present). 463 reflectHours(root); 464 } 465 466 function init(scope) { 467 var roots = (scope || document).querySelectorAll('[data-hzo-callback]:not([data-hzo-callback-ready="true"])'); 468 for (var i = 0; i < roots.length; i++) { setup(roots[i]); } 469 } 470 471 /* ââ Page-level listeners (bound once) ââ */ 472 if (!window.hzo.__callbackBound) { 473 window.hzo.__callbackBound = true; 474 475 // Hours flip â swap copy on every instance. 476 document.addEventListener('hzo:hourschange', reflectHoursAll); 477 478 // Abandon capture: flush a partial for any unsubmitted, valid instance. 479 function flushPartials() { 480 var roots = document.querySelectorAll('[data-hzo-callback][data-hzo-callback-ready="true"]'); 481 for (var i = 0; i < roots.length; i++) { 482 var r = roots[i]; 483 if (r.__hzoSubmitted) { continue; } 484 sendPartial(r, true); 485 } 486 } 487 window.addEventListener('pagehide', flushPartials); 488 document.addEventListener('visibilitychange', function () { 489 if (document.visibilityState === 'hidden') { flushPartials(); } 490 }); 491 492 // success=tel "Call us now" â mirror the intent to dataLayer as 493 // phone_call, and (desktop, where tel: no-ops) offer click-to-copy (§13). 494 document.addEventListener('click', function (e) { 495 var a = e.target && e.target.closest ? e.target.closest('.hzo-callback__success-tel') : null; 496 if (!a) { return; } 497 pushDataLayer({ 498 event: 'phone_call', 499 phone_number: (a.getAttribute('href') || '').replace(/^tel:/, ''), 500 source: 'callback' 501 }); 502 var copy = a.getAttribute('data-hzo-callback-copy'); 503 if (copy && navigator.clipboard && navigator.clipboard.writeText) { 504 navigator.clipboard.writeText(copy).then(function () { 505 announce(sprintf(__('Phone number copied: %s'), copy), 'polite'); 506 })['catch'](function () { /* clipboard blocked â the tel link still works */ }); 507 } 508 }); 509 510 // bfcache restore â clear any stuck busy lock (G58 analog). 511 window.addEventListener('pageshow', function (e) { 512 if (e.persisted) { 513 var roots = document.querySelectorAll('[data-hzo-callback]'); 514 for (var i = 0; i < roots.length;
514 i++) { setLoading(roots[i], false); } 515 } 516 }); 517 } 518 519 window.hzo.callback = { init: init, setup: setup }; 520 521 if (document.readyState === 'loading') { 522 document.addEventListener('DOMContentLoaded', function () { init(); }); 523 } else { 524 init(); 525 } 526})();
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.