(function(){ const zoovuId = '4URxNrh8JNmrK9dcKVc3sbuRO4DOtkI8'; const domainId = 'fe3694d6-3de9-4862-bcf9-92995cb1f5f4'; const variables = [{"id":"b6cf7400-73a7-4287-ae08-1e85f8113060","name":"CART_PAGE_getNewQuantityOnQuantityChange","type":"FUNCTION","valueType":"INTEGER","function":function(event){const parentControler = event.target.closest(".yourPursache__item__info__wrap"); if (parentControler) { const inputElement = parentControler.querySelector("input.cantidad"); if (inputElement) { let quantity = inputElement.value; quantity = getNumericPriceFromString(quantity); if(event.target.classList.contains("count__controler--more")){ return quantity + 1; }else{ return quantity - 1; } } } return 1; /* */},"scope":"LOCAL"},{"id":"ec6bb905-f9c2-47c3-a2cf-d385287b730f","name":"CART_PAGE_getSkuOnQuantityChange","type":"FUNCTION","valueType":"TEXT","function":function(event){const parentControler = event.target.closest(".yourPursache__item__info__count__wrap"); if (parentControler) { const inputElement = parentControler.querySelector("input.cantidad"); if (inputElement) { const idValue = inputElement.id; const numericPart = idValue.match(/\d+/)[0]; return numericPart; } } /* */},"scope":"LOCAL"},{"id":"45da9e02-5fe0-4165-ac5b-dc57ed0aefb1","name":"Clickout, category","type":"CONSTANT","valueType":"TEXT","value":"Clickout, category","scope":"LOCAL"},{"id":"d50c80e0-3e60-4250-89a9-e3050f07416e","name":"Empty list","type":"FUNCTION","valueType":"LIST","function":function(event){return []; /* */},"scope":"LOCAL"},{"id":"1ecdbee8-e5cd-44a1-908b-337af9b6e218","name":"Empty search result popup","type":"CONSTANT","valueType":"TEXT","value":"Empty search result popup","scope":"LOCAL"},{"id":"ad6080ea-719b-4732-952f-ef4f1ce45e02","name":"Event target","type":"FUNCTION","valueType":"EVENT","function":function(event){return event.target /* */},"scope":"GLOBAL"},{"id":"dfbfd82d-238f-4bf6-98d8-0abced230307","name":"GENERIC_isDeclinedTracking","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){return false; return typeof Cookiebot !== 'undefined' ? Cookiebot.doNotTrack:false; /* */},"scope":"LOCAL"},{"id":"3e2fb5dd-d60f-4b41-8ada-1f382bce6ade","name":"Go to PDP - search clickout","type":"CONSTANT","valueType":"TEXT","value":"Go to PDP - search clickout","scope":"LOCAL"},{"id":"6c77635c-e95a-4eca-8086-f6be1b1193e8","name":"Named referral: Search","type":"CONSTANT","valueType":"TEXT","value":"Search","scope":"GLOBAL"},{"id":"75cf8fd1-fafe-44be-9806-23ff3017c9c1","name":"Page URL","type":"FUNCTION","valueType":"TEXT","function":function(event){return window.location.href; /* */},"scope":"GLOBAL"},{"id":"c5fb8c06-2bdd-454d-b515-e12152dee71e","name":"PDP_PAGE_getCategoryFromBreadcrumbNodes","type":"FUNCTION","valueType":"TEXT","function":function(event){return document.querySelectorAll('section.breadcrumb a.breadlast')[0]?.text; /* */},"scope":"LOCAL"},{"id":"93d4e35a-c538-4b3c-8b94-48b8176291cf","name":"PDP_PAGE_getPriceFromHiddenInput","type":"FUNCTION","valueType":"DECIMAL","function":function(event){return getNumericPriceFromString(document.querySelector('input#cartPrice').value); /* */},"scope":"LOCAL"},{"id":"2c76f5a2-8349-4834-baa8-3a9f877b97e2","name":"PDP_PAGE_getSkuFromHiddenInput","type":"FUNCTION","valueType":"TEXT","function":function(event){return document.querySelector('input#productId').value; /* */},"scope":"LOCAL"},{"id":"d4c1b026-9899-4b45-85cc-551484edc60d","name":"PDP_PAGE_isPdpPage","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){return !!document.querySelector('.fichaProducto'); /* */},"scope":"LOCAL"},{"id":"eb3288e2-b032-4d93-8550-5703f52975c8","name":"SearchPopup_getClickoutUrl","type":"FUNCTION","valueType":"TEXT","function":function(event){return event.target.closest('article.unibox__selectable')?.getAttribute('data-href'); /* */},"scope":"LOCAL"},{"id":"da7db41d-f3d9-414d-ac17-426f6d487c6a","name":"SearchPopup_hasEmptyResults","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const suggestions = document.querySelectorAll('div#unibox-suggest-box .unibox__cluster--type-resultGroup article.unibox__selectable'); if(!suggestions || suggestions?.length < 1) { return true } else { return false; } /* */},"scope":"LOCAL"},{"id":"ba196044-1955-427b-8e2c-8b589217f090","name":"SearchPopup_isCategoryClickout","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){function isAfterCategorias(targetEl) { const categoriasEl = [...document.querySelectorAll('h2.unibox__suggest-heading')] .find(el => el.textContent.trim().toLowerCase() === 'categorías'); if (!categoriasEl) return false; // same branch? if (categoriasEl.parentElement !== targetEl.parentElement) { return false; } // appears after Categorías in document flow? return !!(categoriasEl.compareDocumentPosition(targetEl) & Node.DOCUMENT_POSITION_FOLLOWING); } const categoryContainer = event.target.closest("article"); const unibox = event.target.closest('article.unibox__selectable'); const suggestBox = event.target.closest('div#unibox-suggest-box'); return Boolean(unibox) && Boolean(suggestBox) && !unibox.getAttribute('data-product-identifier') && isAfterCategorias(categoryContainer); /* */},"scope":"LOCAL"},{"id":"6b5cfd2e-2bfb-4599-aec0-e9f4a579c8aa","name":"SearchPopup_isPDPClickout","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const unibox = event.target.closest('article.unibox__selectable'); const suggestBox = event.target.closest('div#unibox-suggest-box'); return Boolean(unibox) && Boolean(suggestBox) && Boolean(unibox.getAttribute('data-product-identifier')) /* */},"scope":"LOCAL"},{"id":"6b4a0c9a-60b6-4de1-9bfc-22fcbd1103a0","name":"SERP_getAllSkus","type":"FUNCTION","valueType":"LIST","function":function(event){const metaElements = document.querySelectorAll('ul.z-search-list article meta[itemprop="sku"]'); return Array.from(metaElements).map(meta => meta.getAttribute('content')); /* */},"scope":"LOCAL"},{"id":"452cd154-8278-4975-8d0d-b249bbbc0c63","name":"SERP_getClickedUrl","type":"FUNCTION","valueType":"TEXT","function":function(event){let clickedURL = ""; const anchorElement = event.target.closest("a"); if (anchorElement) { clickedURL = anchorElement.href; } return clickedURL; /* */},"scope":"LOCAL"},{"id":"8bf263b4-79b7-4408-aa38-f95d3b6ba4b5","name":"SERP_getSearchResultLabel","type":"FUNCTION","valueType":"TEXT","function":function(event){const isEmpty = document.querySelector('.z-search-layer__main h2')?.textContent.indexOf('Se han encontrado 0 resultados') > -1 || document.querySelectorAll('ul.z-search-list article meta[itemprop="sku"]').length == 0; if (isEmpty) { return 'Empty search result page'; } else { return 'Search results' } /* */},"scope":"LOCAL"},{"id":"20037d0e-e83c-4204-ae66-5752091b21af","name":"SERP_isEmptyResult","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){return document.querySelector('.z-search-layer__main h2')?.textContent.indexOf('Se han encontrado 0 resultados') > -1 || document.querySelectorAll('ul.z-search-list article meta[itemprop="sku"]').length == 0; /* */},"scope":"LOCAL"},{"id":"349369f8-b518-4b9d-9740-331037783478","name":"SERP_PDP button was clicked","type":"FUNCTION","valueType":"BOOLEAN","function":function(event){const pdpButton = event.target.closest(".z-search-frame--results .z-search-cta"); if (pdpButton) { return true; } else { return false; } /* */},"scope":"LOCAL"},{"id":"e964c3b7-efd7-4ea6-a934-d6d0723b0cd8","name":"STATIC_CurrencyCode","type":"CONSTANT","valueType":"TEXT","value":"EUR","scope":"LOCAL"},{"id":"1769ed3e-2991-49bd-9cb4-99a202f44420","name":"STATIC_Locale","type":"CONSTANT","valueType":"TEXT","value":"es-ES","scope":"LOCAL"},{"id":"070708eb-1dad-4d4d-95a3-75066cb11aff","name":"STATIC_Quantity_0","type":"CONSTANT","valueType":"INTEGER","value":"0","scope":"LOCAL"},{"id":"343651f8-594f-4a67-833d-d0481ce1af36","name":"STATIC_Quantity_1","type":"CONSTANT","valueType":"INTEGER","value":"1","scope":"LOCAL"},{"id":"eddeaa57-276b-49da-b06b-f3323296f7f6","name":"True","type":"CONSTANT","valueType":"BOOLEAN","value":"true","scope":"GLOBAL"}]; const script = {"id":"cba38009-ad63-4ebf-9d9f-7a1518b34e2b","rows":[{"id":"27112a8d-2990-4ae5-bf23-f03965975ed5","rowType":"EVENT","trigger":{"id":"d456e380-1c75-4d56-a277-e70a23a10f92","name":"Page Load PDP","type":"PAGE_LOAD","groups":[{"id":"d68610e1-277a-4037-be22-ad4b8adcfbdb","rows":[{"valueType":"BOOLEAN","id":"93c4bfce-be8e-4273-bb05-8494e921e0e8","variableId":"d4c1b026-9899-4b45-85cc-551484edc60d","value":"true","operator":"EQUALS"}]}]},"action":{"type":"PDP_VISITED"},"fields":[{"id":"d940bc3b-c570-4b6d-918e-e273a749c583","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"8f6b1ecc-8a9b-455f-b13d-e47164d29546","variableId":"2c76f5a2-8349-4834-baa8-3a9f877b97e2","fieldName":"sku"},{"id":"424fae10-1e16-4cbf-915f-6d351a89cf62","variableId":"e964c3b7-efd7-4ea6-a934-d6d0723b0cd8","fieldName":"currencyCode"},{"id":"a953e18f-1d23-45ed-aa8a-b2387be9019f","variableId":"c5fb8c06-2bdd-454d-b515-e12152dee71e","fieldName":"category"},{"id":"ae4b49fc-c339-4388-b583-b05d31066abe","variableId":"93d4e35a-c538-4b3c-8b94-48b8176291cf","fieldName":"price"}]},{"id":"0500ec63-ef51-47d9-b057-439a99904eff","rowType":"EVENT","trigger":{"id":"b60c19cb-b3b5-428b-afd5-4375f9353112","name":"Page Load Generic","type":"PAGE_LOAD","groups":[{"id":"c5b505f9-a3d2-4811-9a88-798668c2bf5f","rows":[{"valueType":"BOOLEAN","id":"1a54d6ec-2fe2-4e91-8991-d672e82b765a","variableId":"eddeaa57-276b-49da-b06b-f3323296f7f6","value":"true","operator":"EQUALS"},{"valueType":"BOOLEAN","id":"e1c5714b-5dd5-4723-9e9d-c4db2c3441d3","variableId":"d4c1b026-9899-4b45-85cc-551484edc60d","value":"false","operator":"EQUALS"}]}]},"action":{"type":"PAGE_VISITED"},"fields":[{"id":"1b26b06b-8408-4091-be63-983206680b09","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"}]},{"id":"0ba18a77-5048-41cc-8835-2d0174174a06","rowType":"EVENT","trigger":{"id":"01c2e69e-b843-4424-9e59-2810ec9cd428","name":"SERP Clickout on search result page","type":"CLICK","groups":[{"id":"029a925a-47fa-429c-91d3-1375a95baabc","rows":[{"valueType":"TEXT","id":"f01d75ef-53bb-4867-b664-bfe46273e8f5","variableId":"75cf8fd1-fafe-44be-9806-23ff3017c9c1","value":"/find.html","operator":"CONTAINS"},{"valueType":"EVENT","id":"6cc0c56e-a0b7-492f-b75f-66d7d88fc18d","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"z-search-suggests__link","operator":"MATCHES_CLASS"}]},{"id":"f14947de-b40a-4523-9a6c-7b37619e1882","rows":[{"valueType":"TEXT","id":"14500d54-e9c5-4a05-8414-23fac75a8f8e","variableId":"75cf8fd1-fafe-44be-9806-23ff3017c9c1","value":"/find.html","operator":"CONTAINS"},{"valueType":"BOOLEAN","id":"81f74e36-291d-46e2-9a6a-8576c1eb1a20","variableId":"349369f8-b518-4b9d-9740-331037783478","value":"true","operator":"EQUALS"}]}]},"action":{"type":"CLICKOUT"},"fields":[{"id":"d1dba68d-4427-4741-9ca1-7dcc9b1c4e5a","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"6b3abae0-25fc-4a61-ae58-54779a87e5be","variableId":"452cd154-8278-4975-8d0d-b249bbbc0c63","fieldName":"targetUrl"},{"id":"3a3192b5-8584-4675-b021-2689f2a7ef28","variableId":"6c77635c-e95a-4eca-8086-f6be1b1193e8","fieldName":"namedReferral"},{"id":"349676a1-0097-45fa-a057-59330a32a6a6","variableId":"3e2fb5dd-d60f-4b41-8ada-1f382bce6ade","fieldName":"eventLabel"}]},{"id":"f8b06563-d739-49d2-9cef-e960b191c8d5","rowType":"EVENT","trigger":{"id":"49fe65ef-3c1d-4f8a-9216-ac1d6b34e8dd","name":"PDP Add to cart","type":"CLICK","groups":[{"id":"1dc537ef-3b95-4b82-b158-fe713dfaedf4","rows":[{"valueType":"EVENT","id":"29a22aad-9cc9-4555-af7f-213b785add47","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"cta-buy","operator":"CONTAINS_CLASS"},{"valueType":"EVENT","id":"99c55bbb-c552-4c73-9020-17354c08238f","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"btnComprar","operator":"CONTAINS_CLASS"}]}]},"action":{"type":"ADD_TO_CART"},"fields":[{"id":"4d55e4fd-d953-4ba9-ad0c-b4d9cce8dfae","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"9ee92fa0-eea1-4a36-9e5e-20a5da95ee73","variableId":"2c76f5a2-8349-4834-baa8-3a9f877b97e2","fieldName":"sku"},{"id":"267130af-919f-4bfa-8243-7946ff3b22c8","variableId":"93d4e35a-c538-4b3c-8b94-48b8176291cf","fieldName":"price"},{"id":"90e8bfe6-aa17-4fd5-b4c7-92a794bc3abd","variableId":"e964c3b7-efd7-4ea6-a934-d6d0723b0cd8","fieldName":"currencyCode"},{"id":"a9f0cf36-cc34-407e-ba65-890b8cf70620","variableId":"c5fb8c06-2bdd-454d-b515-e12152dee71e","fieldName":"category"},{"id":"9e72803a-437d-4ee2-a5af-e315201fbbb6","variableId":"343651f8-594f-4a67-833d-d0481ce1af36","fieldName":"quantity"}]},{"id":"65dca004-1f29-4c1f-b9a2-9cea718ced59","rowType":"EVENT","trigger":{"id":"a917af77-9417-428e-b156-39dd377e76b1","name":"CART PAGE Item on cart deleted","type":"CLICK","groups":[{"id":"c76856f0-4996-4445-a47e-161740a2d204","rows":[{"valueType":"EVENT","id":"c988aea1-a089-4ffa-b748-1bd3665430f3","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"delete","operator":"MATCHES_CLASS"}]}]},"action":{"type":"REMOVE_FROM_CART"},"fields":[{"id":"150a74b0-a243-4a04-a64e-f7a7f90515f3","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"a9ed88a6-233d-4024-bed3-1c1f187ea6f1","variableId":"ec6bb905-f9c2-47c3-a2cf-d385287b730f","fieldName":"sku"},{"id":"ad2b32c5-f8e6-4730-a198-cf008268477f","variableId":"070708eb-1dad-4d4d-95a3-75066cb11aff","fieldName":"quantity"}]},{"id":"bb3ef7b4-10b5-4387-94fc-c9f67d586278","rowType":"EVENT","trigger":{"id":"891e6c52-19fb-46dd-8141-cf3f2eca4360","name":"Search result popup - PDP clickout","type":"CLICK","groups":[{"id":"ca43cb42-ac3a-4ecb-aaec-2f9463e4d68c","rows":[{"valueType":"BOOLEAN","id":"8cfe6bd6-0c2a-4bfa-8100-9f61d325d5d6","variableId":"6b5cfd2e-2bfb-4599-aec0-e9f4a579c8aa","value":"true","operator":"EQUALS"}]}]},"action":{"type":"CLICKOUT"},"fields":[{"id":"f02b3346-86ad-4d0f-a6b8-3239691ca87a","variableId":"3e2fb5dd-d60f-4b41-8ada-1f382bce6ade","fieldName":"eventLabel"},{"id":"81dfa372-78bf-4dbe-82db-d0e4d2951e8d","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"471100f6-0e0d-4e11-bef5-08d3089d44ae","variableId":"eb3288e2-b032-4d93-8550-5703f52975c8","fieldName":"targetUrl"},{"id":"b3c796fb-5a2c-4e04-b7ea-12957b4522a3","variableId":"6c77635c-e95a-4eca-8086-f6be1b1193e8","fieldName":"namedReferral"}]},{"id":"04528704-9c0b-4710-b406-c7559d84b8f5","rowType":"EVENT","trigger":{"id":"fd5fc1ab-a056-404e-89ab-d8cb8575eba8","name":"CART PAGE Item on cart increased","type":"CLICK","groups":[{"id":"df1630a3-1f83-49fa-8e69-76c99aa0bbbc","rows":[{"valueType":"EVENT","id":"2780e2ae-3f4d-4b95-a7a5-b2fb1dbe156f","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"count__controler--more","operator":"MATCHES_CLASS"}]}]},"action":{"type":"UPDATE_CART"},"fields":[{"id":"d330668a-9455-478a-a1d8-fe8ee55e82df","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"1e553c1e-b17e-4de2-affb-9ab32a254889","variableId":"ec6bb905-f9c2-47c3-a2cf-d385287b730f","fieldName":"sku"},{"id":"1e63f4d9-7201-4608-947e-7264cf7ec9d9","variableId":"b6cf7400-73a7-4287-ae08-1e85f8113060","fieldName":"quantity"}]},{"id":"df43092b-3b05-40a9-80f5-dc6130bfb086","rowType":"EVENT","trigger":{"id":"7e7de927-006e-4808-8d73-7c03cd6c839e","name":"CART PAGE Item on cart decreased","type":"CLICK","groups":[{"id":"8d7bbaab-5a1d-48db-bbd7-3bcf5d7442d7","rows":[{"valueType":"EVENT","id":"2a7ca442-5a37-4508-b890-235e8e8fc92f","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"count__controler--less","operator":"MATCHES_CLASS"}]}]},"action":{"type":"UPDATE_CART"},"fields":[{"id":"1a5ae09f-adc8-4143-b0d6-eaab771c5989","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"3e0a3ec2-a39e-42f8-9aa2-a5ed406dd29f","variableId":"ec6bb905-f9c2-47c3-a2cf-d385287b730f","fieldName":"sku"},{"id":"8602e751-4f04-476d-833e-8f7557f84af4","variableId":"b6cf7400-73a7-4287-ae08-1e85f8113060","fieldName":"quantity"}]},{"id":"0b8e4b11-8d8e-456b-8e5f-06bab6c32923","rowType":"EVENT","trigger":{"id":"accd61ba-1e35-40ba-a12e-c91aea61a667","name":"User is on search result page","type":"PAGE_LOAD","groups":[{"id":"11c6ed0b-c1a6-498f-84d6-1642fe105a6e","rows":[{"valueType":"TEXT","id":"0000a30e-ee0e-4908-87de-c016ccad559d","variableId":"75cf8fd1-fafe-44be-9806-23ff3017c9c1","value":"find.html","operator":"CONTAINS"}]}]},"action":{"type":"SEARCH_RESULT"},"fields":[{"id":"d334d53a-286b-44b2-865e-d706b94c005b","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"26504f49-2270-43e1-b691-939568e2fdc2","variableId":"6b4a0c9a-60b6-4de1-9bfc-22fcbd1103a0","fieldName":"skus"},{"id":"5522f373-4e10-4f29-bfff-f9e98ae3ab7e","variableId":"20037d0e-e83c-4204-ae66-5752091b21af","fieldName":"isEmpty"},{"id":"053b7423-a960-434b-afbd-e7cb416a08c8","variableId":"8bf263b4-79b7-4408-aa38-f95d3b6ba4b5","fieldName":"eventLabel"}]},{"id":"78fd39f9-1ad0-4fdb-ae1c-f99abfbfddd2","rowType":"EVENT","trigger":{"id":"1673e731-c3ac-48c4-967d-a10707904bed","name":"Search result popup - category clickout","type":"CLICK","groups":[{"id":"fdb0ac6a-bd11-4970-9db1-bbe00abcebe3","rows":[{"valueType":"BOOLEAN","id":"85ea6d26-6416-4f38-a31e-385c038f0981","variableId":"ba196044-1955-427b-8e2c-8b589217f090","value":"true","operator":"EQUALS"}]}]},"action":{"type":"CLICKOUT"},"fields":[{"id":"be30fed7-032f-4436-adad-20718bb35303","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"7bb9960a-4b80-41d0-9f81-242772d15135","variableId":"eb3288e2-b032-4d93-8550-5703f52975c8","fieldName":"targetUrl"},{"id":"0c124f9b-ed19-4c13-bf99-361580367534","variableId":"6c77635c-e95a-4eca-8086-f6be1b1193e8","fieldName":"namedReferral"},{"id":"1f73344e-d106-455f-b0c3-847f19881bc1","variableId":"45da9e02-5fe0-4165-ac5b-dc57ed0aefb1","fieldName":"eventLabel"}]},{"id":"e659a2d9-52a5-4aef-8dec-46b31a54ee25","rowType":"EVENT","trigger":{"id":"6f8c885d-a0eb-4e94-a38b-fb17942c9c22","name":"Search popup - empty search results displayed","type":"INPUT","delayMs":1500,"groups":[{"id":"3ad80c2b-c490-4f33-b70b-e3da2aac9ce7","rows":[{"valueType":"EVENT","id":"6af08020-3064-44ba-827f-b5b7ee709d5c","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"txtBuscador","operator":"MATCHES_ID"},{"valueType":"BOOLEAN","id":"ced3f92e-279c-430f-96f1-38459d6bbc5c","variableId":"da7db41d-f3d9-414d-ac17-426f6d487c6a","value":"true","operator":"EQUALS"}]}]},"action":{"type":"SEARCH_RESULT"},"fields":[{"id":"77a2081a-44da-4903-a05a-efd20c25d68b","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"},{"id":"04c8bcd3-1817-4af2-99c5-851dd4881f1d","variableId":"eddeaa57-276b-49da-b06b-f3323296f7f6","fieldName":"isEmpty"},{"id":"9e4519d4-9f8b-4e3a-b258-9b8804948837","variableId":"d50c80e0-3e60-4250-89a9-e3050f07416e","fieldName":"skus"},{"id":"bf82190b-548d-47df-9837-0e51e72fa065","variableId":"1ecdbee8-e5cd-44a1-908b-337af9b6e218","fieldName":"eventLabel"}]},{"id":"48b9dfcb-feb7-4800-92ea-ee5cb28c0f64","rowType":"EVENT","trigger":{"id":"6f21f47e-3dd7-4c50-84ea-5321c236541c","name":"Search input field interaction","type":"INPUT","delayMs":1500,"groups":[{"id":"2ba2b7f0-6fb5-4f78-955a-77bcea894f7d","rows":[{"valueType":"EVENT","id":"c6181da4-0538-4503-910c-d6602466f6ca","variableId":"ad6080ea-719b-4732-952f-ef4f1ce45e02","value":"txtBuscador","operator":"MATCHES_ID"}]}]},"action":{"type":"SEARCH"},"fields":[{"id":"505fa011-df6b-417d-8739-f9a6d6cfa864","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"}]},{"id":"10a39a0f-6e35-4886-b2d7-85d0f412b4a6","rowType":"EVENT","trigger":{"id":"851dfd06-8858-4017-a8a1-cd1cfd2defb0","name":"User declined tracking","type":"PAGE_LOAD","groups":[{"id":"5e1fedc0-738a-49e4-b8d8-cc296e8321b5","rows":[{"valueType":"BOOLEAN","id":"29cbea6b-67f5-4306-bd30-5d676cb41bff","variableId":"dfbfd82d-238f-4bf6-98d8-0abced230307","value":"true","operator":"EQUALS"}]}]},"action":{"type":"DECLINE_TRACKING"},"fields":[{"id":"0761ac44-e7d4-4180-81a4-14cccd71cfeb","variableId":"1769ed3e-2991-49bd-9cb4-99a202f44420","fieldName":"locale"}]}]}; const advancedCode = function(){}; const url = 'https://queue-propagator.zoovu.com'; const currentEnvironment = 'orca'; const currentAccountId = 3666; const currencies = ['FJD', 'MXN', 'STD', 'SCR', 'LVL', 'CDF', 'BBD', 'UGX', 'HNL', 'MXV', 'ZAR', 'STN', 'CUC', 'SDD', 'BSD', 'SDG', 'IQD', 'CUP', 'GMD', 'TWD', 'RSD', 'UYI', 'MYR', 'FKP', 'XOF', 'UYU', 'CVE', 'OMR', 'KES', 'SEK', 'BTN', 'GNF', 'MZN', 'SVC', 'MZM', 'ARS', 'QAR', 'NLG', 'IRR', 'XPD', 'XPF', 'UZS', 'THB', 'BDT', 'LYD', 'KWD', 'XPT', 'RUB', 'ISK', 'BEF', 'MKD', 'RUR', 'DZD', 'PAB', 'SGD', 'KGS', 'XAD', 'XAF', 'XAG', 'CHF', 'ITL', 'ATS', 'HRK', 'CHE', 'DJF', 'TZS', 'VND', 'XAU', 'ADP', 'AUD', 'CHW', 'KHR', 'IDR', 'XBA', 'KYD', 'XBC', 'XBB', 'BWP', 'SHP', 'XBD', 'CYP', 'TJS', 'RWF', 'AED', 'DKK', 'ZWD', 'BGL', 'BGN', 'MMK', 'NOK', 'SYP', 'ZWG', 'ZWL', 'YUM', 'ZWN', 'LKR', 'ZWR', 'IEP', 'CZK', 'XCD', 'GRD', 'HTG', 'XSU', 'AFA', 'XCG', 'BHD', 'SIT', 'PTE', 'SZL', 'KZT', 'YER', 'AFN', 'BYB', 'AWG', 'NPR', 'MNT', 'GBP', 'XTS', 'BYN', 'HUF', 'BYR', 'BIF', 'XUA', 'XDR', 'BZD', 'MOP', 'NAD', 'SKK', 'TMM', 'PEN', 'WST', 'TMT', 'FRF', 'CLF', 'GTQ', 'CLP', 'TND', 'SLE', 'SLL', 'AYM', 'XFO', 'DOP', 'KMF', 'XFU', 'GEL', 'MAD', 'TOP', 'AZM', 'PGK', 'AZN', 'UAH', 'ERN', 'TPE', 'MRO', 'CNY', 'MRU', 'BMD', 'PHP', 'XXX', 'PYG', 'JMD', 'GWP', 'ESP', 'COP', 'USD', 'COU', 'USN', 'ETB', 'VEB', 'VED', 'USS', 'VEF', 'VUV', 'SOS', 'LAK', 'BND', 'ZMK', 'LRD', 'ALL', 'GHC', 'MTL', 'VES', 'ZMW', 'TRL', 'ILS', 'GHS', 'GYD', 'KPW', 'BOB', 'MDL', 'AMD', 'TRY', 'LBP', 'JOD', 'HKD', 'EUR', 'LSL', 'CAD', 'BOV', 'EEK', 'MUR', 'ROL', 'GIP', 'RON', 'NGN', 'CRC', 'PKR', 'ANG', 'SRD', 'TTD', 'SAR', 'LTL', 'MVR', 'SRG', 'INR', 'KRW', 'JPY', 'PLN', 'AOA', 'SBD', 'CSD', 'LUF', 'MWK', 'MGA', 'FIM', 'DEM', 'MGF', 'BAM', 'EGP', 'SSP', 'NIO', 'NZD', 'BRL'] // ------------------------- 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); } class TrackingExecutionError extends Error { constructor(message, trackingEntityType, variableId, triggerId, scriptId, trackingErrorType) { super(message); this.trackingEntityType = trackingEntityType; this.variableId = variableId; this.triggerId = triggerId; this.scriptId = scriptId; this.trackingErrorType = trackingErrorType; } } // ------------------------- 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", LEAD_GEN: "LEAD_GEN" }); const trackingEventTypes = Object.freeze({ TRACKING_SUCCESSFUL_EXECUTION: 'TRACKING_SUCCESSFUL_EXECUTION', TRACKING_EXECUTION_FAILURE: 'TRACKING_EXECUTION_FAILURE' }) 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 trackingErrorTypes = Object.freeze({ MISSING_VALUE: 'MISSING_VALUE', TYPE_MISMATCH: 'TYPE_MISMATCH', UNSUPPORTED_VARIABLE: 'UNSUPPORTED_VARIABLE', MISSING_VARIABLE: 'MISSING_VARIABLE', OTHER: 'OTHER' }); const trackingVariableScopes = Object.freeze({ GLOBAL: 'GLOBAL', LOCAL: 'LOCAL' }); const namedReferrals = Object.freeze({ SEARCH: 'SEARCH' }) const getElementAttribute = (target, attributeName) => { const attribute = target[attributeName]; if (typeof attribute === 'string') { return attribute; } if (attribute && attribute.baseVal !== undefined) { return attribute.baseVal; } return target.getAttribute(attributeName === 'className' ? 'class' : attributeName) || ''; }; const getElementId = (target) => { return getElementAttribute(target, 'id'); }; const getElementClassName = (target) => { return getElementAttribute(target, 'className'); }; 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) => getElementId(target) === test, CONTAINS_ID: (target, test) => getElementId(target).includes(test), DOES_NOT_CONTAIN_ID: (target, test) => !getElementId(target).includes(test), MATCHES_CLASS: (target, test) => getElementClassName(target) === test, CONTAINS_CLASS: (target, test) => getElementClassName(target).includes(test), DOES_NOT_CONTAIN_CLASS: (target, test) => !getElementClassName(target).includes(test), MATCHES_REGEX: (a, b) => parseRegexString(b).test(a), }); const trackingFieldName = `${domainId}_${zoovuId}_trackingExecutions` const MAX_RECORDS = 100 // helper to retrieve variableId later let eventFields = [] if (!JSON.parse(localStorage.getItem(trackingFieldName))) { localStorage.setItem(trackingFieldName, JSON.stringify([])) } function parseRegexString(input) { const trimmed = input.trim(); // check if it starts and ends with slashes (e.g., /^abc$/gi) const match = trimmed.match(/^\/(.+)\/([a-z]*)$/i); if (match) { const pattern = match[1]; const flags = match[2]; return new RegExp(pattern, flags); } // no flags return new RegExp(trimmed); } function checkValue(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 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, useTrailing = false) { if (useTrailing) { // Trailing debounce for INPUT triggers - only fires once after delay using requestAnimationFrame let lastCallTime = 0; let animationFrameId = null; let hasScheduledExecution = false; function checkAndExecute(context, args) { const currentTime = performance.now(); const timeSinceLastCall = currentTime - lastCallTime; if (timeSinceLastCall >= delay) { // Delay has passed, execute the function fn.apply(context, args); hasScheduledExecution = false; } else { // Keep checking until delay has passed animationFrameId = requestAnimationFrame(() => { checkAndExecute(context, args); }); } } return function () { const context = this, args = arguments; lastCallTime = performance.now(); if (!hasScheduledExecution) { hasScheduledExecution = true; animationFrameId = requestAnimationFrame(() => { checkAndExecute(context, args); }); } }; } // Original requestAnimationFrame-based debounce for other triggers let lastCallTime = 0; let animationFrameId = null; let isScheduled = false; function execute(context, args, currentTime) { if (currentTime - lastCallTime >= delay) { fn.apply(context, args); isScheduled = false; } else { animationFrameId = requestAnimationFrame((newTime) => { execute(context, args, newTime); }); } } return function () { const context = this, args = arguments; lastCallTime = performance.now(); if (!isScheduled) { isScheduled = true; animationFrameId = requestAnimationFrame((currentTime) => { execute(context, args, currentTime); }); } }; } 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, variableId, triggerId) { let errorMessage = ''; if (value === undefined || value === null) { throw new TrackingExecutionError(`${propertyName}: value is not defined.`, trackingEntityTypes.VARIABLE, variableId, triggerId, script.id, trackingErrorTypes.MISSING_VALUE) } switch (variableType) { case "TEXT": if (typeof value !== 'string') errorMessage = getPropertyTypeError(propertyName, 'string', typeof value); break; case "INTEGER": case "DECIMAL": if (typeof value !== 'number') errorMessage = getPropertyTypeError(propertyName, 'number', typeof value); break; case "BOOLEAN": if (typeof value !== 'boolean') errorMessage = getPropertyTypeError(propertyName, 'boolean', typeof value); break; case 'LIST': if (!Array.isArray(value)) errorMessage = getPropertyTypeError(propertyName, 'list', typeof value); break; case 'EVENT': break; default: throw new TrackingExecutionError(`Not supported variable type: ${variableType}`, trackingEntityTypes.VARIABLE, variableId, triggerId, script.id, trackingErrorTypes.UNSUPPORTED_VARIABLE) } if (errorMessage) { throw new TrackingExecutionError(errorMessage, trackingEntityTypes.VARIABLE, variableId, triggerId, script.id, trackingErrorTypes.TYPE_MISMATCH) } } function ensureRequiredFieldsPresent(fields, eventType) { return Object.entries(fields).every(([key, value]) => { if (!value) { const variableId = eventFields.find(field => field.fieldName === key).variableId; // empty eventFields eventFields = []; throw new TrackingExecutionError( `Required property ${key} missing value for ${eventType}.`, trackingEntityTypes.VARIABLE, variableId, null, script.id, trackingErrorTypes.MISSING_VALUE ) } 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; case actionTypes.LEAD_GEN: return sendLeadGenEvent; } } function checkSpecificFieldValues(fieldName, value, variableId) { if (fieldName === 'currencyCode' && !currencies.includes(value)) { throw new TrackingExecutionError( `Value ${value} is not compatible with currencyCode.`, trackingEntityTypes.VARIABLE, variableId, null, script.id, trackingErrorTypes.OTHER ) } if (fieldName === 'namedReferral' && !namedReferrals[value.toUpperCase()]) { throw new TrackingExecutionError( `Value ${value} is not compatible with namedReferral.`, trackingEntityTypes.VARIABLE, variableId, null, script.id, trackingErrorTypes.OTHER ) } } function getVariableValueById(variableId, event, fieldName, triggerId) { 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') { try { variableValue = foundVariable.function(event); } catch (e) { throw new TrackingExecutionError( e.message, trackingEntityTypes.VARIABLE, variableId, triggerId, script.id, trackingErrorTypes.OTHER ) } } else { throw new TrackingExecutionError( `${foundVariable.type} is not supported.`, trackingEntityTypes.VARIABLE, variableId, triggerId, script.id, trackingErrorTypes.UNSUPPORTED_VARIABLE ) } typeCheckValue(variableValue, foundVariable.valueType, fieldName ? fieldName : foundVariable.name, variableId, triggerId); if (fieldName === 'currencyCode' || fieldName === 'namedReferral') { checkSpecificFieldValues(fieldName, variableValue, variableId) } return variableValue; } else { throw new TrackingExecutionError( `No variable with ID ${variableId}`, trackingEntityTypes.VARIABLE, variableId, triggerId, script.id, trackingErrorTypes.MISSING_VARIABLE ) } } function evaluateSingleTriggerRow(row, event, triggerId) { const target = getVariableValueById(row.variableId, event, undefined, triggerId); const matcher = matchers[row.operator]; checkValue(row.valueType, row.value); 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, trigger.id))) } function runAction(action, fields, event, trigger) { const trackingExecutions = jsonParser(trackingFieldName) if (action.type === actionTypes.CUSTOM && typeof action.code === 'function') { // add script custom action trackingExecutions.push(createTrackingSuccessfulExecutionRecord(trackingEntityTypes.CUSTOM_ACTION, action.id)) try { action.code(event); } catch (e) { throw new TrackingExecutionError( e.message, trackingEntityTypes.CUSTOM_ACTION, action.id, null, script.id, trackingErrorTypes.OTHER ) } localStorage.setItem(trackingFieldName, JSON.stringify(trackingExecutions)); } else { const sendEventFunction = getEventExecutable(action.type); eventFields = [...fields]; if (sendEventFunction && typeof sendEventFunction === 'function') { const payload = preparePayloadFromFields(fields, event) sendEventFunction(payload); addSuccessfulExecutions(trigger, fields) } } } // Store debounced functions per rule to reuse them const debouncedRuleActions = new Map(); let ruleCounter = 0; function evaluateSingleRule(rule, event) { // Assign unique ID to rule if it doesn't have one if (!rule._uniqueId) { rule._uniqueId = ruleCounter++; } if (evaluateTriggerConditions(rule.trigger, event)) { // Set default delay for INPUT triggers if delayMs is undefined const delay = rule.trigger.type === triggerTypes.INPUT && rule.trigger.delayMs === undefined ? 1500 : rule.trigger.delayMs; if (delay) { // Get or create debounced function for this rule using unique rule ID const ruleKey = rule._uniqueId; if (!debouncedRuleActions.has(ruleKey)) { const useTrailing = rule.trigger.type === triggerTypes.INPUT; const debounced = debounce(function (event) { runAction(rule.action, rule.fields, event, rule.trigger); }, delay, useTrailing); debouncedRuleActions.set(ruleKey, debounced); } debouncedRuleActions.get(ruleKey)(event); } else { runAction(rule.action, rule.fields, event, rule.trigger); } } } function evaluateRules(rules, event) { try { rules.forEach(rule => { evaluateSingleRule(rule, event) }); } catch (error) { const trackingExecutions = jsonParser(trackingFieldName); const {message, trackingEntityType, variableId, triggerId, scriptId, trackingErrorType} = error; const failedExecution = createTrackingFailedExecutionRecord( trackingEntityType, variableId, triggerId, scriptId, trackingErrorType, message ) trackingExecutions.push(failedExecution) localStorage.setItem(trackingFieldName, JSON.stringify(trackingExecutions)); 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 createTrackingSuccessfulExecutionRecord(entityType, entityId) { const executionTime = Date.now(); return { organization: getOrganization(), domainId: getDomainId(), eventType: trackingEventTypes.TRACKING_SUCCESSFUL_EXECUTION, trackingEntityType: entityType, trackingEntityId: entityId, executionTime } } function createTrackingFailedExecutionRecord(entityType, entityId, triggerId, scriptId, errorType, message) { const executionTime = Date.now(); return { organization: getOrganization(), domainId: getDomainId(), eventType: trackingEventTypes.TRACKING_EXECUTION_FAILURE, trackingEntityType: entityType, trackingEntityId: entityId, executionTime, triggerId: triggerId || null, scriptId: scriptId || null, error: { type: errorType, message } } } function addSuccessfulExecutions(trigger, fields) { const successfulExecutions = jsonParser(trackingFieldName) // add script trigger successfulExecutions.push(createTrackingSuccessfulExecutionRecord(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 && foundVariableInField.scope === trackingVariableScopes.LOCAL) { successfulExecutions.push(createTrackingSuccessfulExecutionRecord(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 && foundVariable.scope === trackingVariableScopes.LOCAL) { successfulExecutions.push(createTrackingSuccessfulExecutionRecord(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 sendLeadGenEvent({ locale, currencyCode, products, leadId, leadType, category, eventLabel = "Lead gen sent" }) { const body = { locale, eventLabel, // optional currencyCode, products, leadId, leadType, category }; if (ensureRequiredFieldsPresent({locale}, eventTypes.LEAD_GEN)) { sendFactEventBase(eventTypes.LEAD_GEN, body); } }; 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); const handler = (event) => { observeAndReactOnPageChange(() => { evaluateRules(pageLoadRules, event); sendSuccessfulExecutionEvent(); }); }; // Check if page already loaded if (document.readyState === "complete") { handler(new Event('load')); } else { window.addEventListener("load", handler, {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', function (event) { evaluateRules(inputRules, event); sendSuccessfulExecutionEvent() }, {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(); } })();