1var mailchimp,mailchimp_cart,mailchimp_billing_email,mailchimp_username_email,mailchimp_registration_email,mailchimp_submitted_email=false,mailchimpReady=function(a){/in/.test(document.readyState)?setTimeout(()=>{mailchimpReady(a)},9):a()};function mailchimpHandleBillingEmail(selector){try{if(!mailchimp_public_data.allowed_to_set_cookies)return;if(mailchimp_public_data.disable_carts)return;var subscribed=document.querySelector("#mailchimp_woocommerce_newsletter");if(!subscribed)subscribed=document.querySelector("#subscribe-to-newsletter");if(!selector)selector="#billing_email";var a=document.querySelector(selector);var b=void 0!==a?a.value:"";if(!mailchimp_cart.valueEmail(b)||mailchimp_submitted_email===b){return false}mailchimp_cart.setEmail(b);var c=mailchimp_public_data.ajax_url+"?action=mailchimp_set_user_by_email";var d=new XMLHttpRequest;d.open("POST",c,!0);d.onload=function(){var successful=d.status>=200&&d.status<400;var msg=successful?"mailchimp.handle_billing_email.request.success":"mailchimp.handle_billing_email.request.error";if(successful){mailchimp_submitted_email=b}console.log(msg,d.responseText)};d.onerror=function(){console.log("mailchimp.handle_billing_email.request.error",d.responseText)};d.setRequestHeader("Content-Type","application/x-www-form-urlencoded");d.setRequestHeader("Accept","application/json");d.send("email="+b+"&mc_language="+mailchimp_public_data.language+"&subscribed="+(subscribed&&subscribed.checked?"1":"0"));return true}catch(a){console.log("mailchimp.handle_billing_email.error",a);mailchimp_submitted_email=!1}}!function(){"use strict";function mailchimpCart(){this.email_types="input[type=email]";this.regex_email=/^([A-Za-z0-9_+\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;this.current_email=null;this.previous_email=null;this.expireUser=function(){this.current_email=null;if(!mailchimp_public_data.allowed_to_set_cookies)return;mailchimp.storage.expire("mailchimp.cart.current_email")};this.expireSaved=function(){if(!mailchimp_public_data.allowed_to_set_cookies)return;mailchimp.storage.expire("mailchimp.cart.items")};this.setEmail=function(a){if(!mailchimp_public_data.allowed_to_set_cookies)return;if(!this.valueEmail(a))return false;this.setPreviousEmail(this.getEmail());mailchimp.storage.set("mailchimp.cart.current_email",this.current_email=a)};this.getEmail=function(){if(!mailchimp_public_data.allowed_to_set_cookies)return;if(this.current_email)return this.current_email;var a=mailchimp.storage.get("mailchimp.cart.current_email",!1);if(!a||!this.valueEmail(a))return false;return this.current_email=a};this.setPreviousEmail=function(a){if(!mailchimp_public_data.allowed_to_set_cookies)return;if(!this.valueEmail(a))return false;mailchimp.storage.set("mailchimp.cart.previous_email",this.previous_email=a)};this.valueEmail=function(a){return this.regex_email.test(a)};return this}var g={extend:function(a,b){for(var c in b||{})b.hasOwnProperty(c)&&(a[c]=b[c]);return a},getQueryStringVars:function(){var a=window.location.search||"",b=[],c={};if(a=a.substr(1),a.length){b=a.split("&");for(var d in b){var e=b[d];if("string"==typeof e){var f=e.split("="),g=f[0],h=f[1];g.length&&("undefined"==typeof c[g]&&(c[g]=[]),c[g].push(h))}}}return c},unEscape:function(a){return decodeURIComponent(a)},escape:function(a){return encodeURIComponent(a)},createDate:function(a,b){a||(a=0);var c=new Date,d=b?c.getDate()-a:c.getDate()+a;return c.setDate(d),c},arrayUnique:function(a){for(var b=a.concat(),c=0;c<b.length;++c)for(var d=c+1;d<b.length;++d)b[c]===b[d]&&b.splice(d,1);return b},objectCombineUnique:function(a){for(var b=a[0],c=1;c<a.length;c++){var d=a[c];for(var e in d)b[e]=d[e]}return b}},h=function(a,b){var c=function(a,b,d){return 1===arguments.length?c.get(a):c.set(a,b,d)};return c.get=function(b,d){return a.cookie!==c._cacheString&&c._populateCache(),void 0==c._cache[b]?d:c._cache[b]},c.defaults={path:"/",secure:true,samesite:"strict"},c.set=function(d,e,f){switch(f={path:f&&f.path||c.defaults.path,domain:f&&f.domain||c.defaults.domain,expires:f&&f.expires||c.defaults.expires,secure:f&&f.secure!==b?f.secure:c.defaults.secure,samesite:f&&f.samesite||c.defaults.samesite},e===b&&(f.expires=-1),typeof f.expires){case"number":f.expires=new Date((new Date).getTime()+1e3*f.expires);break;case"string":f.expires=new Date(f.expires)}return d=encodeURIComponent(d)+"="+(e+"").replace(/[^!#-+\--:<-\[\]-~]/g,encodeURIComponent),d+=f.path?";path="+f.path:"",d+=f.domain?";domain="+f.domain:"",d+=f.expires?";expires="+f.expires.toGMTString():"",d+=f.secure?";secure":"",d+=f.samesite?";samesite="+f.samesite:"",a.cookie=d,c},c.expire=function(a,d){return c.set(a,b,d)},c._populateCache=function(){c._cache={};try{c._cacheString=a.cookie;for(var d=c._cacheString.split("; "),e=0;e<d.length;e++){var f=d[e].indexOf("="),g=decodeURIComponent(d[e].substr(0,f)),f=decodeURIComponent(d[e].substr(f+1));c._cache[g]===b&&(c._cache[g]=f)}}catch(a){console.log(a)}},c.enabled=function(){var a="1"===c.set("cookies.js","1").get("cookies.js");return c.expire("cookies.js"),a}(),c}(document);mailchimp={storage:h,utils:g};mailchimp_cart=new mailchimpCart}();
1mailchimpReady(function(){if(!mailchimp_public_data.allowed_to_set_cookies)return;if(mailchimp_public_data.disable_carts)return;if(void 0===a){var a={site_url:document.location.origin,defaulted:!0,ajax_url:document.location.origin+"/wp-admin?admin-ajax.php"}}try{var b=mailchimp.utils.getQueryStringVars();var subscribed=document.querySelector("#mailchimp_woocommerce_newsletter");var blockSubscribed=document.querySelector("#subscribe-to-newsletter");if(subscribed){subscribed.onchange=function(){mailchimp_submitted_email=null;mailchimpHandleBillingEmail("#billing_email")}}else if(blockSubscribed){blockSubscribed.onchange=function(){mailchimp_submitted_email=null;mailchimpHandleBillingEmail('#contact-fields input[type="email"]')}}mailchimp_username_email=document.querySelector("#username");mailchimp_billing_email=document.querySelector("#billing_email");mailchimp_registration_email=document.querySelector("#reg_email");var mailchimp_username_email_block=document.querySelector('#contact-fields input[type="email"]');if(mailchimp_billing_email){mailchimp_billing_email.onblur=function(){mailchimpHandleBillingEmail("#billing_email")};mailchimp_billing_email.onfocus=function(){mailchimpHandleBillingEmail("#billing_email")}}if(mailchimp_username_email){mailchimp_username_email.onblur=function(){mailchimpHandleBillingEmail("#username")};mailchimp_username_email.onfocus=function(){mailchimpHandleBillingEmail("#username")}}if(mailchimp_registration_email){mailchimp_registration_email.onblur=function(){mailchimpHandleBillingEmail("#reg_email")};mailchimp_registration_email.onfocus=function(){mailchimpHandleBillingEmail("#reg_email")}}if(mailchimp_username_email_block){mailchimp_username_email_block.onblur=function(){mailchimpHandleBillingEmail('#contact-fields input[type="email"]')};mailchimp_username_email_block.onfocus=function(){mailchimpHandleBillingEmail('#contact-fields input[type="email"]')};var typingTimer;mailchimp_username_email_block.addEventListener("keyup",function(){typingTimer&&clearTimeout(typingTimer);typingTimer=setTimeout(function(){if(mailchimp_cart.valueEmail(mailchimp_username_email_block.value)){mailchimpHandleBillingEmail('#contact-fields input[type="email"]')}},2e3)});mailchimp_username_email_block.addEventListener("keydown",function(){typingTimer&&clearTimeout(typingTimer)})}}catch(e){console.log("mailchimp ready error",e)}});; 2/** 3 * Mailchimp Pixel Tracking for WooCommerce 4 * 5 * Listens for WooCommerce events and sends them to the Mailchimp Pixel SDK. 6 * Only tracks if window.$mcSite.pixel.api is available (MC.js loaded). 7 * 8 * @package MailChimp_WooCommerce 9 * @since 1.0.0 10 */ 11 12(function ($) { 13 'use strict'; 14 15 /** 16 * Default config for waitForPixelSDK exponential backoff 17 */ 18 const PIXEL_SDK_WAIT_CONFIG = { 19 initialDelayMs: 100, 20 maxDelayMs: 5000, 21 maxAttempts: 20 22 }; 23 24 /** 25 * How long PRODUCT_VIEWED waits on a variable product for WooCommerce to resolve a 26 * default or deep-linked variation before settling for the parent. 27 */ 28 const VARIATION_RESOLVE_WAIT_MS = 1200; 29 30 /** 31 * Settle time after a form interaction before re-reading the variation id input. 32 * The blocks form writes it through the Interactivity API on the next render. 33 */ 34 const VARIATION_INPUT_SETTLE_MS = 150; 35 36 /** 37 * Mailchimp Pixel Tracking Handler 38 */ 39 const MailchimpPixelTracking = { 40 // Debounce timers for cart fetch calls 41 _atcTimer: null, 42 _rfcTimer: null, 43 _atcFetching: false, 44 _rfcFetching: false, 45 // PRODUCT_VIEWED state for a variable product being resolved to a variation 46 _viewTimer: null, 47 _viewedParent: null, 48 _viewedKeys: null, 49 _inputTimer: null, 50 51 /** 52 * Initialize tracking. 53 * Waits for Pixel SDK with exponential backoff, then sends page events and attaches handlers. 54 */ 55 init: function () { 56 const self = this; 57 58 // Registered before the SDK wait on purpose: that wait runs for seconds, and an 59 // integration that resolves its variation early must not race us. 60 window.mcPixel = window.mcPixel || {}; 61 window.mcPixel.setVariation = function (variationId, product) { 62 self.setVariation(variationId, product); 63 }; 64 document.addEventListener('mailchimp_woocommerce_set_variation', function (event) { 65 const detail = event.detail || {}; 66 self.setVariation(detail.variationId, detail.product); 67 }); 68 69 this.waitForPixelSDK(PIXEL_SDK_WAIT_CONFIG) 70 .then(function () { 71 self.sendPageEvents(); 72 self.attachCartEventListeners(); 73 self.interceptStoreApiRequests(); 74 console.log('Mailchimp Pixel SDK loaded.'); 75 }) 76 .catch(function (e) { 77 console.log('Mailchimp Pixel SDK not loaded within timeout. Tracking disabled.', e); 78 }); 79 }, 80 81 /** 82 * Wait for Pixel SDK to become available using exponential backoff. 83 * 84 * @param {Object} options Optional config 85 * @param {number} options.initialDelayMs First delay in ms (default 100) 86 * @param {number} options.maxDelayMs Cap on delay in ms (default 5000) 87 * @param {number} options.maxAttempts Max number of attempts (default 20) 88 * @return {Promise<void>}
88 Resolves when SDK is available, rejects after maxAttempts 89 */ 90 waitForPixelSDK: function (options) { 91 const config = options || {}; 92 const initialDelayMs = config.initialDelayMs !== undefined ? config.initialDelayMs : PIXEL_SDK_WAIT_CONFIG.initialDelayMs; 93 const maxDelayMs = config.maxDelayMs !== undefined ? config.maxDelayMs : PIXEL_SDK_WAIT_CONFIG.maxDelayMs; 94 const maxAttempts = config.maxAttempts !== undefined ? config.maxAttempts : PIXEL_SDK_WAIT_CONFIG.maxAttempts; 95 96 function isSDKReady() { 97 return typeof window.$mcSite !== 'undefined' && 98 window.$mcSite.pixel && 99 typeof window.$mcSite.pixel.api !== 'undefined' && 100 typeof window.$mcSite.pixel.api.track === 'function' && 101 window.$mcSite.pixel.installed === true; 102 } 103 104 return new Promise(function (resolve, reject) { 105 let attempt = 0; 106 107 function scheduleCheck() { 108 if (isSDKReady()) { 109 // FIXME: temporary wait to accommodate for not being able to detect ready state 110 console.warn('Pixel SDK - remediation for pixel ready issue') 111 setTimeout(function () { 112 resolve(); 113 }, 1000); 114 return; 115 } 116 if (attempt >= maxAttempts) { 117 reject(new Error('Pixel SDK not available')); 118 return; 119 } 120 121 const delay = Math.min( 122 initialDelayMs * Math.pow(2, attempt), 123 maxDelayMs 124 ); 125 attempt += 1; 126 127 setTimeout(scheduleCheck, delay); 128 } 129 130 scheduleCheck(); 131 }); 132 }, 133 134 /** 135 * Synchronous check if Pixel SDK is available (e.g. before each track call). 136 * 137 * @return {boolean} True if SDK is available 138 */ 139 isPixelSDKReady: function () { 140 return typeof window.$mcSite !== 'undefined' && 141 window.$mcSite.pixel && 142 typeof window.$mcSite.pixel.api !== 'undefined' && 143 typeof window.$mcSite.pixel.api.track === 'function'; 144 }, 145 146 /** 147 * Get cart ID from window.mcPixel 148 * 149 * @return {string} Cart ID 150 */ 151 getCartId: function () { 152 return window.mcPixel && window.mcPixel.cartId ? window.mcPixel.cartId : ''; 153 }, 154 155 /** 156 * Get the REST API base URL from the localized config. 157 * 158 * @return {string} REST base URL 159 */ 160 getRestBase: function () { 161 return (window.mcPixelConfig && window.mcPixelConfig.restBase) || '/wp-json/mailchimp-for-woocommerce/v1/'; 162 }, 163 164 /** 165 * Send page-level events based on pre-populated data 166 */ 167 sendPageEvents: function () { 168 if (!window.mcPixel || !window.mcPixel.data) { 169 return; 170 } 171 172 const data = window.mcPixel.data; 173 const events = data.events || []; 174 175 // Send events based on what was set by PHP 176 events.forEach((eventType) => { 177 //console.log('Mailchimp Pixel: Sending event', eventType, data); 178 switch (eventType) { 179 case 'PRODUCT_ADDED_TO_CART': 180 if (data.added_to_cart) { 181 var atcItems = Array.isArray(data.added_to_cart) ? data.added_to_cart : [data.added_to_cart]; 182 for (var ai = 0; ai < atcItems.length; ai++) { 183 this.sendProductAddedToCart(atcItems[ai]); 184 } 185 } 186 break; 187 case 'PRODUCT_REMOVED_FROM_CART': 188 if (data.removed_from_cart) { 189 var rfcItems = Array.isArray(data.removed_from_cart) ? data.removed_from_cart : [data.removed_from_cart]; 190 for (var ri = 0; ri < rfcItems.length; ri++) { 191 this.sendProductRemovedFromCart(rfcItems[ri]); 192 } 193 } 194 break; 195 case 'IDENTITY': 196 if (data.identity && data.identity.email) { 197 this.sendIdentityEvent(data.identity.email); 198 } 199 break; 200 case 'PRODUCT_VIEWED': 201 // Skip if an add-to-cart or remove-from-cart already fired for the same product. 202 // The cart action implies the view, so firing both is redundant. 203 if (data.product) { 204 const viewedId = String(data.product.productId || data.product.id); 205 const atcArr = Array.isArray(data.added_to_cart) ? data.added_to_cart : (data.added_to_cart ? [data.added_to_cart] : []); 206 const rfcArr = Array.isArray(data.removed_from_cart) ? data.removed_from_cart : (data.removed_from_cart ? [data.removed_from_cart] : []); 207 const atcMatch = atcArr.some(function (p) { return String(p.productId || p.id
207) === viewedId; }); 208 const rfcMatch = rfcArr.some(function (p) { return String(p.productId || p.id) === viewedId; }); 209 210 if ( 211 (events.includes('PRODUCT_ADDED_TO_CART') && atcMatch) || 212 (events.includes('PRODUCT_REMOVED_FROM_CART') && rfcMatch) 213 ) { 214 //console.log('Mailchimp Pixel: Skipping PRODUCT_VIEWED (superseded by cart event for same product)'); 215 break; 216 } 217 this.trackProductViewed(data.product); 218 } 219 break; 220 case 'CART_VIEWED': 221 if (data.cart) { 222 this.sendCartViewed(data.cart); 223 } 224 break; 225 case 'CHECKOUT_STARTED': 226 if (data.checkout) { 227 this.sendCheckoutStarted(data.checkout); 228 window.mcPixel._handled.checkout = true; 229 } 230 break; 231 case 'PURCHASED': 232 if (data.order) { 233 this.sendPurchased(data.order); 234 } 235 break; 236 case 'PRODUCT_CATEGORY_VIEWED': 237 if (data.category) { 238 this.sendCategoryViewed(data.category); 239 window.mcPixel._handled.category = true; 240 } 241 break; 242 case 'SEARCH_SUBMITTED': 243 if (data.search) { 244 this.sendSearchSubmitted(data.search); 245 window.mcPixel._handled.search = true; 246 } 247 break; 248 } 249 }); 250 }, 251 252 /** 253 * PRODUCT_VIEWED for the single product page. 254 * 255 * A variable product renders before the customer has picked anything, so the 256 * server-side payload only knows the parent. WooCommerce announces the chosen 257 * variation on the .variations_form with found_variation, so we hold the event 258 * until that fires - or until it's clear nothing is going to resolve. 259 * 260 * @param {Object} product Parent product data from PHP 261 */ 262 trackProductViewed: function (product) { 263 const self = this; 264 this._viewedKeys = this._viewedKeys || {}; 265 266 // not a variable product - the server already knew everything there is to know 267 if (!window.mcPixel.data.product_variations) { 268 this.sendProductViewedOnce(product, product.id); 269 return; 270 } 271 272 this._viewedParent = product; 273 this.attachVariationListeners(); 274 275 const resolved = this.currentVariationId(); 276 if (resolved) { 277 this.sendProductViewedOnce(this.resolveVariationPayload({ variation_id: resolved }), resolved); 278 return; 279 } 280 281 // nothing picked yet, but a default variation or ?attribute_* deep link still 282 // resolves a tick after the form initialises - give WooCommerce that tick 283 // before settling for the parent 284 this._viewTimer = setTimeout(function () { 285 self.sendProductViewedOnce(product, product.id); 286 }, VARIATION_RESOLVE_WAIT_MS); 287 }, 288 289 /** 290 * One PRODUCT_VIEWED per thing actually viewed: the parent, then each variation 291 * the customer lands on. Switching back to an earlier pick sends nothing, so 292 * clicking through a dropdown doesn't turn into a burst of events. 293 * 294 * @param {Object} product Product data to send 295 * @param {string} key Dedup key - the variation id, or the parent id 296 */ 297 sendProductViewedOnce: function (product, key) { 298 clearTimeout(this._viewTimer); 299 if (this._viewedKeys[key]) { 300 return; 301 } 302 // only claim the key once it actually went out - an integration can call 303 // setVariation() before the SDK is up, and that must not count as sent 304 if (this.sendProductViewed(product)) { 305 this._viewedKeys[key] = true; 306 } 307 }, 308 309 /** 310 * Public API for themes, swatch plugins and custom templates. 311 * 312 * Detection covers what WooCommerce itself ships: the classic variations form, 313 * and the hidden variation_id input the block form maintains. Anything else - a 314 * custom swatch UI, a page builder widget, a headless front end - can announce 315 * the selection itself, either directly: 316 * 317 * window.mcPixel.setVariation( 123 ); 318 * 319 * or, when it cannot reach our global, by dispatching an event: 320 * 321 * document.dispatchEvent( new CustomEvent( 322 * 'mailchimp_woocommerce_set_variation', 323 * { detail: { variationId: 123 } } 324 * ) ); 325 * 326 * Pass a second argument to supply the whole product payload instead of letting 327 * us resolve it. Sending the same variation twice is a no-op either way. 328 * 329 * @param {number|string} variationId Variation being viewed 330 * @param {Object} [product] Optional product payload, sent as-is 331 */ 332 setVariation: function (variationId, product) { 333 const id = String(variationId || ''); 334 335 if (!id || id === '0') { 336 return; 337 } 338 // an integration may beat PRODUCT_VIEWED to it - nothing to dedup against yet 339 this._viewedKeys = this._viewedKeys || {}; 340 this.sendProductViewedOnce( 341 product || this.resolveVariationPayload({ variation_id: id }), 342 id 343 ); 344 }, 345 346 /** 347 * found_variation is WooCommerce's own signal that every attribute is chosen and 348 * a variation matched. Bound delegated off body so a form rendered late - blocks, 349 * lazy themes, quick-view modals - is still covered. 350 */ 351 attachVariationListeners: function () { 352 const self = this; 353 354 $(document.body).on('found_variation', '.variations_form', function (event, variation) { 355 if (!variation || !variation.variation_id) { 356 return; 357 } 358 self.sendProductViewedOnce( 359 self.resolveVariationPayload(variation), 360 String(variation.variation_id) 361 ); 362 }); 363 364 // covers the block form, which fires no event at all 365 this.watchVariationInput(); 366 }, 367 368 /** 369 * WooCommerce often resolves a variation before we boot - the SDK wait runs over a 370 * second, and the form fires found_variation during its own init. The form parks
371 * the answer in its variation_id input, so read that rather than assuming we were 372 * listening at the right moment. 373 * 374 * @return {string} Resolved variation id, or '' when nothing is selected 375 */ 376 currentVariationId: function () { 377 // Classic puts this in .variations_form, blocks puts it in .single_variation_wrap 378 // inside its own form element - so key off the input, not either wrapper. 379 const inputs = document.querySelectorAll('form input[name="variation_id"], form input.variation_id'); 380 381 for (let i = 0; i < inputs.length; i++) { 382 const id = parseInt(inputs[i].value, 10); 383 if (id > 0) { 384 return String(id); 385 } 386 } 387 388 return ''; 389 }, 390 391 /** 392 * Catch variation changes on templates that announce nothing. 393 * 394 * The blocks "Add to Cart with Options" form runs on the Interactivity API and 395 * fires no jQuery event and no CustomEvent, so found_variation never arrives. 396 * What it does keep is the hidden variation_id input - WooCommerce renders it 397 * specifically so "extensions or Express Payment methods" can read the form 398 * state - so re-read that whenever the customer touches the form. Its value is 399 * set as a property by the Interactivity API, which a MutationObserver would 400 * never see, hence driving off the interaction instead. 401 */ 402 watchVariationInput: function () { 403 const self = this; 404 405 const recheck = function (event) { 406 // blocks renders its attribute options as buttons, so change alone misses them 407 if (!event.target || !event.target.closest || !event.target.closest('form')) { 408 return; 409 } 410 clearTimeout(self._inputTimer); 411 self._inputTimer = setTimeout(function () { 412 const id = self.currentVariationId(); 413 if (id) { 414 self.sendProductViewedOnce(self.resolveVariationPayload({ variation_id: id }), id); 415 } 416 }, VARIATION_INPUT_SETTLE_MS); 417 }; 418 419 document.addEventListener('change', recheck, true); 420 document.addEventListener('click', recheck, true); 421 }, 422 423 /** 424 * Turn a resolved variation into the product shape the pixel expects. 425 * 426 * Prefer the server-rendered variation: it carries the sku, categories and 427 * parent-derived permalink that the event payload has no idea about, and it 428 * matches what add-to-cart sends for the same variation. Past the pre-render cap 429 * there is nothing to look up, so patch the parent with what the event does give 430 * us - the id being right matters more than the rest. 431 * 432 * @param {Object} variation found_variation payload (or just {variation_id}) 433 * @return {Object} Product data for the pixel 434 */ 435 resolveVariationPayload: function (variation) { 436 const parent = this._viewedParent || {}; 437 const id = String(variation.variation_id); 438 const rendered = window.mcPixel.data.product_variations[id]; 439 440 if (rendered) { 441 return rendered; 442 } 443 444 const patched = $.extend({}, parent, { 445 id: id, 446 productId: parent.productId || parent.id || id 447 }); 448 if (variation.sku) { 449 patched.sku = variation.sku; 450 } 451 if (variation.display_price !== undefined && variation.display_price !== null) { 452 patched.price = parseFloat(variation.display_price); 453 } 454 if (variation.image && variation.image.full_src) { 455 patched.imageUrl = variation.image.full_src; 456 } 457 458 return patched; 459 }, 460 461 /** 462 * Send PRODUCT_VIEWED event 463 * 464 * @param {Object} product Product data 465 */ 466 sendProductViewed: function (product) { 467 if (!this.isPixelSDKReady()) return false; 468 469 window.$mcSite.pixel.api.track('PRODUCT_VIEWED', { 470 product: product 471 }).catch((error) => { 472 console.error('Mailchimp Pixel: Error tracking PRODUCT_VIEWED', error); 473 }); 474 475 return true; 476 }, 477 478 /** 479 * Send PRODUCT_ADDED_TO_CART event 480 * 481 * @param {Object} product Product data 482 */ 483 sendProductAddedToCart: function (product) { 484 if (!this.isPixelSDKReady()) return; 485 486 const cartId = this.getCartId(); 487 const eventData = { 488 cartId: cartId, 489 product: { 490 item: { 491 id: product.id, 492 productId: product.productId, 493 title: product.title, 494 price: product.price, 495 currency: product.currency, 496 sku: product.sku || '' 497 }, 498 quantity: product.quantity || 1, 499 price: product.price * (product.quantity || 1), 500 currency: product.currency 501 } 502 }; 503 504 window.$mcSite.pixel.api.track('PRODUCT_ADDED_TO_CART', eventData).catch((error) => { 505 console.error('Mailchimp Pixel: Error tracking PRODUCT_ADDED_TO_CART', error); 506 }); 507 }, 508 509 /** 510 * Send PRODUCT_REMOVED_FROM_CART event 511 * 512 * @param {Object} product Product data 513 */ 514 sendProductRemovedFromCart: function (product) { 515 if (!this.isPixelSDKReady()) return; 516 517 const cartId = this.getCartId(); 518 const eventData = { 519 cartId: cartId, 520 product: { 521 item: { 522 id: product.id, 523 productId: product.productId, 524 title: product.title, 525 price: product.price, 526 currency: product.currency, 527 sku: product.sku || '' 528 }, 529 quantity: product.quantity || 1, 530 price: product.price * (product.quantity || 1), 531 currency: product.currency 532 } 533 }; 534 535 window.$mcSite.pixel.api.track('PRODUCT_REMOVED_FROM_CART', eventData).catch((error) => { 536 console.error('Mailchimp Pixel: Error tracking PRODUCT_REMOVED_FROM_CART', error); 537 }); 538 }, 539 540 /** 541 * Send CART_VIEWED event 542 * 543 * @param {Object} cart Cart data 544 */ 545 sendCartViewed: function (cart) { 546 if (!this.isPixelSDKReady()) return; 547 548 window.$mcSite.pixel.api.track('CART_VIEWED', { 549 cart: cart 550 }).catch((error) => { 551 console.error('Mailchimp Pixel: Error tracking CART_VIEWED', error); 552 }); 553 }, 554 555 /** 556 * Send CHECKOUT_STARTED event 557 * 558 * @param {Object} checkout Checkout data 559 */ 560 sendCheckoutStarted: function (checkout) { 561 if (!this.isPixelSDKReady()) return; 562 563 window.$mcSite.pixel.api.track('CHECKOUT_STARTED', { 564 checkout: checkout 565 }).catch((error) => { 566 console.error('Mailchimp Pixel: Error tracking CHECKOUT_STARTED', error); 567 }); 568 }, 569 570 sendIdentityEvent: function(email) { 571 if (!this.isPixelSDKReady()) return; 572 window.$mcSite.pixel.api.identify({ 573 type: 'EMAIL', 574 value: email 575 }); 576 }, 577 578 /** 579 * Send PURCHASED event 580 * 581 * @param {Object} order Order data 582 */ 583 sendPurchased: function (order) { 584 if (!this.isPixelSDKReady()) return; 585 586 window.$mcSite.pixel.api.track('PURCHASED', { 587 order: order 588 }).catch((error) => { 589 console.error('Mailchimp Pixel: Error tracking PURCHASED', error); 590 }); 591 }, 592 593 /** 594 * Send PRODUCT_CATEGORY_VIEWED event 595 * 596 * @param {Object} category Category data 597 */ 598 sendCategoryViewed: function (category) { 599 if (!this.isPixelSDKReady()) return; 600 601 window.$mcSite.pixel.api.track('PRODUCT_CATEGORY_VIEWED', category).catch((error) => { 602 console.error('Mailchimp Pixel: Error tracking PRODUCT_CATEGORY_VIEWED', error); 603 }); 604 }, 605 606 /** 607 * Send SEARCH_SUBMITTED event 608 * 609 * @param {Object} search Search data 610 */ 611 sendSearchSubmitted: function (search) { 612 if (!this.isPixelSDKReady()) return; 613 614 window.$mcSite.pixel.api.track('SEARCH_SUBMITTED', search).catch((error) => { 615 console.error('Mailchimp Pixel: Error tracking SEARCH_SUBMITTED', error); 616 }); 617 }, 618 619 /** 620 * Find product by ID from pre-loaded products 621 * 622 * @param {number|string} productId Product ID 623 * @return {Object|null} Product data or null 624 */ 625 findProductById: function (productId) { 626 if (!window.mcPixel || !window.mcPixel.data || !window.mcPixel.data.products) { 627 return null; 628 } 629 630 const products = window.mcPixel.data.products; 631 const id = String(productId); 632 633 for (let i = 0; i < products.length; i++) { 634 if (String(products[i].id) === id) { 635 return products[i]; 636 } 637 } 638 639 return null; 640 }, 641 642 /** 643 * Debounced fetch for add-to-cart events. 644 * Each call resets the timer so rapid-fire triggers coalesce into one 645 * fetch that drains the entire PHP queue. 646 * If a fetch is already in flight, schedule another drain after it completes. 647 */ 648 fetchAndTrackAddToCart: function () { 649 var self = this; 650 if (!this.isPixelSDKReady()) return; 651 652 clearTimeout(this._atcTimer); 653 this._atcTimer = setTimeout(function () { 654 self._drainAddToCartQueue(); 655 }, 600); 656 }, 657 658 _drainAddToCartQueue: async function () { 659 if (this._atcFetching) return; 660 this._atcFetching = true; 661 662 try { 663 var res = await fetch(this.getRestBase() + 'pixel/atc', { 664 method: 'GET', 665 credentials: 'same-origin', 666 headers: { 'Accept': 'application/json' }, 667 }); 668 669 if (!res.ok) return; 670 671 var data = await res.json(); 672 if (!data) return; 673 674 var items = Array.isArray(data) ? data : [data]; 675 for (var i = 0; i < items.length; i++) { 676 this.sendProductAddedToCart(items[i]); 677 } 678 } catch (e) { 679 // no-op 680 } finally { 681 this._atcFetching = false;
682 } 683 }, 684 685 /** 686 * Debounced fetch for remove-from-cart events. 687 * Same debounce pattern as add-to-cart. 688 */ 689 fetchAndTrackRemoveFromCart: function () { 690 var self = this; 691 if (!this.isPixelSDKReady()) return; 692 693 clearTimeout(this._rfcTimer); 694 this._rfcTimer = setTimeout(function () { 695 self._drainRemoveFromCartQueue(); 696 }, 600); 697 }, 698 699 _drainRemoveFromCartQueue: async function () { 700 if (this._rfcFetching) return; 701 this._rfcFetching = true; 702 703 try { 704 var res = await fetch(this.getRestBase() + 'pixel/rfc', { 705 method: 'GET', 706 credentials: 'same-origin', 707 headers: { 'Accept': 'application/json' }, 708 }); 709 710 if (!res.ok) return; 711 712 var data = await res.json(); 713 if (!data) return; 714 715 var items = Array.isArray(data) ? data : [data]; 716 for (var i = 0; i < items.length; i++) { 717 this.sendProductRemovedFromCart(items[i]); 718 } 719 } catch (e) { 720 // no-op 721 } finally { 722 this._rfcFetching = false; 723 } 724 }, 725 726 /** 727 * Attach DOM and jQuery event listeners for cart add/remove. 728 * These serve as fallback triggers alongside the fetch interceptor. 729 */ 730 attachCartEventListeners: function () { 731 const self = this; 732 733 // Classic themes (jQuery events fired by WooCommerce's add-to-cart.js) 734 $(document.body).on('added_to_cart', function () { 735 self.fetchAndTrackAddToCart(); 736 }); 737 $(document.body).on('removed_from_cart', function () { 738 self.fetchAndTrackRemoveFromCart(); 739 }); 740 741 // WC Blocks DOM CustomEvents 742 document.body.addEventListener('wc-blocks_added_to_cart', function () { 743 self.fetchAndTrackAddToCart(); 744 }); 745 document.body.addEventListener('wc-blocks_removed_from_cart', function () { 746 self.fetchAndTrackRemoveFromCart(); 747 }); 748 }, 749 750 /** 751 * Intercept window.fetch to detect WooCommerce Store API cart mutations. 752 * 753 * This is the most reliable method for catching add-to-cart and remove-from-cart 754 * in block-based WooCommerce setups, where DOM events may not fire consistently. 755 * Watches for successful POST requests to the Store API cart endpoints. 756 */ 757 interceptStoreApiRequests: function () { 758 const self = this; 759 const originalFetch = window.fetch; 760 761 window.fetch = function (input, init) { 762 var promise = originalFetch.apply(this, arguments); 763 764 promise.then(function (response) { 765 try { 766 if (!response.ok) return; 767 768 var method = (init && init.method) ? init.method.toUpperCase() : 769 (input instanceof Request ? input.method.toUpperCase() : 'GET'); 770 if (method !== 'POST') return; 771 772 var url = typeof input === 'string' ? input : 773 (input instanceof Request ? input.url : String(input)); 774 775 if (/wc\/store\/v1\/cart\/add-item/.test(url) || /wc\/store\/v1\/cart\/update-item/.test(url)) { 776 self.fetchAndTrackAddToCart(); 777 } else if (/wc\/store\/v1\/cart\/remove-item/.test(url)) { 778 self.fetchAndTrackRemoveFromCart(); 779 } 780 } catch (e) { 781 // Silently ignore interceptor errors 782 } 783 }).catch(function () { 784 // Ignore - the original caller handles fetch errors 785 }); 786 787 return promise; 788 }; 789 }, 790 }; 791 792 // Initialize when DOM is ready 793 $(document).ready(function () { 794 MailchimpPixelTracking.init(); 795 }); 796 797})(jQuery); 798; 799(()=>{"use strict";const e=window.wp.hooks,i="mailchimp-woocommerce";let t=!1;function c(){return void 0!==window.$mcSite&&window.$mcSite.pixel&&void 0!==window.$mcSite.pixel.api&&"function"==typeof window.$mcSite.pixel.api.track}function r(){return window.mcPixel&&window.mcPixel.cartId?window.mcPixel.cartId:""}function n(e,i){window.mcPixel&&i&&String(i)!==String(e)&&(window.mcPixel.parentMap||(window.mcPixel.parentMap={}),window.mcPixel.parentMap[String(e)]=String(i))}function o(e,i){t&&c()&&window.$mcSite.pixel.api.track(e,i).catch(function(i){console.error("Mailchimp Pixel Blocks: Error tracking "+e,i)})}function a(e){const i=e.prices||{},t=i.currency_minor_unit||2,c=Math.pow(10,t),r=i.price?parseInt(i.price,10)/c:0,o=String(e.id),a=(e.extensions||{})["mailchimp-pixel"]||{},d=a.product_id?String(a.product_id):function(e){return(window.mcPixel&&window.mcPixel.parentMap||{})[String(e)]||""}(o)||o;return n(o,d),{id:o,productId:d,title:e.name||"",price:r,currency:(i.currency_code||"").toUpperCase(),sku:e.sku||"",imageUrl:e.images&&e.images.length>0?e.images[0].src:"",productUrl:e.permalink||"",vendor:"",categories:[]}}function d(e){const i=e.prices||{},t=i.currency_minor_unit||2,c=Math.pow(10,t),r=i.line_total?parseInt(i.line_total,10)/c:0;return{item:a(e),quantity:e.quantity||1,price:r,currency:(i.currency_code||"").toUpperCase()}}(0,e.addAction)("experimental__woocommerce_blocks-cart-add-item",i,function(e){if(!t)return;if(window.mcPixel&&window.mcPixel._handled.addToCart)return;const i=e.product;if(!i)return;const c=function(e){const i=e.prices||{},t=i.currency_minor_unit||2,c=Math.pow(10,t),r=i.price?parseInt(i.price,10)/c:0,o=String(e.id),a=((e.extensions||{})["mailchimp-pixel"]||{}).product_id||e.parent,d=a?String(a):"";return n(o,d),{id:o,productId:d||o,title:e.name||"",price:r,currency:(i.currency_code||"").toUpperCase(),sku:e.sku||"",imageUrl:e.images&&e.images.length>0?e.images[0].src:"",productUrl:e.permalink||"",vendor:"",categories:(e.categories||[]).map(function(e){return e.name||""})}}(i),a=e.quantity||1;o("PRODUCT_ADDED_TO_CART",{cartId:r(),product:{item:{id:c.id,productId:c.productId,title:c.title,price:c.price,currency:c.currency,sku:c.sku},quantity:a,price:c.price*a,currency:c.currency}}),window.mcPixel&&(window.mcPixel._handled.addToCart=!0,setTimeout(function(){window.mcPixel._handled.addToCart=!1},1e3))}),(0,e.addAction)("experimental__woocommerce_blocks-cart-remove-item",i,function(e){if(!t)return;if(window.mcPixel&&window.mcPixel._handled.removeFromCart)return;const i=e.product;if(!i)return;const c=a(i),n=e.quantity||1;o("PRODUCT_REMOVED_FROM_CART",{cartId:r(),product:{item:{id:c.id,productId:c.productId,title:c.title,price:c.price,currency:c.currency,sku:c.sku},quantity:n,price:c.price*n,currency:c.currency}}),window.mcPixel&&(window.mcPixel._handled.removeFromCart=!0,setTimeout(function(){window.mcPixel._handled.removeFromCart=!1},1e3))}),(0,e.addAction)("experimental__woocommerce_blocks-checkout-render-checkout-form",i,function(e){if(!t)return;if(window.mcPixel&&window.mcPixel._handled.checkout)return;const i=e.storeCart;i&&i.cartItems&&0!==i.cartItems.length&&(o("CHECKOUT_STARTED",{checkout:function(e){const i=e.cartTotals||{},t=i.currency_minor_unit||2,c=Math.pow(10,t),n=(e.cartItems||[]).map(d),o=r();return{id:"checkout_"+o,cartId:o,lineItems:n,subtotalPrice:i.total_items?parseInt(i.total_items,10)/c:0,totalTax:i.total_tax?parseInt(i.total_tax,10)/c:0,totalShipping:i.total_shipping?parseInt(i.total_shipping,10)/c:0,totalPrice:i.total_price?parseInt(i.total_price,10)/c:0,currency:(i.currency_code||"").toUpperCase()}}(i)}),window.mcPixel&&(window.mcPixel._handled.checkout=!0))}),(0,e.addAction)("experimental__woocommerce_blocks-product-list-render",i,function(){t&&(window.mcPixel&&window.mcPixel._handled.category||window.mcPixel&&window.mcPixel.data&&window.mcPixel.data.category&&(o("PRODUCT_CATEGORY_VIEWED",window.mcPixel.data.category),window.mcPixel&&(window.mcPixel._handled.category=!0)))}),(0,e.addAction)("experimental__woocommerce_blocks-product-search",i,function(e){if(!t)return;if(window.mcPixel&&window.mcPixel._handled.search)return;const i=e.searchTerm||"";i&&(o("SEARCH_SUBMITTED",{query:i}),window.mcPixel&&(window.mcPixel._handled.search=!0))}),function(){let e=0;!function i(){if(c())return void setTimeout(function(){t=!0},1e3);if(e>=20)return;const r=Math.min(100*Math.pow(2,e),5e3);e+=1,setTimeout(i,r)}()}()})();; 800(function ($) { 801 'use strict'; 802 803 function login_dropdown() { 804 $('.site-header-account').mouseenter(function () { 805 if ($('.account-dropdown .account-wrap').length == 0) { 806 $('.account-dropdown', this).append($('.account-wrap')); 807 } 808 809 }); 810 } 811 812 function handleWindow() { 813 var body = document.querySelector('body'); 814 815 if (window.innerWidth > body.clientWidth + 5) { 816 body.classList.add('has-scrollbar'); 817 body.setAttribute('style', '--scroll-bar: ' + (window.innerWidth - body.clientWidth) + 'px'); 818 } else { 819 body.classList.remove('has-scrollbar'); 820 } 821 } 822 823 function minHeight() { 824 var bodyHeight = $(window).outerHeight(), 825 headerHeight = $('.site-header').outerHeight(true), 826 contentHeight = $('.site-content').height(), 827 contentouterHeight = $('.site-content').outerHeight(true), 828 footerHeight = $('.site-footer').outerHeight(true); 829 if ((bodyHeight - footerHeight - headerHeight - contentouterHeight) > 0) { 830 831 $('.site-content').css({ 832 'min-height': bodyHeight - (headerHeight + footerHeight) - (contentouterHeight - contentHeight) 833 }); 834 } 835 } 836 837 minHeight(); 838 handleWindow(); 839 login_dropdown(); 840})(jQuery); 841; 842!function(){"use strict";var e=-1<navigator.userAgent.toLowerCase().indexOf("webkit"),t=-1<navigator.userAgent.toLowerCase().indexOf("opera"),n=-1<navigator.userAgent.toLowerCase().indexOf("msie");(e||t||n)&&document.getElementById&&window.addEventListener&&window.addEventListener("hashchange",function(){var e=document.getElementById(location.hash.substring(1));e&&(/^(?:a|select|input|button|textarea)$/i.test(e.tagName)||(e.tabIndex=-1),e.focus())},!1)}();; 843(function ($) { 844 'use strict'; 845 $( document ).ready(function() { 846 $('.button-search-popup').on('click', function (e) { 847 e.preventDefault(); 848 $('html').toggleClass('search-popup-active'); 849 }); 850 851 $('.site-search-popup-close').on('click', function (e) { 852 e.preventDefault(); 853 $('html').toggleClass('search-popup-active'); 854 }); 855 }); 856})(jQuery);; 857(function($){ 858 'use strict'; 859 $('body').on('click', '.supplero-login-form-ajax button[type="submit"]', (event) => { 860 var $this = $(event.currentTarget); 861 var $form = $this.closest('form'); 862 $.ajax({ 863 type: 'POST', 864 url: suppleroAjax.ajaxurl, 865 data: $form.serialize(), 866 beforeSend: () => { 867 $form.addClass('loading'); 868 $form.find('input,button').prop('disabled', true); 869 }, 870 success: (response) => { 871 if (response.status) { 872 $form.find('.result-error').remove(); 873 location.reload(); 874 } else { 875 if($form.find('.result-error').length <= 0){ 876 $form.prepend(`<div class="result-error woocommerce-message woocommerce-error">${response.msg}</div>`); 877 } 878 } 879 },
880 complete: function () { 881 $form.find('input,button').prop('disabled', false); 882 $form.removeClass('loading'); 883 } 884 }); 885 886 return false; 887 }); 888})(jQuery); 889 890; 891/* Elementor bundles are self-contained; this file exists for the elementor-webpack-runtime handle. */ 892;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.