(function(){ const zoovuId = 'aQuj1T9D0LFX5zjW3jG+zAekRM5lPlyR'; const domainId = 'fc3090e7-7f80-4597-9931-13fcec996d19'; const variables = [{"id":"a8672eaa-5b4a-4281-a630-e6d6db7dbffb","name":"Cart SKU","type":"FUNCTION","valueType":"TEXT","function":function(event){const productTileElement = event.target.closest('[data-ref*="minicart-item-"]') return productTileElement?.getAttribute('data-ref').trim().replace('minicart-item-', '').trim();}},{"id":"093bb4d2-ed7f-43fa-8742-5bfcfad1bc9f","name":"emptyArray","type":"FUNCTION","valueType":"LIST","function":function(event){return [] }},{"id":"2be9ee3a-4184-41eb-89b2-06527db40a0b","name":"isPDPpage","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){return location.href.includes("/p/"); }},{"id":"35b8592e-c54f-419f-892b-77189851c864","name":"IsSearchBarEmpty","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){return event.target.value === ""}},{"id":"6c77635c-e95a-4eca-8086-f6be1b1193e8","name":"Named referral: Search","type":"CONSTANT","valueType":"TEXT","value":"Search"},{"id":"f19cba8d-3182-44b7-84bd-f22c4b11ab5d","name":"OW ATC Click","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){let addToCartClicked = false; // const addToCartButton = document.querySelector('button.addToCart'); const addToCartButton = event.srcElement.parentElement.parentElement; if (addToCartButton) { addToCartButton.addEventListener('click', () => { addToCartClicked = true; }); } else { console.warn("Add to Cart button not found."); } return addToCartClicked !== null ? true : false}},{"id":"ecac9340-fc7b-4fac-9e63-9748b779f755","name":"OW Cart decrease quantity","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const increaseButton = event.target.closest('button[aria-label="subtract"]') return increaseButton !== null ? true : false}},{"id":"ead710bb-5fec-4bc9-a102-099fad10da57","name":"OW Cart increase quantity","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const increaseButton = event.target.closest('button[aria-label="add"]') return increaseButton !== null ? true : false}},{"id":"24b153eb-092e-45e7-af50-391e7bec1337","name":"OW Cart Price","type":"FUNCTION","valueType":"DECIMAL","function":function(event){const cartPrice = event.target.closest('div[data-ref*="minicart-item-"]') return getNumericPriceFromString(cartPrice.querySelector('label[aria-label="price"]').textContent)}},{"id":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","name":"OW Currency","type":"CONSTANT","valueType":"TEXT","value":"AUD"},{"id":"48afe234-ae40-4192-af5c-4a1476e8392d","name":"OW Delete from cart","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const deleteButton = event.target.closest('button[aria-label="Delete"]') return deleteButton !== null ? true : false}},{"id":"016cae04-1a11-43e6-889b-f4364cd71986","name":"OW Empty Result Page","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const emptyResultHeader = document.querySelector('div[data-ref="search-no-results"]') return emptyResultHeader !== null ? true : false }},{"id":"62efe916-9a34-407a-918c-945a5d34c910","name":"OW Image Click Search","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const imageClickSearch = event.target.closest('div[class*="styles__ProductImageWrapper"]') return imageClickSearch !== null ? true : false}},{"id":"714e5583-09c7-40ee-953b-4f659b6fb652","name":"OW Locale","type":"CONSTANT","valueType":"TEXT","value":"en-AU"},{"id":"9b212d72-fef3-4657-b789-93286a3306c4","name":"OW Order Number","type":"FUNCTION","valueType":"TEXT","function":function(event){return document.querySelector('[data-ref="order-number"]')?.textContent.trim().replace('Order number ', '').trim();}},{"id":"6e149a7a-ada0-42ce-8921-89ded556be31","name":"OW Price","type":"FUNCTION","valueType":"DECIMAL","function":function(event){const buttonElem = event.srcElement.parentElement.parentElement; if (buttonElem.dataset.ref) { console.log("jacek", buttonElem) const priceString = buttonElem.closest('[data-ref^="product-tile"]').querySelector('[data-ref^="product-price"]').innerHTML.replace("$", "").replace(",", ""); getNumericPriceFromString(priceString) } const buttonIsSmall = buttonElem.classList.contains("sm"); if (buttonIsSmall) { const priceString = event.srcElement.parentElement?.parentElement?.parentElement?.parentElement?.querySelector('[data-testid="price"]').innerHTML.replace("$").replace(","); return getNumericPriceFromString(priceString); } return getNumericPriceFromString(document.querySelector('[class*="style__StyledPrice"]')?.textContent);}},{"id":"cf97fb64-e2b4-46a3-8873-2b27d857d74e","name":"OW Purchase Confirmation Check","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const purchaseCheck = document.querySelector('[data-ref="order-number"]') return purchaseCheck !== null ? true : false}},{"id":"82f218dd-b7e5-43f0-85ac-3b7537f7906f","name":"OW Purchased SKU","type":"FUNCTION","valueType":"LIST","function":function(event){const getRowDataRefs = (elements) => { const rowRefs = []; for (const el of elements) { const dataRef = el.getAttribute('data-ref'); if (dataRef && dataRef.startsWith('row_')) { rowRefs.push(dataRef.replace('row_','')); } } return rowRefs; }; // Now call it const container = document.querySelector('[data-ref="confirmation-cart"]'); const refs = getRowDataRefs(container.children); return refs;}},{"id":"23cea9dd-97c7-4c51-83e7-56ffafc88e48","name":"OW Search Check","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const searchCheck = document.querySelector('div[class*="ProductViewBar__SearchViewBarHeader"]') console.log("zoovu search check", searchCheck) return searchCheck !== null ? true : false}},{"id":"8affbb36-834b-4523-85e7-d117544fb89c","name":"OW Search Image Target URL","type":"FUNCTION","valueType":"TEXT","function":function(event){const imageLink = event.target.parentElement.getAttribute("href") return imageLink }},{"id":"8874cdf1-75c8-4c1f-82cf-7588fe6dfee8","name":"OW Search Title Click","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const titleClickSearch = event.srcElement.parentElement.tagName === "H5" console.log("zoovu click", event.srcElement, titleClickSearch) return titleClickSearch}},{"id":"fb9f91fc-cfd1-412a-8350-81ffa9aa38a1","name":"OW Search Title Target URL","type":"FUNCTION","valueType":"TEXT","function":function(event){const searchTitleLink = event.srcElement.closest("a").href console.log("zoovu title link" , searchTitleLink) return searchTitleLink }},{"id":"a0342ccc-3578-45c4-ae9c-6f86cc69b5ea","name":"OW SKU","type":"FUNCTION","valueType":"TEXT","function":function(event){const buttonElem = event.srcElement.parentElement.parentElement; if (buttonElem.dataset.ref) { return buttonElem.dataset.ref.replace("add-to-cart-button-", "") } const buttonIsSmall = buttonElem.classList.contains("sm"); if (buttonIsSmall) { return event.srcElement.parentElement.parentElement.parentElement.parentElement.ariaLabel.replace("product-tile-", "") } else { const skuElement = Array.from(document.querySelectorAll('[class^="style__Detail"]')) .find(el => el.textContent.includes("Product code:")); return skuElement?.querySelector('strong')?.textContent; } }},{"id":"d3c4a231-6f49-4739-abfe-b713b02c0ae7","name":"plp price","type":"FUNCTION","valueType":"DECIMAL","function":function(event){const priceString= event.target.parentElement?.querySelector('[data-testid="price"]').innerHTML.replace("$").replace(","); return getNumericPriceFromString(priceString);}},{"id":"2a8277a9-203a-4fb1-99fa-24b888c90778","name":"plp sku","type":"FUNCTION","valueType":"TEXT","function":function(event){return event.target.parentElement?.querySelector('[aria-label^="product-tile-"]').ariaLabel.replace("product-tile-", "")}},{"id":"5a132ef8-5940-4b98-96bc-f14f6738faed","name":"Quantity -1","type":"CONSTANT","valueType":"INTEGER","value":"-1"}]; const script = {"rows":[{"id":"84f126c8-13a4-494c-81f2-93547c134582","rowType":"EVENT","trigger":{"id":"a093f879-7f05-44fd-a7ca-85b4199d015b","name":"OW Search Bar","type":"INPUT","groups":[{"id":"ff27dfb3-4883-4420-8e6b-aa7519dcc60f","rows":[{"valueType":"BOOLEAN","id":"ae35ee02-a9dc-444f-8bde-22b6ff01432c","variableId":"35b8592e-c54f-419f-892b-77189851c864","value":"false","operator":"EQUALS"}]}]},"action":{"type":"SEARCH"},"fields":[{"id":"5d94044e-a3b8-45bb-ae2d-1b3d43ae031c","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"}]},{"id":"5011c85f-1fd2-4e99-8c2f-dc13b0778051","rowType":"EVENT","trigger":{"id":"b12ce1e4-97fe-4d37-8a31-3d2bce300cf8","name":"OW Remove From Cart","type":"CLICK","groups":[{"id":"7c023b88-8e64-4294-b1ee-eb4d8e9dc9a1","rows":[{"valueType":"BOOLEAN","id":"fcc58f59-60dd-4688-843c-dac004409851","variableId":"48afe234-ae40-4192-af5c-4a1476e8392d","value":"true","operator":"EQUALS"}]}]},"action":{"type":"REMOVE_FROM_CART"},"fields":[{"id":"ad9add22-f464-409d-a839-d779b5d48e9f","variableId":"a8672eaa-5b4a-4281-a630-e6d6db7dbffb","fieldName":"sku"},{"id":"a0bcb5e3-d3ce-45df-a55d-bfa04ad2bc40","variableId":"24b153eb-092e-45e7-af50-391e7bec1337","fieldName":"price"},{"id":"b71a9c25-272d-4984-b61a-31fbb7362199","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"},{"id":"a7025234-a3d4-4cc3-856e-6eedaa839433","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"}]},{"id":"70ae4f92-f683-48ce-84e3-d1c27035eed6","rowType":"EVENT","trigger":{"id":"7497b2c4-39dc-4a2b-9093-628b42ed820b","name":"OW Empty Result","type":"PAGE_LOAD","groups":[{"id":"ce24e4b0-f9a9-43b3-a35a-e646c9ae93da","rows":[{"valueType":"BOOLEAN","id":"e907bea8-802e-4385-9255-8b488f2a9cb4","variableId":"016cae04-1a11-43e6-889b-f4364cd71986","value":"true","operator":"EQUALS"}]}]},"action":{"type":"SEARCH_RESULT"},"fields":[{"id":"47c7aebc-8b25-416c-bde2-b52aecb2dace","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"bbc227c4-18ea-41b9-bf6c-988516421840","variableId":"016cae04-1a11-43e6-889b-f4364cd71986","fieldName":"isEmpty"},{"id":"0abcfddf-a7b9-4eca-a556-c9ee14b31430","variableId":"093bb4d2-ed7f-43fa-8742-5bfcfad1bc9f","fieldName":"skus"}]},{"id":"14dbdd76-e5cf-4646-93ac-f491ccd9968c","rowType":"EVENT","trigger":{"id":"31d9811f-9525-41dc-b54f-d89a4490b228","name":"Cart Increase","type":"CLICK","groups":[{"id":"9f567b1f-6333-4a50-aff3-148f00ced757","rows":[{"valueType":"BOOLEAN","id":"1b65de95-2d35-4e17-83e8-be12fa599b97","variableId":"ead710bb-5fec-4bc9-a102-099fad10da57","value":"true","operator":"EQUALS"}]}]},"action":{"type":"UPDATE_CART"},"fields":[{"id":"25bf47f8-ec85-4aa8-b41d-a4ca63a1ea98","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"02b84a26-8c6f-4289-bbf1-5b87512458af","variableId":"a8672eaa-5b4a-4281-a630-e6d6db7dbffb","fieldName":"sku"},{"id":"3dbdf03b-0a13-4193-9743-8a2fda374601","variableId":"24b153eb-092e-45e7-af50-391e7bec1337","fieldName":"price"},{"id":"43bd8f20-0381-4e3c-8ec3-cb3e8d1fb2b9","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"}]},{"id":"443d5f3d-9129-4a19-985d-a41d756b022c","rowType":"EVENT","trigger":{"id":"ca8e467c-db16-46bb-8a9e-5eda8f21e598","name":"Cart decrease","type":"CLICK","groups":[{"id":"a0ae4923-4fa4-4a4f-a995-9a2432d390f4","rows":[{"valueType":"BOOLEAN","id":"f2b60eab-18e1-4575-980f-9f1f364c389c","variableId":"ecac9340-fc7b-4fac-9e63-9748b779f755","value":"true","operator":"EQUALS"}]}]},"action":{"type":"UPDATE_CART"},"fields":[{"id":"cd0065d3-addb-44a2-be5c-09c6bca5599d","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"64af69ad-66b9-43dd-9415-cd2958d9ddbe","variableId":"a8672eaa-5b4a-4281-a630-e6d6db7dbffb","fieldName":"sku"},{"id":"22742524-888a-48d5-a406-c5165c3ffc2c","variableId":"24b153eb-092e-45e7-af50-391e7bec1337","fieldName":"price"},{"id":"06602126-71cb-4585-900b-5e3596713b4c","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"},{"id":"7f434957-4580-4e25-933b-e50fdcd82111","variableId":"5a132ef8-5940-4b98-96bc-f14f6738faed","fieldName":"quantity"}]},{"id":"580f793e-9b7a-4368-9ae5-c791fa7cd8e4","rowType":"EVENT","trigger":{"id":"acfb1e19-bd49-4ce6-9c3a-7ad7cb78b5a0","name":"OW Search Image Click","type":"CLICK","groups":[{"id":"68a82119-ff80-4d46-a5f7-1312bf63c89a","rows":[{"valueType":"BOOLEAN","id":"c15a073b-429b-40b2-bda9-34efc27713b9","variableId":"23cea9dd-97c7-4c51-83e7-56ffafc88e48","value":"true","operator":"EQUALS"},{"valueType":"BOOLEAN","id":"4b77c393-4bdd-44ed-842b-0cbe1cbc0d89","variableId":"62efe916-9a34-407a-918c-945a5d34c910","value":"true","operator":"EQUALS"}]}]},"action":{"type":"CLICKOUT"},"fields":[{"id":"083df6b2-3e57-4922-8a6d-d0cd3a34f31b","variableId":"6c77635c-e95a-4eca-8086-f6be1b1193e8","fieldName":"namedReferral"},{"id":"29c5e93a-e179-42ac-ac8c-3dd665120f0d","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"00907c56-3726-4a6d-ae07-57205d23487d","variableId":"8affbb36-834b-4523-85e7-d117544fb89c","fieldName":"targetUrl"}]},{"id":"f85f2a3d-6a91-4ec2-a91e-2da90df3d154","rowType":"EVENT","trigger":{"id":"b4ff50e6-6a1c-4533-b885-95dd0918bb5a","name":"OW Search Title Click","type":"CLICK","groups":[{"id":"1016d01d-39e1-4b85-802c-d85fa38f2283","rows":[{"valueType":"BOOLEAN","id":"28ac6aa4-a760-4197-b5d7-10cacfde5bff","variableId":"23cea9dd-97c7-4c51-83e7-56ffafc88e48","value":"true","operator":"EQUALS"},{"valueType":"BOOLEAN","id":"79ed69d5-461c-48e7-b6d9-e3671e164704","variableId":"8874cdf1-75c8-4c1f-82cf-7588fe6dfee8","value":"true","operator":"EQUALS"}]}]},"action":{"type":"CLICKOUT"},"fields":[{"id":"14fb94e3-084a-46ad-8362-1490e8600719","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"21b8b23a-68a9-4a3f-a0cd-e44efdcf73b3","variableId":"fb9f91fc-cfd1-412a-8350-81ffa9aa38a1","fieldName":"targetUrl"},{"id":"0f3fe512-09c2-4b17-bfaf-ce114e565166","variableId":"6c77635c-e95a-4eca-8086-f6be1b1193e8","fieldName":"namedReferral"}]},{"id":"f44a458a-1805-4e98-a426-e4cd266a3565","rowType":"EVENT","trigger":{"id":"a7ab4cdf-df50-4247-a31b-ca85d5992b5b","name":"Purchase Page Check","type":"PAGE_LOAD","groups":[{"id":"4172bbc9-411d-4a97-98b0-76e621975049","rows":[{"valueType":"BOOLEAN","id":"2b567d76-6a91-47da-8f1a-954ae6289551","variableId":"cf97fb64-e2b4-46a3-8873-2b27d857d74e","value":"true","operator":"EQUALS"}]}]},"action":{"type":"PURCHASED"},"fields":[{"id":"eead0822-67c5-4979-ab55-78a813605041","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"},{"id":"87dd9043-4797-4a96-8dc9-93c7c8cad284","variableId":"9b212d72-fef3-4657-b789-93286a3306c4","fieldName":"transactionId"},{"id":"5856cc04-47a8-4830-a5f8-bee8d4fbc44f","variableId":"82f218dd-b7e5-43f0-85ac-3b7537f7906f","fieldName":"products"}]},{"id":"316b8af9-1514-4825-ad9a-e7227f0cb71d","rowType":"EVENT","trigger":{"id":"ed5cc33b-9630-4530-8de9-102f3d4f1630","name":"OW PDP Visited","type":"PAGE_LOAD","groups":[{"id":"0018c4b9-5171-47f3-855e-9940f1787646","rows":[{"valueType":"BOOLEAN","id":"115012a8-b326-4380-80b3-02947ac8dd6c","variableId":"2be9ee3a-4184-41eb-89b2-06527db40a0b","value":"true","operator":"EQUALS"}]}]},"action":{"type":"PDP_VISITED"},"fields":[{"id":"cd872c9d-fcad-40ae-8ad7-bdc6483a5978","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"ae6af31a-fc53-4ceb-8848-9ecc1d255375","variableId":"a0342ccc-3578-45c4-ae9c-6f86cc69b5ea","fieldName":"sku"},{"id":"dc3aaedd-01fe-4a63-9619-ecdd30e114f9","variableId":"6e149a7a-ada0-42ce-8921-89ded556be31","fieldName":"price"},{"id":"c0846a90-33ff-4583-be7f-bbba5a04c4f6","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"}]},{"id":"a2211699-c9a7-45fc-bf54-bfc1bd90212b","rowType":"EVENT","trigger":{"id":"1ee6d655-bd42-42bf-b737-c6b7b3044454","name":"OW Page Load","type":"PAGE_LOAD","groups":[{"id":"659ac57c-aa18-4482-82cf-85cc503890f1","rows":[{"valueType":"BOOLEAN","id":"01a10b93-44e7-4dad-8153-1497304d0fe7","variableId":"2be9ee3a-4184-41eb-89b2-06527db40a0b","value":"false","operator":"EQUALS"}]}]},"action":{"type":"PAGE_VISITED"},"fields":[{"id":"14677c50-bfad-42b7-8f3b-a176beb1ae03","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"}]},{"id":"34b20f83-c1fd-4fd6-8753-ed36a9ebf7c2","rowType":"EVENT","trigger":{"id":"40d0375d-024f-4c18-bf18-309503e2546e","name":"OW ATC PDP Click","type":"CLICK","groups":[{"id":"06db3a9f-d44c-4b38-94b8-e283e3ada7cd","rows":[{"valueType":"BOOLEAN","id":"98591ec4-0b4a-4df2-be30-54cb51185ede","variableId":"f19cba8d-3182-44b7-84bd-f22c4b11ab5d","value":"true","operator":"EQUALS"}]}]},"action":{"type":"ADD_TO_CART"},"fields":[{"id":"6b72e8ec-fb60-4789-8592-57cc25a9836b","variableId":"714e5583-09c7-40ee-953b-4f659b6fb652","fieldName":"locale"},{"id":"60d9e4c9-85fc-4ff1-b27d-183e63e02770","variableId":"a0342ccc-3578-45c4-ae9c-6f86cc69b5ea","fieldName":"sku"},{"id":"31e8a9e8-f0c5-4b99-8093-cf5f8faea948","variableId":"6e149a7a-ada0-42ce-8921-89ded556be31","fieldName":"price"},{"id":"9dba8194-ac00-4a07-a577-b46c9ef3c53a","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"}]},{"id":"6241878a-c771-4349-9d59-56c6de94d42f","rowType":"EVENT","trigger":{"id":"3a1f16f6-de67-435d-9043-ebb773a44d8b","name":"OW Additional ATC Clicks","type":"PAGE_LOAD","groups":[{"id":"4d4af0e7-923e-4980-97ef-f6d3d0691250","rows":[{"valueType":"BOOLEAN","id":"f90505c2-6ef9-44b0-867f-d2124468389a","variableId":"f19cba8d-3182-44b7-84bd-f22c4b11ab5d","value":"true","operator":"EQUALS"}]}]},"action":{"type":"ADD_TO_CART"},"fields":[{"id":"c2128c56-3fc3-4b63-9557-8004f5423228","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"locale"},{"id":"caac1fc7-f9a8-4960-abeb-f0ca9f7815c3","variableId":"2a8277a9-203a-4fb1-99fa-24b888c90778","fieldName":"sku"},{"id":"aadee8b3-47bc-40e5-aeb7-01ff4b5cc1ef","variableId":"d3c4a231-6f49-4739-abfe-b713b02c0ae7","fieldName":"price"},{"id":"3bc15319-173f-43eb-9a6b-ab42ec19c64d","variableId":"d79c5251-b7cd-4b8c-a943-c729909c5dcb","fieldName":"currencyCode"}]}]}; const advancedCode = function(){}; const url = 'https://queue-propagator.zoovu.com'; const currentEnvironment = 'barracuda'; const currentAccountId = 510; // ------------------------- API ------------------------- let trackingEnabled = true; function disableTracking() { trackingEnabled = false; } function enableTracking() { trackingEnabled = true; } function getNumericPriceFromString(stringPrice) { const parsedPrice = stringPrice .replace(/\.$/, '') .replace(/([^.',\s\d])*/g, '') .replace(/([.',\s](?=\d{3}))/g, '') .replace(/([.',](?=\d{2}))/g, '.'); return Number(parsedPrice); } // ------------------------- helpers ------------------------- const eventTypes = Object.freeze({ PAGE_VISITED: "PAGE_VISITED", ADD_TO_CART: "ADD_TO_CART", REMOVE_FROM_CART: "REMOVE_FROM_CART", PDP_VISITED: "PDP_VISITED", PURCHASED: "PURCHASED", UPDATE_CART: "UPDATE_CART", DECLINE_TRACKING: "DECLINE_TRACKING", SEARCH: "SEARCH", SEARCH_RESULT: "SEARCH_RESULT", CLICKOUT: "CLICKOUT" }); const trackingEventTypes = Object.freeze({ TRACKING_SUCCESSFUL_EXECUTION: 'TRACKING_SUCCESSFUL_EXECUTION' }) const actionTypes = Object.freeze({ ...eventTypes, CUSTOM: 'CUSTOM' }) const triggerTypes = Object.freeze({ CLICK: 'CLICK', INPUT: 'INPUT', PAGE_LOAD: 'PAGE_LOAD' }); const variableTypes = Object.freeze({ FUNCTION: 'FUNCTION', CONSTANT: 'CONSTANT' }); const trackingEntityTypes = Object.freeze({ VARIABLE: 'VARIABLE', TRIGGER: 'TRIGGER', CUSTOM_ACTION: 'CUSTOM_ACTION' }) const matchers = Object.freeze({ EQUALS: (a, b) => a === b, DOES_NOT_EQUAL: (a, b) => a !== b, CONTAINS: (a, b) => a.indexOf(b) >= 0, DOES_NOT_CONTAIN: (a, b) => a.indexOf(b) === -1, GREATER_THAN: (a, b) => a > b, LESS_THAN: (a, b) => a < b, GREATER_THAN_OR_EQUAL: (a, b) => a >= b, LESS_THAN_OR_EQUAL: (a, b) => a <= b, MATCHES_CSS_SELECTOR: (target, test) => target.matches(test), MATCHES_ID: (target, test) => target.id === test, CONTAINS_ID: (target, test) => target.id.includes(test), DOES_NOT_CONTAIN_ID: (target, test) => !target.id.includes(test), MATCHES_CLASS: (target, test) => target.className === test, CONTAINS_CLASS: (target, test) => target.className.includes(test), DOES_NOT_CONTAIN_CLASS: (target, test) => !target.className.includes(test), }); const trackingFieldName = `${domainId}_${zoovuId}_successfulExecutions` const MAX_RECORDS = 100 const DEBOUNCE_TIME = 1500 const successfulExecutions = JSON.parse(localStorage.getItem(trackingFieldName)) || localStorage.setItem(trackingFieldName, JSON.stringify([])) function cast(type, value) { switch (type) { case 'BOOLEAN': return value === true || value === 'true'; case 'TEXT': return value; case 'DECIMAL': return typeof value === 'string' ? parseFloat(value) : value; case 'INTEGER': return typeof value === 'string' ? parseInt(value) : value; default: return value; } } function debounce(fn, delay) { let timer = null; return function () { const context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); }; } function getOrganization() { return zoovuId; } function getDomainId() { return domainId; } function getTimezone() { return Intl.DateTimeFormat().resolvedOptions().timeZone; } function getReferrer() { const referrer = document.referrer; if (/^(https?|android-app):\/\//i.test(referrer)) { return referrer; } else { return undefined; } } function getPath() { return window.location.href.replace(window.location.origin, ''); } function getCookieValue(cookieName) { const cookies = document.cookie.split('; '); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].split('='); if (cookie[0] === cookieName) { return cookie[1]; } } return null; } function generateUuid() { return self.crypto.randomUUID(); } function addZoovuCidToCookies() { const uuid = generateUuid(); const zoovuCid = `zoovu-cid=${uuid}; path=/`; document.cookie = zoovuCid; return uuid; } function getCID() { return getCookieValue(`zoovu-cid`) !== null ? getCookieValue(`zoovu-cid`) : addZoovuCidToCookies(); } function getEnvironment() { return currentEnvironment; } function getAccountId() { return currentAccountId; } function getPropertyTypeError(propertyName, expectedType, currentType) { return `TYPE MISMATCH: ${propertyName}: should be ${expectedType} but is ${currentType}` } function typeCheckValue(value, variableType, propertyName) { if (value === undefined || value === null) { return `${propertyName}: value is not defined.` } switch (variableType) { case "TEXT": return typeof value === 'string' ? null : getPropertyTypeError(propertyName, 'string', typeof value); case "INTEGER": case "DECIMAL": return typeof value === 'number' ? null : getPropertyTypeError(propertyName, 'number', typeof value); case "BOOLEAN": return typeof value === 'boolean' ? null : getPropertyTypeError(propertyName, 'boolean', typeof value); case 'LIST': return Array.isArray(value) ? null : getPropertyTypeError(propertyName, 'list', typeof value); case 'EVENT': return null; default: return "Not supported variable type: " + variableType; } } function ensureRequiredFieldsPresent(fields, eventType) { return Object.entries(fields).every(([key, value]) => { if (!value) { throw Error(`REQUIRED PROPERTY MISSING FOR ${eventType}: ${key} not provided. Event won't be sent.`) } return true; }); } function getBaseEventBody(eventType) { return { origin: 'CLIENT', queryParams: {}, organization: getOrganization(), domainId: getDomainId(), path: getPath(), referrer: getReferrer(), cid: getCID(), timezone: getTimezone(), // dynamic eventType: eventType ?? '', eventLabel: '', }; } function getEventExecutable(actionType) { switch (actionType) { case actionTypes.PAGE_VISITED: return sendPageVisitedEvent; case actionTypes.ADD_TO_CART: return sendAddToCartEvent; case actionTypes.CLICKOUT: return sendClickoutEvent; case actionTypes.REMOVE_FROM_CART: return sendRemoveFromCartEvent; case actionTypes.PDP_VISITED: return sendPdpVisitedEvent; case actionTypes.PURCHASED: return sendPurchaseEvent; case actionTypes.DECLINE_TRACKING: return sendDeclineTrackingEvent; case actionTypes.UPDATE_CART: return sendUpdateCartEvent; case actionTypes.SEARCH: return sendSearchEvent; case actionTypes.SEARCH_RESULT: return sendSearchResultEvent; } } function getVariableValueById(variableId, event, fieldName) { const foundVariable = variables.find(v => v.id === variableId); let variableValue = null; if (foundVariable) { if (foundVariable.type === variableTypes.CONSTANT) { variableValue = cast(foundVariable.valueType, foundVariable.value); } else if (foundVariable.type === variableTypes.FUNCTION && typeof foundVariable.function === 'function') { variableValue = foundVariable.function(event); } const errorMessage = typeCheckValue(variableValue, foundVariable.valueType, fieldName ? fieldName : foundVariable.name); if (errorMessage) { throw new Error(errorMessage) } else { return variableValue; } } else { throw new Error(`No variable with ID ${variableId}`) } } function evaluateSingleTriggerRow(row, event) { const target = getVariableValueById(row.variableId, event); const matcher = matchers[row.operator]; const value = cast(row.valueType, row.value); if (matcher && typeof matcher === 'function') { return matcher(target, value); } return false; } function preparePayloadFromFields(fields, event) { const payload = {}; fields.forEach(f => { payload[f.fieldName] = getVariableValueById(f.variableId, event, f.fieldName) }) return payload; } function evaluateTriggerConditions(trigger, event) { // OR between groups return trigger.groups.some(group => // AND between rows group.rows.every(row => evaluateSingleTriggerRow(row, event))) } function runAction(action, fields, event, trigger) { const successfulExecutions = jsonParser(trackingFieldName) if (action.type === actionTypes.CUSTOM && typeof action.code === 'function') { // add script custom action successfulExecutions.push(createTrackingExecutionRecord(trackingEntityTypes.CUSTOM_ACTION, action.id)) action.code(event); localStorage.setItem(trackingFieldName, JSON.stringify(successfulExecutions)); } else { const sendEventFunction = getEventExecutable(action.type); if (sendEventFunction && typeof sendEventFunction === 'function') { const payload = preparePayloadFromFields(fields, event) sendEventFunction(payload); addSuccessfulExecutions(trigger, fields) } } } function evaluateSingleRule(rule, event) { if (evaluateTriggerConditions(rule.trigger, event)) { runAction(rule.action, rule.fields, event, rule.trigger); } } function evaluateRules(rules, event) { try { rules.forEach(rule => { evaluateSingleRule(rule, event) }); } catch (error) { console.debug(error) } } function observeAndReactOnPageChange(callback) { let oldHref; if (oldHref === undefined) { callback(); oldHref = document.location.href; } const body = document.querySelector('body'); const observer = new MutationObserver(() => { if (oldHref !== document.location.href) { oldHref = document.location.href; callback(); } }); observer.observe(body, {childList: true, subtree: true}); } function executeAdvancedCode() { if (typeof advancedCode === 'function') { advancedCode(); } } function jsonParser(key) { const foundValue = JSON.parse(localStorage.getItem(key)) if (!Array.isArray(foundValue)) { throw Error('Invalid value in localStorage for key ' + key) } return foundValue } function createTrackingExecutionRecord(entityType, entityId) { const executionTime = Date.now(); return { organization: getOrganization(), domainId: getDomainId(), eventType: trackingEventTypes.TRACKING_SUCCESSFUL_EXECUTION, trackingEntityType: entityType, trackingEntityId: entityId, executionTime } } function addSuccessfulExecutions(trigger, fields) { const successfulExecutions = jsonParser(trackingFieldName) // add script trigger successfulExecutions.push(createTrackingExecutionRecord(trackingEntityTypes.TRIGGER, trigger.id)) // add script function variables fields.forEach(field => { const foundVariableInField = variables.find(variable => variable.id === field.variableId) if (foundVariableInField.type === variableTypes.FUNCTION) { successfulExecutions.push(createTrackingExecutionRecord(trackingEntityTypes.VARIABLE, foundVariableInField.id)) } }) // add trigger function variables trigger.groups.forEach(groups => { groups.rows.forEach(row => { const foundVariable = variables.find(variable => variable.id === row.variableId) if (foundVariable.type === variableTypes.FUNCTION) { successfulExecutions.push(createTrackingExecutionRecord(trackingEntityTypes.VARIABLE, foundVariable.id)) } }) }) localStorage.setItem(trackingFieldName, JSON.stringify(successfulExecutions)); } // ------------------------- events ------------------------- async function sendEvent(body) { if (trackingEnabled) { await fetch(`${url}/fact`, { method: 'POST', mode: 'cors', cache: 'no-cache', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', }, redirect: 'follow', referrerPolicy: 'no-referrer', body: JSON.stringify(body), }); } else { console.debug("Tracking disabled - no permission to track"); } } async function sendSSTRPurchaseEvent(body) { if (trackingEnabled) { const purchaseUrl = 'https://ev-co.zoovu.com/v1/CollectSalesEvent'; await fetch(purchaseUrl, { method: 'POST', mode: 'cors', cache: 'no-cache', headers: { 'Content-Type': 'application/json', }, redirect: 'follow', referrerPolicy: 'no-referrer', body: JSON.stringify(body), }); } else { console.debug("Tracking disabled - no permission to track"); } } async function sendTrackingSuccessfulExecution(body) { if (trackingEnabled) { await fetch(`${url}/tracking/executions`, { method: 'POST', mode: 'cors', cache: 'no-cache', headers: { 'Content-Type': 'application/json', }, redirect: 'follow', referrerPolicy: 'no-referrer', body: JSON.stringify(body), }); } else { console.debug("Tracking disabled - no permission to track"); } } function sendFactEventBase(eventType, eventSpecificBody) { const body = { ...getBaseEventBody(eventType), // event label + custom per-event fields ...eventSpecificBody, }; sendEvent(body); }; function sendPageVisitedEvent({locale, category, eventLabel = 'Page visit'}) { const body = { locale, eventLabel, // optional category, }; if (ensureRequiredFieldsPresent({locale}, eventTypes.PAGE_VISITED)) { sendFactEventBase(eventTypes.PAGE_VISITED, body); } }; function sendPdpVisitedEvent({ locale, sku, quantity = 1, currencyCode, eventLabel = "Product details page visit", price, category }) { const body = { locale, sku, eventLabel, // optional category, price, currencyCode, quantity, }; if (ensureRequiredFieldsPresent({locale, sku}, eventTypes.PDP_VISITED)) { sendFactEventBase(eventTypes.PDP_VISITED, body); } }; function sendAddToCartEvent({ locale, sku, quantity = 1, currencyCode, eventLabel = "Add to cart", price, category }) { const body = { locale, sku, eventLabel, // optional category, price, currencyCode, quantity, }; if (ensureRequiredFieldsPresent({locale, sku}, eventTypes.ADD_TO_CART)) { sendFactEventBase(eventTypes.ADD_TO_CART, body); } }; function sendRemoveFromCartEvent({ locale, sku, quantity = 1, currencyCode, eventLabel = "Remove from cart", price, category }) { const body = { locale, sku, eventLabel, // optional category, price, currencyCode, quantity, }; if (ensureRequiredFieldsPresent({locale, sku}, eventTypes.REMOVE_FROM_CART)) { sendFactEventBase(eventTypes.REMOVE_FROM_CART, body); } }; function sendUpdateCartEvent({ locale, sku, quantity = 1, currencyCode, eventLabel = "Update cart", price, category }) { const body = { locale, sku, eventLabel, // optional category, price, currencyCode, quantity, }; if (ensureRequiredFieldsPresent({locale, sku}, eventTypes.UPDATE_CART)) { sendFactEventBase(eventTypes.UPDATE_CART, body); } }; function sendDeclineTrackingEvent({ locale, eventLabel = "No permission to track", category }) { const body = { locale, eventLabel, // optional category }; if (ensureRequiredFieldsPresent({locale}, eventTypes.DECLINE_TRACKING)) { sendFactEventBase(eventTypes.DECLINE_TRACKING, body); disableTracking(); } }; function sendSearchEvent({ locale, eventLabel = "Search phrase typed", category }) { const body = { locale, eventLabel, // optional category, }; if (ensureRequiredFieldsPresent({locale}, eventTypes.SEARCH)) { sendFactEventBase(eventTypes.SEARCH, body); } }; function sendSearchResultEvent({ locale, isEmpty = false, eventLabel = "Search results", skus, category }) { const body = { locale, isEmpty, skus, eventLabel, // optional category }; if (ensureRequiredFieldsPresent({locale, skus}, eventTypes.SEARCH_RESULT)) { sendFactEventBase(eventTypes.SEARCH_RESULT, body); } }; function sendClickoutEvent({ locale, targetUrl, namedReferral, eventLabel = "Clickout", category }) { const body = { locale, targetUrl, namedReferral, eventLabel, // optional category }; if (ensureRequiredFieldsPresent({locale, targetUrl, namedReferral}, eventTypes.CLICKOUT)) { sendFactEventBase(eventTypes.CLICKOUT, body); } }; function sendPurchaseEvent({ currencyCode, transactionId, products }) { const sstrBody = { transactionId, products, currency: currencyCode, browserTimestamp: Date.now(), env: getEnvironment(), accountId: getAccountId(), clientId: getCID(), }; if (ensureRequiredFieldsPresent({products, currencyCode,}, eventTypes.PURCHASED)) { sendSSTRPurchaseEvent(sstrBody); } }; function sendSuccessfulExecutionEvent(forceSend) { const successfulExecutions = jsonParser(trackingFieldName) if(successfulExecutions.length === MAX_RECORDS || (forceSend && successfulExecutions.length > 0)) { sendTrackingSuccessfulExecution(successfulExecutions) localStorage.setItem(trackingFieldName, JSON.stringify([])) } } // ------------------------- core ------------------------- function reactOnLoad() { const pageLoadRules = script.rows.filter(row => row.trigger.type === triggerTypes.PAGE_LOAD); window.addEventListener("load", (event) => { observeAndReactOnPageChange(() => evaluateRules(pageLoadRules, event) && sendSuccessfulExecutionEvent()) }, {capture: true}); } function reactOnClick() { const clickRules = script.rows.filter(row => row.trigger.type === triggerTypes.CLICK); // click via mouse document.querySelector('body').addEventListener('mousedown', (event) => { evaluateRules(clickRules, event); sendSuccessfulExecutionEvent() }, {capture: true}); // click via enter button document.querySelector('body').addEventListener('keydown', (event) => { if (event.code === "Enter" || event.code === "NumpadEnter") { evaluateRules(clickRules, event); sendSuccessfulExecutionEvent() } }, {capture: true}); } function reactOnInput() { const inputRules = script.rows.filter(row => row.trigger.type === triggerTypes.INPUT); document.querySelector('body').addEventListener('input', debounce(function (event) { evaluateRules(inputRules, event); sendSuccessfulExecutionEvent() }, DEBOUNCE_TIME), {capture: true}); } function reactOnMouseLeave() { document.addEventListener("mouseleave", function(event){ if (event.clientY <= 0 || event.clientX <= 0 || (event.clientX >= window.innerWidth || event.clientY >= window.innerHeight)) { sendSuccessfulExecutionEvent(true) } }); } function track() { try { executeAdvancedCode(); reactOnLoad(); reactOnClick(); reactOnInput(); reactOnMouseLeave(); } catch (error) { console.debug("Tracking disabled", error); } } // RUN SCRIPT AFTER PAGE LOAD if (document.readyState === "loading" || document.readyState === "interactive") { // Loading hasn't finished yet document.addEventListener("readystatechange", (event) => { if (event.target.readyState === "complete") { track(); } }); } else { track(); } })();