/* only run JS code when all DOM & content is loaded */ window.addEventListener('load', function() { var agent = navigator.userAgent; var domain = window.location.hostname; var page = window.location.pathname; var timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; var scrollPercentage = 0; var sessionId = sessionSet(); var browserId = browserSet(); var referrer = document.referrer; var endpoint = "https://icnsquid.work/api/UKbkUbuiYTc"; // Set to false to disable checkout-specific event mapping. var enableCheckoutFormLogic = false; if (referrer === '') { referrer = 'direct'; } function isPageRefreshed() { return performance.navigation.type === 1; } /* get source of user i.e ppc */ var queryString = window.location.search; var urlParams = new URLSearchParams(queryString); /*var squidSource = urlParams.get('squid_source');*/ var squidSource = urlParams.get('utm_source'); if(squidSource) { localStorage.setItem('squid_source', squidSource); } else { squidSource = localStorage.getItem('squid_source') ?? 'none'; } var sources = JSON.parse(localStorage.getItem('squid_sources')) ?? []; if(urlParams.has('squid_source')) { squidSource = urlParams.get('squid_source'); if(!sources.includes(squidSource)) { sources.push(squidSource); localStorage.setItem('squid_sources', JSON.stringify(sources)); } } /* get email campaign if exists */ var squidEmail = urlParams.get('squid_email'); if(squidEmail) { localStorage.setItem('squid_email', squidEmail); } else { squidEmail = localStorage.getItem('squid_email') ?? null; } /* generate IDs */ function randomString(length) { var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; var result = ''; for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]; return result; } /* fetch or set sessionId */ function sessionSet() { var sessionId = localStorage.getItem('session_id'); if(!sessionId) { sessionId = randomString(24); var fetchPromise = fetch("https://icnsquid.work/api/checksessionid/"+sessionId); fetchPromise.then(response => { return response.json(); }).then(res => { if(!res.OK) { sessionId = res.session_id; } }); localStorage.setItem('session_id', sessionId); } return sessionId; } /* fetch or set browserId */ function browserSet() { var browserId = localStorage.getItem('browser_id'); if(!browserId) { browserId = randomString(24); var fetchPromise = fetch("https://icnsquid.work/api/checkbrowserid/"+browserId); fetchPromise.then(response => { return response.json(); }).then(res => { if(!res.OK) { browserId = res.browser_id; } }); localStorage.setItem('browser_id', browserId); } return browserId; } /* page views */ function pageView() { setLatestEvent(); if (!isPageRefreshed()) { uniqueData = { category: 'page_view' } sendData(uniqueData); } } /* Track link clicks */ var links = document.getElementsByTagName("a"); for(var link of links) { link.addEventListener("click", function (e) { setLatestEvent(); sendData({ category: 'link', link: e.target.href, }); }); } /* Track button clicks */ var buttons = document.getElementsByTagName("button"); for(var button of buttons) { button.addEventListener("click", function (e) { setLatestEvent(); sendData({ category: 'button', button_text: e.target.innerText, }); }); } // Track Senso iframe Forms [!!] Doesn't work because of Cross-origin restrictions imposed by the external host of the source // var iframes = document.getElementsByTagName("iframe"); // for(var iframe of iframes) { // // Get the document object of the iframe // var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; // // Find all form elements within the iframe document // var formElements = iframeDocument.getElementsByTagName("form"); // // Add event listener for all iframe forms // for(var ifrForm of formElements) { // ifrForm.addEventListener("submit", function (e) { // setLatestEvent(); // e.preventDefault(); // getFormData(e.target); // }); // } // } /* track form submits */ var forms = document.getElementsByTagName("form"); for(var form of forms) { form.addEventListener("submit", function (e) { // Do NOT prevent default. Just capture data. setLatestEvent(); sendData(getFormData(e.target), true); }); } /* get form fill data */ function getFormData(form) { var formName = form.id; var formData = {}; Object.assign(formData, {form_class: formName}); var inputs = form.getElementsByTagName('INPUT'); var textAreas = form.getElementsByTagName('TEXTAREA'); for(var input of inputs) { var labels = input.labels; for(var label of Object.keys(labels || {})) { var labelName = labels[label].textContent; formData[labelName] = input.value; } } for(var textArea of textAreas) { var labels = textArea.labels; for(var label of Object.keys(labels || {})) { var labelName = labels[label].textContent; formData[labelName] = textArea.value; } } if (!enableCheckoutFormLogic) { return { category: 'form', form: formData, }; } function getFieldValue(patterns) { var escaped = patterns.map(function(pattern) { return pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); }); var selector = escaped.map(function(pattern) { return '[name*="' + pattern + '" i], [id*="' + pattern + '" i]'; }).join(', '); var el = form.querySelector(selector); return el ? el.value : null; } function toNumber(value) { if (value == null) return null; var cleaned = String(value).replace(/[^0-9.,-]/g, ''); // Handle "1,234.56" and "1234,56" if (cleaned.indexOf(',') > -1 && cleaned.indexOf('.') > -1) { cleaned = cleaned.replace(/,/g, ''); } else if (cleaned.indexOf(',') > -1 && cleaned.indexOf('.') === -1) { cleaned = cleaned.replace(',', '.'); } var parsed = parseFloat(cleaned); return Number.isFinite(parsed) ? parsed : null; } function normalizeCurrency(value) { if (!value) return null; var currency = String(value).trim(); if (currency === '£') return 'GBP'; if (currency === '$') return 'USD'; if (currency === '€') return 'EUR'; if (currency.length === 3) return currency.toUpperCase(); return currency.toUpperCase(); } function getCheckoutFromTable() { var table = document.querySelector('table.shop_table.woocommerce-checkout-review-order-table'); if (!table) return null; var totalRow = table.querySelector('tfoot tr.order-total'); if (!totalRow) return null; var currencySymbolEl = totalRow.querySelector('.woocommerce-Price-currencySymbol'); var currencyRaw = currencySymbolEl ? currencySymbolEl.textContent : null; var currency = normalizeCurrency(currencyRaw); var amountEl = totalRow.querySelector('.woocommerce-Price-amount bdi') || totalRow.querySelector('.woocommerce-Price-amount'); var amountRaw = amountEl ? amountEl.textContent : null; var amount = toNumber(amountRaw); if (amount === null && !currency) return null; return { original_payment_amount: amount, original_payment_currency: currency }; } var checkoutFromTable = getCheckoutFromTable(); var checkoutConvertedAmount = toNumber(getFieldValue(['converted_payment_amount'])); var checkoutOriginalCurrency = normalizeCurrency(getFieldValue(['original_payment_currency'])); var checkoutOriginalAmount = toNumber(getFieldValue(['original_payment_amount'])); var checkoutEmail = getFieldValue(['email']) || null; var checkoutFormId = form.id || form.name || 'checkout_form'; if (!checkoutEmail) { var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; for (var formKey in formData) { if (!Object.prototype.hasOwnProperty.call(formData, formKey)) continue; var formValue = formData[formKey]; if (typeof formValue === 'string' && emailRegex.test(formValue.trim())) { checkoutEmail = formValue.trim(); break; } } } if (checkoutFromTable) { checkoutOriginalAmount = checkoutFromTable.original_payment_amount; checkoutOriginalCurrency = checkoutFromTable.original_payment_currency; } if (checkoutOriginalCurrency === 'GBP' && checkoutOriginalAmount !== null) { checkoutConvertedAmount = checkoutOriginalAmount; } var isCheckoutForm = ( checkoutConvertedAmount !== null || checkoutOriginalAmount !== null || (checkoutOriginalCurrency && checkoutOriginalCurrency.length > 0) ); if (isCheckoutForm) { return { category: 'checkout_form', form_id: checkoutFormId, email: checkoutEmail, converted_payment_amount: checkoutConvertedAmount, original_payment_currency: checkoutOriginalCurrency, original_payment_amount: checkoutOriginalAmount, form: formData }; } return { category: 'form', form: formData, }; } /** * track page scrolls * get current browser viewpane heigtht */ function getWindowHeight() { return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0; } /** * get current absolute window scroll position */ function getWindowYScroll() { return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop || 0; } /** * get current absolute document height */ function getDocHeight() { return Math.max( document.body.scrollHeight || 0, document.documentElement.scrollHeight || 0, document.body.offsetHeight || 0, document.documentElement.offsetHeight || 0, document.body.clientHeight || 0, document.documentElement.clientHeight || 0 ); } /** * get current vertical scroll percentage */ function getScrollPercentage() { return ( (getWindowYScroll() + getWindowHeight()) / getDocHeight() ) * 100; } var throttledListener = throttle(scrollListener, 25); window.addEventListener('scroll', throttledListener, {passive: true}); function throttle(func, delay) { // allows [func] to run once every [delay] ms var func = func.bind(func), last = Date.now(); return function() { if (Date.now() - last > delay) { func(); last = Date.now(); } } } function scrollListener() { if(scrollPercentage < Math.round(getScrollPercentage())) { scrollPercentage = Math.round(getScrollPercentage()); var threshold = document.body.clientHeight * 0.6; if (window.pageYOffset >= threshold) { window.removeEventListener('scroll', throttledListener); } sendData({ category: 'scroll', scroll_percentage: scrollPercentage, }); } } /* track video */ var timer = null; var totalTime = 0; var players = document.getElementsByTagName("video"); for(var player of players) { player.addEventListener("play", startPlaying); player.addEventListener("pause", pausePlaying); } function startPlaying() { timer = window.setInterval(function() { totalTime += 10; }, 10); } function pausePlaying() { if (timer) clearInterval(timer); sendData({ category: 'video', 'duration': totalTime }); } /* reset session_id */ function sessionReset() { localStorage.removeItem("session_id"); sessionId = sessionSet(); } /* track last event time */ function setLatestEvent() { var now = Date.now(); var latestEvent = localStorage.getItem("last_event") || now; if(now - latestEvent > 1800000) { sessionReset(); } localStorage.setItem("last_event", now); } /* send all data to SQUID */ async function sendData(uniqueData, preferBeacon) { eventData = { 'squid': { time: Date.now(), timezone: timezone, page: page, domain: domain, referrer: referrer, source: squidSource, sources: sources, email: squidEmail, user_agent: agent, session_id: sessionId, browser_id: browserId } } Object.assign(eventData['squid'], uniqueData); console.log(eventData); var headersList = { "Accept": "*/*", "Content-Type": "application/json" } var blob = new Blob([JSON.stringify(eventData)], { type: 'application/json; charset=UTF-8' }); var payload = JSON.stringify(eventData); if (preferBeacon && navigator.sendBeacon) { // sendBeacon is best for form submits/page unload if (navigator.sendBeacon(endpoint, payload)) { return; } } try { var response = await fetch(endpoint, { method: "POST", body: blob, headers: headersList, keepalive: !!preferBeacon }); var res = await response.text(); console.log(res); } catch (error) { console.log('SQUID send failed', error); } } pageView(); });