function initSquidTracking() { 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"; // Enable checkout-specific event mapping. var enableCheckoutFormLogic = true; 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); // }); // } // } var pendingForms = {}; // Holds form data while waiting for WPForms AJAX to finish function getFormKey(formEl) { return formEl.id || formEl.getAttribute('data-formid') || formEl.getAttribute('data-form-id') || formEl.getAttribute('data-form') || 'unknown'; } /* 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(); var formEl = e.target; var formKey = getFormKey(formEl); var formEventData = getFormData(formEl); if (!formEventData) { return; } if (formEl.classList.contains('wpforms-ajax-form')) { // Hold the data in memory until WPForms reports success/fail. pendingForms[formKey] = formEventData; } else { // Non-AJAX forms: send immediately as success. formEventData.success = true; sendData(formEventData, true); } }); } if (typeof jQuery !== 'undefined') { // Form Success jQuery(document).on('wpformsAjaxSubmitSuccess', 'form.wpforms-ajax-form', function(e) { var formEl = e.target; var formKey = getFormKey(formEl); var formEventData = pendingForms[formKey] || getFormData(formEl); if (!formEventData) return; formEventData.success = true; sendData(formEventData, true); delete pendingForms[formKey]; }); // Form Failed (validation error, captcha failed, etc.) jQuery(document).on('wpformsAjaxSubmitFailed wpformsAjaxSubmitError', 'form.wpforms-ajax-form', function(e) { var formEl = e.target; var formKey = getFormKey(formEl); var formEventData = pendingForms[formKey] || getFormData(formEl); if (!formEventData) return; formEventData.success = false; sendData(formEventData, true); delete pendingForms[formKey]; }); } /* get form fill data */ function getFormData(form) { var formName = form.id; var formData = {}; Object.assign(formData, {form_class: formName}); function getFormIdentifier(formEl) { var id = (formEl.id || '').trim(); if (id) return id; var nameAttr = (formEl.getAttribute('name') || '').trim(); if (nameAttr) return nameAttr; var dataId = ( formEl.getAttribute('data-form-id') || formEl.getAttribute('data-formid') || formEl.getAttribute('data-form') || '' ).trim(); if (dataId) return dataId; var ariaLabel = (formEl.getAttribute('aria-label') || '').trim(); if (ariaLabel) return ariaLabel; var title = (formEl.getAttribute('title') || '').trim(); if (title) return title; var legend = ''; var legendEl = formEl.querySelector('fieldset legend'); if (legendEl && legendEl.textContent) { legend = legendEl.textContent.trim(); } if (legend) return legend; var action = (formEl.getAttribute('action') || '').trim(); if (action) return action; var className = (formEl.className || '').trim(); if (className) return className; var formsList = Array.prototype.slice.call(document.getElementsByTagName('form')); var idx = formsList.indexOf(formEl); return idx >= 0 ? 'form_' + (idx + 1) : 'form_unknown'; } var formIdentifier = getFormIdentifier(form); var inputs = form.getElementsByTagName('INPUT'); var textAreas = form.getElementsByTagName('TEXTAREA'); var fieldCounter = 0; function setFieldValue(key, value) { var normalizedKey = String(key || '').trim(); if (!normalizedKey) { fieldCounter += 1; normalizedKey = 'field_' + fieldCounter; } formData[normalizedKey] = value; } function getFallbackFieldName(field) { return field.getAttribute('name') || field.id || field.getAttribute('placeholder') || field.type || ''; } 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 getEmailFromFormData() { var directEmail = getFieldValue(['email']); if (directEmail && String(directEmail).trim() !== '') { return String(directEmail).trim(); } 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())) { return formValue.trim(); } } return null; } for(var input of inputs) { var labels = input.labels; var hasLabels = !!(labels && labels.length); for(var label of Object.keys(labels || {})) { var labelName = labels[label].textContent; if (!labelName || String(labelName).trim() === '') continue; setFieldValue(labelName, input.value); } if (!hasLabels) { setFieldValue(getFallbackFieldName(input), input.value); } } for(var textArea of textAreas) { var labels = textArea.labels; var hasLabels = !!(labels && labels.length); for(var label of Object.keys(labels || {})) { var labelName = labels[label].textContent; if (!labelName || String(labelName).trim() === '') continue; setFieldValue(labelName, textArea.value); } if (!hasLabels) { setFieldValue(getFallbackFieldName(textArea), textArea.value); } } var extractedEmail = getEmailFromFormData(); if (!enableCheckoutFormLogic) { return { category: 'form', email: extractedEmail, form_id: formIdentifier, form: formData, }; } 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 = extractedEmail; var checkoutFormId = formIdentifier || 'checkout_form'; var formId = (form.id || '').toLowerCase(); var formNameAttr = (form.getAttribute('name') || '').toLowerCase(); var formClass = (form.className || '').toLowerCase(); var isCheckoutContext = ( page.indexOf('/checkout') !== -1 || formId.indexOf('checkout') !== -1 || formNameAttr.indexOf('checkout') !== -1 || formClass.indexOf('checkout') !== -1 || checkoutFromTable !== null ); 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 = ( isCheckoutContext || checkoutConvertedAmount !== null || checkoutOriginalAmount !== null || (checkoutOriginalCurrency && checkoutOriginalCurrency.length > 0) ); if (isCheckoutForm) { return { category: 'checkout_form', form_id: checkoutFormId, email: checkoutEmail, converted_payment_amount: null, original_payment_currency: null, original_payment_amount: null, form: formData }; } // Non-checkout forms continue to use generic form tracking. return { category: 'form', email: extractedEmail, form_id: formIdentifier, 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(); } // Check if the page is already loaded if (document.readyState === 'complete' || document.readyState === 'interactive') { // The load event already fired, run immediately initSquidTracking(); } else { // The page is still loading, wait for it window.addEventListener('load', initSquidTracking); }