1/*! Zippykind LLC - Proprietary and confidential. Copyright (c) Zippykind LLC. All rights reserved. 2 * zk-signature: sha256=c9b203059df1191f46324da617a7ad50d4caf6c4a802b8fe0abba8b8d3372eb6 file=zk-track.js by=Zippykind LLC 3 */ 4/* fe-1054 */ 5;(function(){var s="Zippykind LLC|zk-signature|sha256=c9b203059df1191f46324da617a7ad50d4caf6c4a802b8fe0abba8b8d3372eb6|file=zk-track.js";return s;})(); 6(function (w, d) { 7 "use strict"; 8 9 // fe-1055 10 var SEND = true; 11 // fe-1056 12 var ENDPOINT = "/ajaxfront/track"; 13 // fe-1057 14 var IX_ENDPOINT = "/ajaxfront/behavior"; 15 // fe-1058 16 var SEND_IX = true; 17 // fe-1059 18 var BEACON_TYPE = "text/plain;charset=UTF-8"; 19 20 var UTM = ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term", "zkref"]; 21 22 // fe-1060 23 var AFF_PARAMS = ["zkref", "zka", "aff"]; 24 var AFF_CAMPAIGN_PARAM = "zkcid"; 25 var AFF_MAX = 64; 26 27 // fe-1061 28 var DWELL_MIN_MS = 1000; // fe-1062 29 var SECTION_RATIO = 0.3; // fe-1063 30 var HOVER_MS = 400; // fe-1064 31 var SEARCH_DEBOUNCE_MS = 1200; 32 var SELECT_DEBOUNCE_MS = 900; 33 var IDLE_MS = 30000; // fe-1065 34 var FLUSH_MS = 30000; 35 var IX_CAP = 200; // fe-1066 36 var TEXT_CAP = 50; // fe-1067 37 var SELECT_CAP = 200; 38 var QUERY_CAP = 100; 39 40 // fe-1068 41 var STEPS = { 42 // fe-1069 43 page_view: "any page load", 44 // fe-1070 45 qo_hover: "industry cell hovered/focused, capture panel shown", 46 qo_dismiss: "capture panel closed without a submit", 47 qo_submit: "email submitted from the capture panel", 48 qo_sent: "verification email queued (server side once ported)", 49 qo_resend: "resend link clicked", 50 qo_verified: "verification link followed, landed on quick start", 51 qo_choice: "the two-option modal was shown", 52 qo_peek: "chose Peek at an Active Account", 53 qo_tour_tools: "chose Tour The Tools That Will Make Your Life Easier", 54 qo_tour_step1: "tour step 1 shown (Take Tour button)", 55 qo_tour_step2: "tour step 2 shown (Dashboard Demo switch)", 56 qo_tour_done: "tour finished or skipped", 57 qo_demo_load: "dashboard demo loaded (props.demo = operator|admin)", 58 // fe-1071 59 qo_signup_click: "clicked through to signup -- the zk_vid handoff to the app", 60 // fe-1072 61 ix_batch: "batched interaction records (see zkTrack.ix)", 62 cv_contact: "contact form submitted", 63 cv_demo: "book-a-demo form submitted (qualified)", 64 cv_newsletter: "footer email capture submitted", 65 cv_login_click: "clicked through to /app/login", 66 cv_phone_click: "clicked a tel: link", 67 // fe-1073 68 how_it_works_status: "how-it-works status pill clicked (zk-flow.js)", 69 video_play: "inline video opened in the lightbox (zk-video.js)", 70 // fe-1074 71 qo_signup: "account created (SERVER SIDE)", 72 qo_paid: "first payment taken (SERVER SIDE)" 73 }; 74 75 // fe-1075 76 var CONVERSIONS = { 77 contact: "cv_contact", 78 demo: "cv_demo", 79 newsletter: "cv_newsletter", 80 signup_click: "qo_signup_click", 81 login_click: "cv_login_click", 82 phone_click: "cv_phone_click" 83 }; 84 // fe-1076 85 86 // fe-1077 87 function get(store, k) { 88 try { return w[store].getItem(k); } catch (e) { return null; } 89 } 90 function set(store, k, v) { 91 try { w[store].setItem(k, v); } catch (e) { /* fe-1078 */ } 92 } 93 function getJSON(store, k) { 94 var raw = get(store, k); 95 if (!raw) return null; 96 try { return JSON.parse(raw); } catch (e) { return null; } 97 } 98 function setJSON(store, k, v) { set(store, k, JSON.stringify(v)); } 99 100 // fe-1079 101 function cookieDomain() { 102 var h = (w.location.hostname || "").toLowerCase(); 103 if (h === "zippykind.com" || h.slice(-14) === ".zippykind.com") return ".zippykind.com"; 104 return ""; // fe-1080 105 } 106 function getCookie(k) { 107 try { 108 var parts = ("; " + (d.cookie || "")).split("; " + k + "="); 109 return parts.length < 2 ? null : decodeURIComponent(parts.pop().split(";").shift()); 110 } catch (e) { return null; } 111 } 112 function setCookie(k, v, days) { 113 try { 114 var dom = cookieDomain(); 115 var exp = new Date(Date.now() + days * 86400000).toUTCString(); 116 d.cookie = k + "=" + encodeURIComponent(v) + 117 ";expires=" + exp + ";path=/" + 118 (dom ? ";domain=" + dom : "") + 119 ";SameSite=Lax" + 120 (w.location.protocol === "https:" ? ";Secure" : ""); 121 } catch (e) { /* fe-1081 */ } 122 } 123 124 function rid() { 125 // fe-1082 126 return Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10); 127 } 128 129 function params() { 130 var out = {}; 131 var q = w.location.search; 132 if (!q || q.length < 2) return out; 133 q.slice(1).split("&").forEach(function (pair) { 134 if (!pair) return; 135 var i = pair.indexOf("="); 136 var k = decodeURIComponent(i < 0 ? pair : pair.slice(0, i)
136).replace(/\+/g, " "); 137 var v = i < 0 ? "" : decodeURIComponent(pair.slice(i + 1).replace(/\+/g, " ")); 138 out[k] = v; 139 }); 140 return out; 141 } 142 143 var Q = params(); 144 var CFG = w.ZK_TRACK_CFG || {}; // fe-1083 145 146 function campaignFromUrl() { 147 var c = null; 148 UTM.forEach(function (k) { 149 if (Q[k]) { c = c || {}; c[k] = Q[k].slice(0, 255); } 150 }); 151 if (c) { 152 c.landing = w.location.pathname.split("/").pop() || "index.html"; 153 c.referrer = (d.referrer || "").slice(0, 512); 154 c.ts = new Date().toISOString(); 155 } 156 return c; 157 } 158 159 // fe-1084 160 if (w.ZKConsent && w.ZKConsent.optedOut("analytics")) { 161 var stub = function () {}; 162 w.zkTrack = function () {}; 163 w.zkTrack.link = function (href) { return href; }; 164 w.zkTrack.steps = STEPS; 165 w.zkTrack.param = function (k) { return Q[k] || ""; }; 166 w.zkTrack.vid = function () { return ""; }; 167 w.zkTrack.campaign = function () { return { first: null, last: null }; }; 168 w.zkTrack.conversion = stub; 169 w.zkTrack.conversions = CONVERSIONS; 170 w.zkTrack.ix = stub; 171 w.zkTrack.flush = stub; 172 w.zkTrack.arrival = function () { return { session: null, first: null }; }; 173 w.zkTrack.affiliate = function () { return null; }; 174 w.zkTrack.visitor = function () { return { type: "unknown", confidence: "assumed", signals: [] }; }; 175 w.zkTrack.rescan = stub; 176 return; 177 } 178 179 // fe-1085 180 var vid = getCookie("zk_vid") || get("localStorage", "zk_vid"); 181 if (!vid) { vid = rid(); } 182 vid = String(vid).slice(0, 40); 183 set("localStorage", "zk_vid", vid); 184 setCookie("zk_vid", vid, 730); 185 186 var sid = get("sessionStorage", "zk_sid"); 187 if (!sid) { sid = rid(); set("sessionStorage", "zk_sid", sid); } 188 189 var fromUrl = campaignFromUrl(); 190 var first = getJSON("localStorage", "zk_campaign_first"); 191 if (!first && fromUrl) { first = fromUrl; set("localStorage", "zk_campaign_first", JSON.stringify(first)); } 192 var last = fromUrl || getJSON("sessionStorage", "zk_campaign_last"); 193 if (fromUrl) set("sessionStorage", "zk_campaign_last", JSON.stringify(fromUrl)); 194 195 // fe-1086 196 if (first) { 197 setCookie("zk_camp", [ 198 (first.utm_source || "").slice(0, 60), 199 (first.utm_medium || "").slice(0, 40), 200 (first.utm_campaign || "").slice(0, 80) 201 ].join("|"), 365); 202 } 203 204 // fe-1087 205 var AI_HOSTS = [ 206 ["chatgpt.com", "chatgpt"], ["chat.openai.com", "chatgpt"], ["openai.com", "chatgpt"], 207 ["perplexity.ai", "perplexity"], 208 ["claude.ai", "claude"], ["anthropic.com", "claude"], 209 ["gemini.google.com", "gemini"], ["bard.google.com", "gemini"], 210 ["copilot.microsoft.com", "copilot"], ["edgeservices.bing.com", "copilot"], 211 ["you.com", "you"], ["phind.com", "phind"], ["poe.com", "poe"], 212 ["meta.ai", "meta_ai"], ["grok.com", "grok"], ["x.ai", "grok"], 213 ["deepseek.com", "deepseek"], ["mistral.ai", "mistral"], ["kagi.com", "kagi"] 214 ]; 215 var SOCIAL_HOSTS = [ 216 ["instagram.com", "instagram"], 217 ["facebook.com", "facebook"], ["fb.com", "facebook"], ["fb.me", "facebook"], 218 ["linkedin.com", "linkedin"], ["lnkd.in", "linkedin"], 219 ["x.com", "x"], ["twitter.com", "x"], ["t.co", "x"], 220 ["youtube.com", "youtube"], ["youtu.be", "youtube"], 221 ["tiktok.com", "tiktok"], ["pinterest.", "pinterest"], 222 ["threads.net", "threads"], ["threads.com", "threads"], 223 ["reddit.com", "reddit"], ["quora.com", "quora"], ["nextdoor.com", "nextdoor"] 224 ]; 225 var SEARCH_HOSTS = [ 226 ["google.", "google"], ["bing.com", "bing"], ["yahoo.", "yahoo"], 227 ["duckduckgo.com", "duckduckgo"], ["ecosia.org", "ecosia"], 228 ["search.brave.com", "brave"], ["startpage.com", "startpage"], 229 ["yandex.", "yandex"], ["baidu.com", "baidu"], ["ask.com", "ask"] 230 ]; 231 var MAIL_HOSTS = [ 232 ["mail.google.com", "email"], ["outlook.", "email"], ["mail.
232yahoo.com", "email"], 233 ["mail.proton.me", "email"], ["superhuman.com", "email"] 234 ]; 235 // fe-1088 236 var SRC_MAP = { 237 "google": "google", "google ads": "google", "googleads": "google", "adwords": "google", 238 "bing": "bing", "microsoft": "bing", "msn": "bing", "yahoo": "yahoo", 239 "duckduckgo": "duckduckgo", "ddg": "duckduckgo", 240 "chatgpt": "chatgpt", "chatgpt.com": "chatgpt", "openai": "chatgpt", 241 "perplexity": "perplexity", "perplexity.ai": "perplexity", 242 "claude": "claude", "claude.ai": "claude", 243 "gemini": "gemini", "bard": "gemini", "copilot": "copilot", 244 "ig": "instagram", "instagram": "instagram", 245 "fb": "facebook", "facebook": "facebook", "meta": "facebook", 246 "li": "linkedin", "linkedin": "linkedin", 247 "x": "x", "twitter": "x", 248 "yt": "youtube", "youtube": "youtube", 249 "tiktok": "tiktok", "pinterest": "pinterest", "reddit": "reddit", 250 "email": "email", "sendgrid": "email", "newsletter": "email" 251 }; 252 var AI_CHANNELS = { chatgpt: 1, perplexity: 1, claude: 1, gemini: 1, copilot: 1, 253 you: 1, phind: 1, poe: 1, meta_ai: 1, grok: 1, deepseek: 1,
254 mistral: 1, kagi: 1 }; 255 var SEARCH_CHANNELS = { google: 1, bing: 1, yahoo: 1, duckduckgo: 1, ecosia: 1, 256 brave: 1, startpage: 1, yandex: 1, baidu: 1, ask: 1 }; 257 var SOCIAL_CHANNELS = { instagram: 1, facebook: 1, linkedin: 1, x: 1, youtube: 1, 258 tiktok: 1, pinterest: 1, threads: 1, reddit: 1, quora: 1, 259 nextdoor: 1 }; 260 // fe-1089 261 var CLICK_IDS = [["gclid", "google"], ["gbraid", "google"], ["wbraid", "google"], 262 ["dclid", "google"], ["msclkid", "bing"], ["fbclid", "facebook"], 263 ["igshid", "instagram"], ["li_fat_id", "linkedin"], 264 ["ttclid", "tiktok"], ["twclid", "x"], ["epik", "pinterest"]]; 265 var PAID_MEDIUMS = { cpc: 1, ppc: 1, paid: 1, paidsocial: 1, paid_social: 1, 266 "paid-social": 1, display: 1, banner: 1, retargeting: 1 }; 267 268 function hostOf(url) { 269 if (!url) return ""; 270 try { 271 var a = d.createElement("a"); 272 a.href = url; 273 return (a.hostname || "").toLowerCase(); 274 } catch (e) { return ""; } 275 } 276 function matchHost(host, table) { 277 for (var i = 0; i < table.length; i++) { 278 if (host.indexOf(table[i][0]) > -1) return table[i][1]; 279 } 280 return ""; 281 } 282 function typeOf(channel) { 283 if (AI_CHANNELS[channel]) return "ai"; 284 if (SEARCH_CHANNELS[channel]) return "search"; 285 if (SOCIAL_CHANNELS[channel]) return "social"; 286 if (channel === "email") return "email"; 287 if (channel === "direct" || channel === "internal" || channel === "referral") return channel; 288 return "referral"; 289 } 290 // fe-1090 291 function refQuery(ref) { 292 if (!ref || ref.indexOf("?") < 0) return ""; 293 var qs = ref.slice(ref.indexOf("?") + 1).split("&"); 294 var keys = { q: 1, query: 1, p: 1, wd: 1, text: 1 }; 295 for (var i = 0; i < qs.length; i++) { 296 var kv = qs[i].split("="); 297 if (keys[decodeURIComponent(kv[0] || "").toLowerCase()] && kv[1]) { 298 try { return decodeURIComponent(kv[1].replace(/\+/g, " ")).slice(0, 120); } 299 catch (e) { return ""; } 300 } 301 } 302 return ""; 303 } 304 305 function resolveArrival() { 306 var ref = (d.referrer || ""); 307 var refHost = hostOf(ref); 308 var here = (w.location.hostname || "").toLowerCase(); 309 var internal = !!refHost && (refHost === here || refHost === "zippykind.com" || 310 refHost.slice(-14) === ".zippykind.com"); 311 var src = (Q.utm_source || "").toLowerCase().trim(); 312 var medium = (Q.utm_medium || "").toLowerCase().trim(); 313 var channel = ""; 314 var how = ""; 315 var i; 316 317 if (src && SRC_MAP[src]) { channel = SRC_MAP[src]; how = "utm_source"; } 318 if (!channel) { 319 for (i = 0; i < CLICK_IDS.length; i++) { 320 if (Q[CLICK_IDS[i][0]]) { channel = CLICK_IDS[i][1]; how = CLICK_IDS[i][0]; break; } 321 } 322 } 323 if (!channel && refHost && !internal) { 324 channel = matchHost(refHost, AI_HOSTS); 325 if (channel) how = "referrer_ai"; 326 if (!channel) { channel = matchHost(refHost, SOCIAL_HOSTS); if (channel) how = "referrer_social"; } 327 if (!channel) { channel = matchHost(refHost, SEARCH_HOSTS); if (channel) how = "referrer_search"; } 328 if (!channel) { channel = matchHost(refHost, MAIL_HOSTS); if (channel) how = "referrer_mail"; } 329 } 330 if (!channel && (medium === "email" || src === "email")) { channel = "email"; how = "utm_medium"; } 331 if (!channel && internal) { channel = "internal"; how = "referrer_internal"; } 332 if (!channel && refHost) { channel = "referral"; how = "referrer_other"; } 333 if (!channel) { channel = "direct"; how = (src || medium) ? "utm_only" : "no_referrer"; } 334 335 var paid = !!PAID_MEDIUMS[medium]; 336 for (i = 0; i < CLICK_IDS.length && !paid; i++) { if (Q[CLICK_IDS[i][0]]) paid = true; } 337 338 return { 339 channel: channel, 340 channel_type: typeOf(channel), 341 paid: paid, 342 resolved_by: how, 343 referrer_host: refHost.slice(0, 120), 344 referrer: ref.slice(0, 512), 345 ref_query: refQuery(ref), 346 landing: w.location.pathname.split("/").pop() || "index.html", 347 landing_path: w.location.pathname.slice(0, 255), 348 landing_host: here.slice(0, 120), 349 utm_source: (Q.utm_source || "").slice(0, 255), 350 utm_medium: (Q.utm_medium || "").slice(0, 255), 351 utm_campaign: (Q.utm_campaign || "").slice(0, 255), 352 ts: new Date().toISOString() 353 }; 354 } 355 356 // fe-1091 357 var arrival = getJSON("sessionStorage", "zk_arrival"); 358 if (!arrival) { 359 arrival = resolveArrival(); 360 setJSON("sessionStorage", "zk_arrival", arrival); 361 } 362 var arrivalFirst = getJSON("localStorage", "zk_arrival_first"); 363 if (!arrivalFirst) { arrivalFirst = arrival; setJSON("localStorage", "zk_arrival_first", arrival); } 364 365 // fe-1092 366 var UA = (w.navigator && w.navigator.userAgent) || ""; 367 // fe-1093 368 var UA_AI = /(GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-User|Claude-SearchBot|anthropic-ai|PerplexityBot|Perplexity-User|Google-Extended|GoogleOther|Applebot-Extended|Bytespider|CCBot|meta-externalagent|meta-externalfetcher|FacebookBot|cohere-ai|Diffbot|YouBot|DuckAssistBot|Amazonbot|Ai2Bot|PanguBot|ImagesiftBot|Timpibot|omgili|AndiBot|MistralAI-User|DeepSeekBot)/i; 369 // fe-1094 370 var UA_BOT = /(bot\b|bot\/|crawl|spider|slurp|headless|phantomjs|puppeteer|playwright|selenium|python-requests|curl\/|wget|httpclient|axios\/|node-fetch|go-http-client|java\/|scrapy|libwww|okhttp|lighthouse|pagespeed|gtmetrix|pingdom|uptimerobot)/i; 371 372 function classifyVisitor() { 373 var signals = []; 374 var type = "human"; 375 var confidence = "assumed"; // fe-1095 376 var nav = w.navigator || {}; 377 378 if (UA_AI.test(UA)) { type = "ai_agent"; confidence = "declared"; signals.push("ua_ai"); } 379 else if (UA_BOT.test(UA)) { type = "bot"; confidence = "declared"; signals.push("ua_bot"); } 380 381 if (nav.webdriver === true) { 382 signals.push("webdriver"); 383 if (type === "human") { type = "automation"; confidence = "strong"; } 384 } 385 if (/HeadlessChrome/i.test(UA)) { 386 signals.push("headless_ua"); 387 if (type === "human") { type = "automation"; confidence = "strong"; } 388 } 389 // fe-1096 390 if (!nav.languages || !nav.languages.length) signals.push("no_languages"); 391 if (nav.plugins && nav.plugins.length === 0 && !/Mobi|Android|iPhone|iPad/i.test(UA)) { 392 signals.push("no_plugins"); 393 } 394 if (type === "human" && signals.length >= 2) confidence = "weak"; 395 return { type: type, confidence: confidence, signals: signals }; 396 } 397 var visitor = classifyVisitor(); 398 399 // fe-1097 400 var sawHumanInput = false;
401 function noteHumanInput() { sawHumanInput = true; } 402 403 // fe-1098 404 function cleanCode(v) { 405 if (!v) return ""; 406 // fe-1099 407 return String(v).replace(/[^A-Za-z0-9_.\-]/g, "").slice(0, AFF_MAX); 408 } 409 410 function affFromUrl() { 411 var code = ""; 412 var param = ""; 413 for (var i = 0; i < AFF_PARAMS.length && !code; i++) { 414 code = cleanCode(Q[AFF_PARAMS[i]]); 415 if (code) param = AFF_PARAMS[i]; 416 } 417 if (!code) return null; 418 return { 419 code: code, 420 campaign_id: cleanCode(Q[AFF_CAMPAIGN_PARAM]), 421 param: param, 422 landing: w.location.pathname.split("/").pop() || "index.html", 423 referrer: (d.referrer || "").slice(0, 512), 424 channel: arrival.channel, 425 channel_type: arrival.channel_type, 426 ts: new Date().toISOString() 427 }; 428 } 429 430 var affUrl = affFromUrl(); 431 var affFirst = getJSON("localStorage", "zk_aff_first"); 432 // fe-1100 433 if (!affFirst) { 434 var ck = getCookie("zk_aff"); 435 if (ck) { 436 var bits = ck.split("|"); 437 if (cleanCode(bits[0])) { 438 affFirst = { code: cleanCode(bits[0]), campaign_id: cleanCode(bits[1]), 439 param: "cookie", landing: "", referrer: "", channel: "", 440 channel_type: "", ts: "" }; 441 setJSON("localStorage", "zk_aff_first", affFirst); 442 } 443 } 444 } 445 // fe-1101 446 if (!affFirst && affUrl) { 447 affFirst = affUrl; 448 setJSON("localStorage", "zk_aff_first", affFirst); 449 } 450 if (affFirst && affFirst.code) { 451 setCookie("zk_aff", affFirst.code + "|" + (affFirst.campaign_id || ""), 365); 452 } 453 var affLast = affUrl || getJSON("sessionStorage", "zk_aff_last"); 454 if (affUrl) setJSON("sessionStorage", "zk_aff_last", affUrl); 455 456 function affiliateBlock() { 457 if (!affFirst && !affLast) return null; 458 var f = affFirst || {}; 459 var l = affLast || {}; 460 return { 461 // fe-1102 462 code: f.code || "", 463 campaign_id: f.campaign_id || "", 464 first_ts: f.ts || "", 465 first_landing: f.landing || "", 466 first_param: f.param || "", 467 // fe-1103 468 last_code: l.code || "", 469 last_ts: l.ts || "", 470 contested: !!(f.code && l.code && f.code !== l.code), 471 // fe-1104 472 term_days: null, 473 term_note: "365d default, starts at referred-party signup, computed server side" 474 }; 475 } 476 477 // fe-1105 478 w.dataLayer = w.dataLayer || []; 479 w.zkTrackLog = w.zkTrackLog || []; 480 481 function baseEvent(step, props) { 482 return { 483 event: "zk_" + step, // fe-1106 484 ev: "zk", 485 step: step, 486 ts: new Date().toISOString(), 487 vid: vid, 488 sid: sid, 489 // fe-1107 490 ysid: CFG.ysid || "", 491 uid: CFG.uid || "", 492 page: w.location.pathname.split("/").pop() || "index.html", 493 href: w.location.href.slice(0, 512), 494 campaign_first: first || null, 495 campaign_last: last || null, 496 // fe-1108 497 arrival: arrival, 498 arrival_first: (arrivalFirst && arrivalFirst.ts !== arrival.ts) ? arrivalFirst : null, 499 affiliate: affiliateBlock(), 500 vt: visitor.type, 501 vt_confidence: visitor.confidence, 502 props: props || {} 503 }; 504 } 505 506 function send(url, ev) { 507 if (!SEND) { 508 console.debug("[zk-track] STUBBED (nothing sent):", ev.step, ev.props); 509 return; 510 } 511 // fe-1109 512 if (url === IX_ENDPOINT && !SEND_IX) { 513 console.debug("[zk-track] ix_batch held (SEND_IX off):", ev.step); 514 return; 515 } 516 // fe-1110 517 try { 518 var body = JSON.stringify(ev); 519 if (w.navigator && w.navigator.sendBeacon) { 520 w.navigator.sendBeacon(url, new Blob([body], { type: BEACON_TYPE })); 521 } else { 522 fetch(url, { 523 method: "POST", 524 headers: { "Content-Type": BEACON_TYPE }, 525 body: body, 526 keepalive: true 527 })["catch"](function () {}); 528 } 529 } catch (e) { /* fe-1111 */ } 530 } 531 532 function track(step, props) { 533 if (!STEPS[step]) { 534 // fe-1112 535 console.warn("[zk-track] unknown step id: " + step + " (add it to STEPS in zk-track.js)"); 536 } 537 var ev = baseEvent(step, props); 538 w.dataLayer.push(ev); 539 w.zkTrackLog.push(ev); 540 send(ENDPOINT, ev); 541 } 542 543 // fe-1113 544 function conversion(kind, props) { 545 var step = CONVERSIONS[kind]; 546 if (!step) { 547 console.warn("[zk-track] unknown conversion kind: " + kind + " (add it to CONVERSIONS)"); 548 return; 549 } 550 var p = props || {}; 551 p.conversion_kind = kind; 552 var ev = baseEvent(step, p); 553 ev.conversion = true; 554 w.dataLayer.push(ev); 555 w.zkTrackLog.push(ev); 556 send(ENDPOINT, ev); 557 // fe-1114 558 flush("conversion"); 559 } 560 561 // fe-1115 562 function link(href, step, extra) { 563 var base = href.split("#")[0]; 564 var hash = href.indexOf("#") > -1 ? href.slice(href.indexOf("#")) : ""; 565 var qs = []; 566 var sawRef = false;
567 if (step) qs.push("zkc=" + encodeURIComponent(step)); 568 qs.push("zk_vid=" + encodeURIComponent(vid)); 569 UTM.forEach(function (k) { 570 var v = (last && last[k]) || (first && first[k]); 571 if (v) { qs.push(k + "=" + encodeURIComponent(v)); if (k === "zkref") sawRef = true; } 572 }); 573 // fe-1116 574 if (!sawRef && affFirst && affFirst.code) { 575 qs.push("zkref=" + encodeURIComponent(affFirst.code)); 576 } 577 if (affFirst && affFirst.campaign_id) { 578 qs.push(AFF_CAMPAIGN_PARAM + "=" + encodeURIComponent(affFirst.campaign_id)); 579 } 580 if (extra) { 581 Object.keys(extra).forEach(function (k) { 582 if (extra[k] !== undefined && extra[k] !== null && extra[k] !== "") { 583 qs.push(encodeURIComponent(k) + "=" + encodeURIComponent(extra[k])); 584 } 585 }); 586 } 587 return base + (base.indexOf("?") > -1 ? "&" : "?") + qs.join("&") + hash; 588 } 589 590 // fe-1117 591 var buf = []; 592 var dropped = 0; 593 var engagedMs = 0; 594 var maxScroll = 0; 595 var lastActivity = Date.now(); 596 var obs = null; 597 598 function sectionOf(el) { 599 if (!el) return "global"; 600 // fe-1118 601 if (el.nodeType === 3) el = el.parentElement; 602 if (!el || !el.closest) return "global"; 603 // fe-1119 604 var s = el.closest("[data-zk-section]") || el.closest("[data-section]"); 605 if (!s) return "global"; 606 return (s.getAttribute("data-zk-section") || s.getAttribute("data-section") || "global").slice(0, 60); 607 } 608 609 function noTrack(el) { 610 if (!el) return false; 611 if (el.nodeType === 3) el = el.parentElement; 612 return !!(el && el.closest && el.closest("[data-zk-notrack]")); 613 } 614 615 function ix(kind, data) { 616 var rec = data || {}; 617 rec.k = kind; 618 rec.t = Date.now(); 619 rec.p = w.location.pathname.split("/").pop() || "index.html"; 620 if (buf.length >= IX_CAP) { buf.shift(); dropped++; } 621 buf.push(rec); 622 } 623 624 // fe-1120 625 var openAt = {}; // fe-1121 626 var dwell = {}; // fe-1122 627 var enters = {}; // fe-1123 628 629 function closeOpenSections() { 630 var now = Date.now(); 631 Object.keys(openAt).forEach(function (name) { 632 var ms = now - openAt[name]; 633 if (ms >= DWELL_MIN_MS) dwell[name] = (dwell[name] || 0) + ms; 634 openAt[name] = now; // fe-1124 635 }); 636 Object.keys(dwell).forEach(function (name) { 637 ix("view", { s: name, ms: Math.round(dwell[name]), n: enters[name] || 1 }); 638 delete dwell[name]; 639 }); 640 } 641 642 function flush(reason) { 643 // fe-1125 644 closeOpenSections(); 645 if (!buf.length && !engagedMs) return; 646 var ev = baseEvent("ix_batch", { 647 reason: reason || "", 648 records: buf.slice(0), 649 dropped: dropped, 650 meta: { 651 engaged_s: Math.round(engagedMs / 1000), 652 max_scroll_pct: maxScroll, 653 viewport: (w.innerWidth || 0) + "x" + (w.innerHeight || 0), 654 screen: (w.screen ? w.screen.width : 0) + "x" + (w.screen ? w.screen.height : 0), 655 dpr: w.devicePixelRatio || 1, 656 // fe-1126 657 ua: UA.slice(0, 255), 658 vt_signals: visitor.signals.join(","), 659 human_input: sawHumanInput 660 } 661 }); 662 w.dataLayer.push(ev); 663 w.zkTrackLog.push(ev); 664 send(IX_ENDPOINT, ev); 665 buf = []; 666 dropped = 0; 667 engagedMs = 0; 668 } 669 670 if (w.IntersectionObserver) { 671 obs = new w.IntersectionObserver(function (entries) { 672 var now = Date.now(); 673 entries.forEach(function (e) { 674 var name = (e.target.getAttribute("data-zk-section") || 675 e.target.getAttribute("data-section") || "").slice(0, 60); 676 if (!name) return; 677 if (e.isIntersecting && e.intersectionRatio >= SECTION_RATIO) { 678 if (!openAt[name]) { openAt[name] = now; enters[name] = (enters[name] || 0) + 1; } 679 } else if (openAt[name]) { 680 var ms = now - openAt[name]; 681 if (ms >= DWELL_MIN_MS) dwell[name] = (dwell[name] || 0) + ms; 682 delete openAt[name]; 683 } 684 }); 685 }, { root: null, threshold: [0, SECTION_RATIO] }); 686 } 687 688 // fe-1127 689 function observeSections() { 690 if (!obs) return; 691 var nodes = d.querySelectorAll("[data-zk-section],[data-section]"); 692 for (var i = 0; i < nodes.length; i++) { 693 if (nodes[i].getAttribute("data-zk-observed")) continue; 694 nodes[i].setAttribute("data-zk-observed", "1"); 695 if (nodes[i].hasAttribute && nodes[i].hasAttribute("data-zk-nodwell")) continue; 696 obs.observe(nodes[i]); 697 } 698 } 699 700 // fe-1128 701 var scrollQueued = false;
702 var hitDepth = {}; 703 function measureScroll() { 704 scrollQueued = false; 705 var doc = d.documentElement; 706 var h = Math.max(doc.scrollHeight || 0, d.body ? (d.body.scrollHeight || 0) : 0); 707 if (h <= 0) return; 708 var pct = Math.min(100, Math.round((((w.pageYOffset || doc.scrollTop || 0) + (w.innerHeight || 0)) / h) * 100)); 709 if (pct > maxScroll) maxScroll = pct; 710 [25, 50, 75, 100].forEach(function (m) { 711 if (pct >= m && !hitDepth[m]) { hitDepth[m] = 1; ix("scroll", { pct: m }); } 712 }); 713 } 714 w.addEventListener("scroll", function () { 715 lastActivity = Date.now(); 716 if (scrollQueued) return; 717 scrollQueued = true; 718 (w.requestAnimationFrame || function (f) { setTimeout(f, 60); })(measureScroll); 719 }, { passive: true }); 720 721 // fe-1129 722 d.addEventListener("click", function (e) { 723 lastActivity = Date.now(); 724 var t = e.target; 725 if (!t || noTrack(t)) return; 726 var el = t.nodeType === 3 ? t.parentElement : t; 727 if (!el || !el.closest) return; 728 var a = el.closest("a[href],button,[role=button],input[type=submit]"); 729 var node = a || el; 730 var section = sectionOf(el); 731 // fe-1130 732 if (section === "global" && !a) return; 733 var href = (a && a.getAttribute) ? (a.getAttribute("href") || "") : ""; 734 var text = ""; 735 try { 736 text = (node.innerText || node.textContent || node.value || "") 737 .replace(/\s+/g, " ").trim().slice(0, TEXT_CAP); 738 } catch (err) { /* fe-1131 */ } 739 ix("click", { 740 s: section, 741 tag: (node.tagName || "").toLowerCase(), 742 id: (node.id || "").slice(0, 60), 743 cls: ((node.className && String(node.className)) || "").split(/\s+/)[0].slice(0, 40), 744 txt: text, 745 href: href.slice(0, 255), 746 out: /^(https?:)?\/\//i.test(href) && href.indexOf(w.location.host) < 0 747 }); 748 }, true); 749 750 // fe-1132 751 var HOVERABLE = "a[href],button,[data-zk-hover],.zk-plan,.zk-price-box,.zkq-cell,[data-key]"; 752 var hoverTimer = null; 753 var hovered = {}; 754 var canHover = !w.matchMedia || w.matchMedia("(hover: hover)").matches; 755 if (canHover) { 756 d.addEventListener("mouseover", function (e) { 757 var el = e.target; 758 if (!el || el.nodeType === 3) el = el && el.parentElement; 759 if (!el || !el.closest || noTrack(el)) return; 760 var node = el.closest(HOVERABLE); 761 if (!node) return; 762 var label = (node.innerText || "").replace(/\s+/g, " ").trim().slice(0, 30); 763 var key = sectionOf(node) + "|" + (node.id || "") + "|" + label; 764 if (hovered[key]) return; 765 if (hoverTimer) clearTimeout(hoverTimer); 766 hoverTimer = setTimeout(function () { 767 hovered[key] = 1; 768 ix("hover", { 769 s: sectionOf(node), 770 tag: (node.tagName || "").toLowerCase(), 771 id: (node.id || "").slice(0, 60), 772 txt: label, 773 ms: HOVER_MS 774 }); 775 }, HOVER_MS); 776 }, true); 777 d.addEventListener("mouseout", function () { 778 if (hoverTimer) { clearTimeout(hoverTimer); hoverTimer = null; } 779 }, true); 780 } 781 782 // fe-1133 783 var selTimer = null; 784 var seenSel = {}; 785 d.addEventListener("selectionchange", function () { 786 if (selTimer) clearTimeout(selTimer); 787 selTimer = setTimeout(function () { 788 var sel = w.getSelection ? w.getSelection() : null; 789 if (!sel) return; 790 var txt = String(sel).replace(/\s+/g, " ").trim(); 791 if (txt.length < 5) return; 792 var node = sel.anchorNode; 793 if (noTrack(node)) return; 794 var short = txt.slice(0, SELECT_CAP); 795 if (seenSel[short]) return; 796 seenSel[short] = 1; 797 ix("select", { s: sectionOf(node), txt: short, len: txt.length }); 798 }, SELECT_DEBOUNCE_MS); 799 }); 800 801 // fe-1134 802 function videoEvent(e, action) { 803 var v = e.target; 804 if (!v || (v.tagName || "").toLowerCase() !== "video" || noTrack(v)) return; 805 ix("video", { 806 s: sectionOf(v), 807 act: action, 808 src: (v.currentSrc || v.getAttribute("src") || "").split("/").pop().slice(0, 120), 809 id: (v.id || "").slice(0, 60), 810 at: Math.round(v.currentTime || 0), 811 dur: Math.round(v.duration || 0) 812 }); 813 } 814 d.addEventListener("play", function (e) { videoEvent(e, "play"); }, true); 815 d.addEventListener("pause", function (e) { if (!e.target.ended) videoEvent(e, "pause"); }, true); 816 d.addEventListener("ended", function (e) { videoEvent(e, "ended"); }, true); 817 818 // fe-1135 819 var searchTimer = null; 820 d.addEventListener("input", function (e) { 821 lastActivity = Date.now(); 822 var t = e.target; 823 if (!t || noTrack(t)) return; 824 var name = (t.name || "").toLowerCase(); 825 var id = (t.id || "").toLowerCase(); 826 if (!(t.type === "search" || id.indexOf("search") > -1 || name.indexOf("search") > -1)) return; 827 if (searchTimer) clearTimeout(searchTimer); 828 searchTimer = setTimeout(function () { 829 var q = (t.value || "").trim(); 830 if (q.length < 2) return; 831 ix("search", { s: sectionOf(t), id: (t.id || "").slice(0, 60), q: q.slice(0, QUERY_CAP) }); 832 }, SEARCH_DEBOUNCE_MS); 833 }, true); 834 835 // fe-1136 836 function stampForm(f) { 837 if (!f || !f.querySelectorAll) return; 838 var pairs = [["[data-zk-vid]", vid], 839 ["[data-zk-aff]", (affFirst && affFirst.code) || ""], 840 ["[data-zk-cid]", (affFirst && affFirst.campaign_id) || ""], 841 ["[data-zk-chan]", arrival.channel + "/" + arrival.channel_type]];
842 pairs.forEach(function (pair) { 843 var nodes = f.querySelectorAll(pair[0]); 844 for (var i = 0; i < nodes.length; i++) { nodes[i].value = pair[1]; } 845 }); 846 } 847 function stampAllForms() { 848 var forms = d.querySelectorAll("form"); 849 for (var i = 0; i < forms.length; i++) { stampForm(forms[i]); } 850 } 851 852 // fe-1137 853 d.addEventListener("submit", function (e) { 854 var f = e.target; 855 if (!f || noTrack(f)) return; 856 var kind = f.getAttribute ? (f.getAttribute("data-zk-conv") || "") : ""; 857 stampForm(f); 858 // fe-1138 859 var valid = !f.checkValidity || f.checkValidity(); 860 if (kind && valid) { 861 conversion(kind, { s: sectionOf(f), form: String(f.id || f.className || "").slice(0, 60) }); 862 return; 863 } 864 ix("form", { s: sectionOf(f), id: (f.id || "").slice(0, 60), 865 act: (f.getAttribute("action") || "").slice(0, 255), 866 conv: kind, invalid: !valid }); 867 flush("submit"); 868 }, true); 869 870 // fe-1139 871 ["mousemove", "keydown", "touchstart", "wheel", "pointerdown"].forEach(function (n) { 872 w.addEventListener(n, function () { lastActivity = Date.now(); noteHumanInput(); }, { passive: true }); 873 }); 874 setInterval(function () { 875 if (d.visibilityState === "hidden") return; 876 if (Date.now() - lastActivity > IDLE_MS) return; 877 engagedMs += 1000; 878 }, 1000); 879 880 // fe-1140 881 d.addEventListener("visibilitychange", function () { 882 if (d.visibilityState === "hidden") flush("hidden"); 883 }); 884 w.addEventListener("pagehide", function () { flush("pagehide"); }); 885 setInterval(function () { if (buf.length) flush("interval"); }, FLUSH_MS); 886 887 // fe-1141 888 w.zkTrack = track; 889 w.zkTrack.link = link; 890 w.zkTrack.steps = STEPS; 891 w.zkTrack.param = function (k) { return Q[k] || ""; }; 892 w.zkTrack.vid = function () { return vid; }; 893 w.zkTrack.campaign = function () { return { first: first || null, last: last || null }; }; 894 // fe-1142 895 w.zkTrack.conversion = conversion; 896 w.zkTrack.conversions = CONVERSIONS; 897 w.zkTrack.ix = ix; 898 w.zkTrack.flush = flush; 899 w.zkTrack.arrival = function () { return { session: arrival, first: arrivalFirst }; }; 900 w.zkTrack.affiliate = affiliateBlock; 901 w.zkTrack.visitor = function () { return visitor; }; 902 // fe-1143 903 w.zkTrack.rescan = function () { observeSections(); stampAllForms(); }; 904 905 // fe-1144 906 track("page_view", { 907 from_step: Q.zkc || "", 908 entry: !d.referrer, 909 channel: arrival.channel, 910 channel_type: arrival.channel_type, 911 paid: arrival.paid, 912 landing: arrival.landing, 913 campaign_link: !!(fromUrl || affUrl), 914 affiliate_code: (affFirst && affFirst.code) || "" 915 }); 916 917 if (d.readyState === "loading") { 918 d.addEventListener("DOMContentLoaded", function () { observeSections(); stampAllForms(); }); 919 } else { 920 observeSections(); 921 stampAllForms(); 922 } 923 924 // fe-1145 925 d.addEventListener("click", function (e) { 926 var a = e.target && e.target.closest ? e.target.closest("a[href]") : null; 927 if (!a || a.getAttribute("data-zkc-linked")) return; 928 var href = a.getAttribute("href") || ""; 929 if (href.slice(0, 4) === "tel:") { conversion("phone_click", { href: href.slice(0, 60) }); return; } 930 var isSignup = href.indexOf("signup.html") > -1 || href.indexOf("/signup") > -1; 931 var isLogin = href.indexOf("/app/login") > -1 || href.indexOf("/login") > -1; 932 if (!isSignup && !isLogin) return; 933 if (/^(https?:)?\/\//i.test(href) && href.indexOf(w.location.host) < 0) return; // fe-1146 934 a.setAttribute("data-zkc-linked", "1"); 935 a.href = link(href, isSignup ? "qo_signup_click" : "cv_login_click"); 936 conversion(isSignup ? "signup_click" : "login_click", 937 { from: w.location.pathname.split("/").pop() || "index.html" }); 938 }, true); 939 940 /* fe-1147 */ 941})(window, document);
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.