PageSourceSearch

https://brennostudio.com/_static/??-eJy1jUEOwjAMBD+EG1pRIQ6It6QmFN…Tz7iukParo5D5otDhT6WX/gfb3drZtN09btA9Iwf1Y=

js brennostudio.com collected 2026-10-10 02:10:40 UTC 35,646 bytes, 800 lines download raw bytes

1(()=>{function e(e,n){if(e){if("string"==typeof e)return t(e,n);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?t(e,n):void 0}}function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function n(e,t,n){if(n){var r=e.getElementsByClassName(t)[0];r&&(r.textContent=n)}}function r(t,r){for(var l=0,a=Object.entries(r);l<a.length;l++){var o=(i=a[l],u=2,function(e){if(Array.isArray(e))return e}(i)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,l,a,o,i=[],u=!0,s=!1;try{if(a=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;u=!1}else for(;!(u=(r=a.call(n)).done)&&(i.push(r.value),i.length!==t);u=!0);}catch(e){s=!0,l=e}finally{try{if(!u&&null!=n.return&&(o=n.return(),Object(o)!==o))return}finally{if(s)throw l}}return i}}(i,u)||e(i,u)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}());n(t,o[0],o[1])}var i,u}!function(){var t,n=function(t,n){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!r){if(Array.isArray(t)||(r=e(t))){r&&(t=r);var l=0,a=function(){};return{s:a,n:function(){return l>=t.length?{done:!0}:{done:!1,value:t[l++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i=!0,u=!1;return{s:function(){r=r.call(t)},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,o=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw o}}}}(document.getElementsByClassName("cm-contact-form"));try{for(n.s();!(t=n.n()).done;)t.value.onsubmit=function(e){var t,n,l,a,o,i,u,s;e.preventDefault();var m=e.target,c=null===(t=m.getElementsByClassName("firstName")[0])||void 0===t?void 0:t.value,f=null===(n=m.getElementsByClassName("lastName")[0])||void 0===n?void 0:n.value,v=null===(l=m.getElementsByClassName("email")[0])||void 0===l?void 0:l.value,d=null===(a=m.getElementsByClassName("telephone")[0])||void 0===a?void 0:a.value,y=null===(o=m.getElementsByClassName("consent_check")[0])||void 0===o?void 0:o.checked,b=(null===(i=m.getElementsByClassName("list_id")[0])||void 0===i?void 0:i.value)||null;jQuery.post(null===(u=ce4wp_form_submit_data)||void 0===u?void 0:u.url,{action:"ce4wp_form_submission",
1nonce:null===(s=ce4wp_form_submit_data)||void 0===s?void 0:s.nonce,first_name:c,last_name:f,email:v,telephone:d,consent:y,list_id:b}).done((function(){var e,t=m.parentElement,n=null===(e=t.getElementsByClassName("onSubmissionSetting")[0])||void 0===e?void 0:e.value;if("redirect"===n){var l,a=new RegExp("^(https?)://"),o=null===(l=t.getElementsByClassName("redirect")[0])||void 0===l?void 0:l.value;a.test(o)||(o="https://"+o),window.location.href=o}else m.style.visibility="hidden","summary"===n&&r(t,{firstNameSummary:c?c+" ":null,lastNameSummary:f,telephoneSummary:d,emailSummary:v}),t.getElementsByClassName("onSubmission")[0].style.display="block"}))}}catch(e){n.e(e)}finally{n.f()}}()})();;
2(()=>{const e=document.querySelectorAll(".coblocks-animate");if("IntersectionObserver"in window){const t=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&(e.target.classList.add(e.target.dataset.coblocksAnimation),t.unobserve(e.target))}))}),{threshold:[.15]});e.forEach((e=>{t.observe(e)}))}else e.forEach((e=>{e.classList.remove("coblocks-animate"),delete e.dataset.coblocksAnimation}))})();;
3var 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"}
3,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}();mailchimpReady(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)}});;
4/**
5 * Mailchimp Pixel Tracking for WooCommerce
6 *
7 * Listens for WooCommerce events and sends them to the Mailchimp Pixel SDK.
8 * Only tracks if window.$mcSite.pixel.api is available (MC.js loaded).
9 *
10 * @package MailChimp_WooCommerce
11 * @since 1.0.0
12 */
13
14(function ($) {
15	'use strict';
16
17	/**
18	 * Default config for waitForPixelSDK exponential backoff
19	 */
20	const PIXEL_SDK_WAIT_CONFIG = {
21		initialDelayMs: 100,
22		maxDelayMs: 5000,
23		maxAttempts: 20
24	};
25
26	/**
27	 * How long PRODUCT_VIEWED waits on a variable product for WooCommerce to resolve a
28	 * default or deep-linked variation before settling for the parent.
29	 */
30	const VARIATION_RESOLVE_WAIT_MS = 1200;
31
32	/**
33	 * Settle time after a form interaction before re-reading the variation id input.
34	 * The blocks form writes it through the Interactivity API on the next render.
35	 */
36	const VARIATION_INPUT_SETTLE_MS = 150;
37
38	/**
39	 * Mailchimp Pixel Tracking Handler
40	 */
41	const MailchimpPixelTracking = {
42		// Debounce timers for cart fetch calls
43		_atcTimer: null,
44		_rfcTimer: null,
45		_atcFetching: false,
46		_rfcFetching: false,
47		// PRODUCT_VIEWED state for a variable product being resolved to a variation
48		_viewTimer: null,
49		_viewedParent: null,
50		_viewedKeys: null,
51		_inputTimer: null,
52
53		/**
54		 * Initialize tracking.
55		 * Waits for Pixel SDK with exponential backoff, then sends page events and attaches handlers.
56		 */
57		init: function () {
58			const self = this;
59
60			// Registered before the SDK wait on purpose: that wait runs for seconds, and an
61			// integration that resolves its variation early must not race us.
62			window.mcPixel = window.mcPixel || {};
63			window.mcPixel.setVariation = function (variationId, product) {
64				self.setVariation(variationId, product);
65			};
66			document.addEventListener('mailchimp_woocommerce_set_variation', function (event) {
67				const detail = event.detail || {};
68				self.setVariation(detail.variationId, detail.product);
69			});
70
71			this.waitForPixelSDK(PIXEL_SDK_WAIT_CONFIG)
72				.then(function () {
73					self.sendPageEvents();
74					self.attachCartEventListeners();
75					self.interceptStoreApiRequests();
76					console.log('Mailchimp Pixel SDK loaded.');
77				})
78				.catch(function (e) {
79					console.log('Mailchimp Pixel SDK not loaded within timeout. Tracking disabled.', e);
80				});
81		},
82
83		/**
84		 * Wait for Pixel SDK to become available using exponential backoff.
85		 *
86		 * @param {Object} options Optional config
87		 * @param {number} options.initialDelayMs First delay in ms (default 100)
88		 * @param {number} options.maxDelayMs Cap on delay in ms (default 5000)
89		 * @param {number} options.maxAttempts Max number of attempts (default 20)
90		 * @return {Promise<void>}
90 Resolves when SDK is available, rejects after maxAttempts
91		 */
92		waitForPixelSDK: function (options) {
93			const config = options || {};
94			const initialDelayMs = config.initialDelayMs !== undefined ? config.initialDelayMs : PIXEL_SDK_WAIT_CONFIG.initialDelayMs;
95			const maxDelayMs = config.maxDelayMs !== undefined ? config.maxDelayMs : PIXEL_SDK_WAIT_CONFIG.maxDelayMs;
96			const maxAttempts = config.maxAttempts !== undefined ? config.maxAttempts : PIXEL_SDK_WAIT_CONFIG.maxAttempts;
97
98			function isSDKReady() {
99				return typeof window.$mcSite !== 'undefined' &&
100					window.$mcSite.pixel &&
101					typeof window.$mcSite.pixel.api !== 'undefined' &&
102					typeof window.$mcSite.pixel.api.track === 'function' &&
103					window.$mcSite.pixel.installed === true;
104			}
105
106			return new Promise(function (resolve, reject) {
107				let attempt = 0;
108
109				function scheduleCheck() {
110					if (isSDKReady()) {
111						// FIXME: temporary wait to accommodate for not being able to detect ready state
112						console.warn('Pixel SDK - remediation for pixel ready issue')
113						setTimeout(function () {
114							resolve();
115						}, 1000);
116						return;
117					}
118					if (attempt >= maxAttempts) {
119						reject(new Error('Pixel SDK not available'));
120						return;
121					}
122
123					const delay = Math.min(
124						initialDelayMs * Math.pow(2, attempt),
125						maxDelayMs
126					);
127					attempt += 1;
128
129					setTimeout(scheduleCheck, delay);
130				}
131
132				scheduleCheck();
133			});
134		},
135
136		/**
137		 * Synchronous check if Pixel SDK is available (e.g. before each track call).
138		 *
139		 * @return {boolean} True if SDK is available
140		 */
141		isPixelSDKReady: function () {
142			return typeof window.$mcSite !== 'undefined' &&
143				window.$mcSite.pixel &&
144				typeof window.$mcSite.pixel.api !== 'undefined' &&
145				typeof window.$mcSite.pixel.api.track === 'function';
146		},
147
148		/**
149		 * Get cart ID from window.mcPixel
150		 *
151		 * @return {string} Cart ID
152		 */
153		getCartId: function () {
154			return window.mcPixel && window.mcPixel.cartId ? window.mcPixel.cartId : '';
155		},
156
157		/**
158		 * Get the REST API base URL from the localized config.
159		 *
160		 * @return {string} REST base URL
161		 */
162		getRestBase: function () {
163			return (window.mcPixelConfig && window.mcPixelConfig.restBase) || '/wp-json/mailchimp-for-woocommerce/v1/';
164		},
165
166		/**
167		 * Send page-level events based on pre-populated data
168		 */
169		sendPageEvents: function () {
170			if (!window.mcPixel || !window.mcPixel.data) {
171				return;
172			}
173
174			const data = window.mcPixel.data;
175			const events = data.events || [];
176
177			// Send events based on what was set by PHP
178			events.forEach((eventType) => {
179				//console.log('Mailchimp Pixel: Sending event', eventType, data);
180				switch (eventType) {
181					case 'PRODUCT_ADDED_TO_CART':
182						if (data.added_to_cart) {
183							var atcItems = Array.isArray(data.added_to_cart) ? data.added_to_cart : [data.added_to_cart];
184							for (var ai = 0; ai < atcItems.length; ai++) {
185								this.sendProductAddedToCart(atcItems[ai]);
186							}
187						}
188						break;
189					case 'PRODUCT_REMOVED_FROM_CART':
190						if (data.removed_from_cart) {
191							var rfcItems = Array.isArray(data.removed_from_cart) ? data.removed_from_cart : [data.removed_from_cart];
192							for (var ri = 0; ri < rfcItems.length; ri++) {
193								this.sendProductRemovedFromCart(rfcItems[ri]);
194							}
195						}
196						break;
197					case 'IDENTITY':
198						if (data.identity && data.identity.email) {
199							this.sendIdentityEvent(data.identity.email);
200						}
201						break;
202					case 'PRODUCT_VIEWED':
203						// Skip if an add-to-cart or remove-from-cart already fired for the same product.
204						// The cart action implies the view, so firing both is redundant.
205						if (data.product) {
206							const viewedId = String(data.product.productId || data.product.id);
207							const atcArr = Array.isArray(data.added_to_cart) ? data.added_to_cart : (data.added_to_cart ? [data.added_to_cart] : []);
208							const rfcArr = Array.isArray(data.removed_from_cart) ? data.removed_from_cart : (data.removed_from_cart ? [data.removed_from_cart] : []);
209							const atcMatch = atcArr.some(function (p) { return String(p.productId || p.id
209) === viewedId; });
210							const rfcMatch = rfcArr.some(function (p) { return String(p.productId || p.id) === viewedId; });
211
212							if (
213								(events.includes('PRODUCT_ADDED_TO_CART') && atcMatch) ||
214								(events.includes('PRODUCT_REMOVED_FROM_CART') && rfcMatch)
215							) {
216								//console.log('Mailchimp Pixel: Skipping PRODUCT_VIEWED (superseded by cart event for same product)');
217								break;
218							}
219							this.trackProductViewed(data.product);
220						}
221						break;
222					case 'CART_VIEWED':
223						if (data.cart) {
224							this.sendCartViewed(data.cart);
225						}
226						break;
227					case 'CHECKOUT_STARTED':
228						if (data.checkout) {
229							this.sendCheckoutStarted(data.checkout);
230							window.mcPixel._handled.checkout = true;
231						}
232						break;
233					case 'PURCHASED':
234						if (data.order) {
235							this.sendPurchased(data.order);
236						}
237						break;
238					case 'PRODUCT_CATEGORY_VIEWED':
239						if (data.category) {
240							this.sendCategoryViewed(data.category);
241							window.mcPixel._handled.category = true;
242						}
243						break;
244					case 'SEARCH_SUBMITTED':
245						if (data.search) {
246							this.sendSearchSubmitted(data.search);
247							window.mcPixel._handled.search = true;
248						}
249						break;
250				}
251			});
252		},
253
254		/**
255		 * PRODUCT_VIEWED for the single product page.
256		 *
257		 * A variable product renders before the customer has picked anything, so the
258		 * server-side payload only knows the parent. WooCommerce announces the chosen
259		 * variation on the .variations_form with found_variation, so we hold the event
260		 * until that fires - or until it's clear nothing is going to resolve.
261		 *
262		 * @param {Object} product Parent product data from PHP
263		 */
264		trackProductViewed: function (product) {
265			const self = this;
266			this._viewedKeys = this._viewedKeys || {};
267
268			// not a variable product - the server already knew everything there is to know
269			if (!window.mcPixel.data.product_variations) {
270				this.sendProductViewedOnce(product, product.id);
271				return;
272			}
273
274			this._viewedParent = product;
275			this.attachVariationListeners();
276
277			const resolved = this.currentVariationId();
278			if (resolved) {
279				this.sendProductViewedOnce(this.resolveVariationPayload({ variation_id: resolved }), resolved);
280				return;
281			}
282
283			// nothing picked yet, but a default variation or ?attribute_* deep link still
284			// resolves a tick after the form initialises - give WooCommerce that tick
285			// before settling for the parent
286			this._viewTimer = setTimeout(function () {
287				self.sendProductViewedOnce(product, product.id);
288			}, VARIATION_RESOLVE_WAIT_MS);
289		},
290
291		/**
292		 * One PRODUCT_VIEWED per thing actually viewed: the parent, then each variation
293		 * the customer lands on. Switching back to an earlier pick sends nothing, so
294		 * clicking through a dropdown doesn't turn into a burst of events.
295		 *
296		 * @param {Object} product Product data to send
297		 * @param {string} key Dedup key - the variation id, or the parent id
298		 */
299		sendProductViewedOnce: function (product, key) {
300			clearTimeout(this._viewTimer);
301			if (this._viewedKeys[key]) {
302				return;
303			}
304			// only claim the key once it actually went out - an integration can call
305			// setVariation() before the SDK is up, and that must not count as sent
306			if (this.sendProductViewed(product)) {
307				this._viewedKeys[key] = true;
308			}
309		},
310
311		/**
312		 * Public API for themes, swatch plugins and custom templates.
313		 *
314		 * Detection covers what WooCommerce itself ships: the classic variations form,
315		 * and the hidden variation_id input the block form maintains. Anything else - a
316		 * custom swatch UI, a page builder widget, a headless front end - can announce
317		 * the selection itself, either directly:
318		 *
319		 *     window.mcPixel.setVariation( 123 );
320		 *
321		 * or, when it cannot reach our global, by dispatching an event:
322		 *
323		 *     document.dispatchEvent( new CustomEvent(
324		 *         'mailchimp_woocommerce_set_variation',
325		 *         { detail: { variationId: 123 } }
326		 *     ) );
327		 *
328		 * Pass a second argument to supply the whole product payload instead of letting
329		 * us resolve it. Sending the same variation twice is a no-op either way.
330		 *
331		 * @param {number|string} variationId Variation being viewed
332		 * @param {Object} [product] Optional product payload, sent as-is
333		 */
334		setVariation: function (variationId, product) {
335			const id = String(variationId || '');
336
337			if (!id || id === '0') {
338				return;
339			}
340			// an integration may beat PRODUCT_VIEWED to it - nothing to dedup against yet
341			this._viewedKeys = this._viewedKeys || {};
342			this.sendProductViewedOnce(
343				product || this.resolveVariationPayload({ variation_id: id }),
344				id
345			);
346		},
347
348		/**
349		 * found_variation is WooCommerce's own signal that every attribute is chosen and
350		 * a variation matched. Bound delegated off body so a form rendered late - blocks,
351		 * lazy themes, quick-view modals - is still covered.
352		 */
353		attachVariationListeners: function () {
354			const self = this;
355
356			$(document.body).on('found_variation', '.variations_form', function (event, variation) {
357				if (!variation || !variation.variation_id) {
358					return;
359				}
360				self.sendProductViewedOnce(
361					self.resolveVariationPayload(variation),
362					String(variation.variation_id)
363				);
364			});
365
366			// covers the block form, which fires no event at all
367			this.watchVariationInput();
368		},
369
370		/**
371		 * WooCommerce often resolves a variation before we boot - the SDK wait runs over a
372		 * second, and the form fires found_variation during its own init. The form parks
373		 * the answer in its variation_id input, so read that rather than assuming we were
374		 * listening at the right moment.
375		 *
376		 * @return {string} Resolved variation id, or '' when nothing is selected
377		 */
378		currentVariationId: function () {
379			// Classic puts this in .variations_form, blocks puts it in .single_variation_wrap
380			// inside its own form element - so key off the input, not either wrapper.
381			const inputs = document.querySelectorAll('form input[name="variation_id"], form input.variation_id');
382
383			for (let i = 0; i < inputs.length; i++) {
384				const id = parseInt(inputs[i].value, 10);
385				if (id > 0) {
386					return String(id);
387				}
388			}
389
390			return '';
391		},
392
393		/**
394		 * Catch variation changes on templates that announce nothing.
395		 *
396		 * The blocks "Add to Cart with Options" form runs on the Interactivity API and
397		 * fires no jQuery event and no CustomEvent, so found_variation never arrives.
398		 * What it does keep is the hidden variation_id input - WooCommerce renders it
399		 * specifically so "extensions or Express Payment methods" can read the form
400		 * state - so re-read that whenever the customer touches the form. Its value is
401		 * set as a property by the Interactivity API, which a MutationObserver would
402		 * never see, hence driving off the interaction instead.
403		 */
404		watchVariationInput: function () {
405			const self = this;
406
407			const recheck = function (event) {
408				// blocks renders its attribute options as buttons, so change alone misses them
409				if (!event.target || !event.target.closest || !event.target.closest('form')) {
410					return;
411				}
412				clearTimeout(self._inputTimer);
413				self._inputTimer = setTimeout(function () {
414					const id = self.currentVariationId();
415					if (id) {
416						self.sendProductViewedOnce(self.resolveVariationPayload({ variation_id: id }), id);
417					}
418				}, VARIATION_INPUT_SETTLE_MS);
419			};
420
421			document.addEventListener('change', recheck, true);
422			document.addEventListener('click', recheck, true);
423		},
424
425		/**
426		 * Turn a resolved variation into the product shape the pixel expects.
427		 *
428		 * Prefer the server-rendered variation: it carries the sku, categories and
429		 * parent-derived permalink that the event payload has no idea about, and it
430		 * matches what add-to-cart sends for the same variation. Past the pre-render cap
431		 * there is nothing to look up, so patch the parent with what the event does give
432		 * us - the id being right matters more than the rest.
433		 *
434		 * @param {Object} variation found_variation payload (or just {variation_id})
435		 * @return {Object} Product data for the pixel
436		 */
437		resolveVariationPayload: function (variation) {
438			const parent = this._viewedParent || {};
439			const id = String(variation.variation_id);
440			const rendered = window.mcPixel.data.product_variations[id];
441
442			if (rendered) {
443				return rendered;
444			}
445
446			const patched = $.extend({}, parent, {
447				id: id,
448				productId: parent.productId || parent.id || id
449			});
450			if (variation.sku) {
451				patched.sku = variation.sku;
452			}
453			if (variation.display_price !== undefined && variation.display_price !== null) {
454				patched.price = parseFloat(variation.display_price);
455			}
456			if (variation.image && variation.image.full_src) {
457				patched.imageUrl = variation.image.full_src;
458			}
459
460			return patched;
461		},
462
463		/**
464		 * Send PRODUCT_VIEWED event
465		 *
466		 * @param {Object} product Product data
467		 */
468		sendProductViewed: function (product) {
469			if (!this.isPixelSDKReady()) return false;
470
471			window.$mcSite.pixel.api.track('PRODUCT_VIEWED', {
472				product: product
473			}).catch((error) => {
474				console.error('Mailchimp Pixel: Error tracking PRODUCT_VIEWED', error);
475			});
476
477			return true;
478		},
479
480		/**
481		 * Send PRODUCT_ADDED_TO_CART event
482		 *
483		 * @param {Object} product Product data
484		 */
485		sendProductAddedToCart: function (product) {
486			if (!this.isPixelSDKReady()) return;
487
488			const cartId = this.getCartId();
489			const eventData = {
490				cartId: cartId,
491				product: {
492					item: {
493						id: product.id,
494						productId: product.productId,
495						title: product.title,
496						price: product.price,
497						currency: product.currency,
498						sku: product.sku || ''
499					},
500					quantity: product.quantity || 1,
501					price: product.price * (product.quantity || 1),
502					currency: product.currency
503				}
504			};
505
506			window.$mcSite.pixel.api.track('PRODUCT_ADDED_TO_CART', eventData).catch((error) => {
507				console.error('Mailchimp Pixel: Error tracking PRODUCT_ADDED_TO_CART', error);
508			});
509		},
510
511		/**
512		 * Send PRODUCT_REMOVED_FROM_CART event
513		 *
514		 * @param {Object} product Product data
515		 */
516		sendProductRemovedFromCart: function (product) {
517			if (!this.isPixelSDKReady()) return;
518
519			const cartId = this.getCartId();
520			const eventData = {
521				cartId: cartId,
522				product: {
523					item: {
524						id: product.id,
525						productId: product.productId,
526						title: product.title,
527						price: product.price,
528						currency: product.currency,
529						sku: product.sku || ''
530					},
531					quantity: product.quantity || 1,
532					price: product.price * (product.quantity || 1),
533					currency: product.currency
534				}
535			};
536
537			window.$mcSite.pixel.api.track('PRODUCT_REMOVED_FROM_CART', eventData).catch((error) => {
538				console.error('Mailchimp Pixel: Error tracking PRODUCT_REMOVED_FROM_CART', error);
539			});
540		},
541
542		/**
543		 * Send CART_VIEWED event
544		 *
545		 * @param {Object} cart Cart data
546		 */
547		sendCartViewed: function (cart) {
548			if (!this.isPixelSDKReady()) return;
549
550			window.$mcSite.pixel.api.track('CART_VIEWED', {
551				cart: cart
552			}).catch((error) => {
553				console.error('Mailchimp Pixel: Error tracking CART_VIEWED', error);
554			});
555		},
556
557		/**
558		 * Send CHECKOUT_STARTED event
559		 *
560		 * @param {Object} checkout Checkout data
561		 */
562		sendCheckoutStarted: function (checkout) {
563			if (!this.isPixelSDKReady()) return;
564
565			window.$mcSite.pixel.api.track('CHECKOUT_STARTED', {
566				checkout: checkout
567			}).catch((error) => {
568				console.error('Mailchimp Pixel: Error tracking CHECKOUT_STARTED', error);
569			});
570		},
571
572		sendIdentityEvent: function(email) {
573			if (!this.isPixelSDKReady()) return;
574			window.$mcSite.pixel.api.identify({
575				type: 'EMAIL',
576				value: email
577			});
578		},
579
580		/**
581		 * Send PURCHASED event
582		 *
583		 * @param {Object} order Order data
584		 */
585		sendPurchased: function (order) {
586			if (!this.isPixelSDKReady()) return;
587
588			window.$mcSite.pixel.api.track('PURCHASED', {
589				order: order
590			}).catch((error) => {
591				console.error('Mailchimp Pixel: Error tracking PURCHASED', error);
592			});
593		},
594
595		/**
596		 * Send PRODUCT_CATEGORY_VIEWED event
597		 *
598		 * @param {Object} category Category data
599		 */
600		sendCategoryViewed: function (category) {
601			if (!this.isPixelSDKReady()) return;
602
603			window.$mcSite.pixel.api.track('PRODUCT_CATEGORY_VIEWED', category).catch((error) => {
604				console.error('Mailchimp Pixel: Error tracking PRODUCT_CATEGORY_VIEWED', error);
605			});
606		},
607
608		/**
609		 * Send SEARCH_SUBMITTED event
610		 *
611		 * @param {Object} search Search data
612		 */
613		sendSearchSubmitted: function (search) {
614			if (!this.isPixelSDKReady()) return;
615
616			window.$mcSite.pixel.api.track('SEARCH_SUBMITTED', search).catch((error) => {
617				console.error('Mailchimp Pixel: Error tracking SEARCH_SUBMITTED', error);
618			});
619		},
620
621		/**
622		 * Find product by ID from pre-loaded products
623		 *
624		 * @param {number|string} productId Product ID
625		 * @return {Object|null} Product data or null
626		 */
627		findProductById: function (productId) {
628			if (!window.mcPixel || !window.mcPixel.data || !window.mcPixel.data.products) {
629				return null;
630			}
631
632			const products = window.mcPixel.data.products;
633			const id = String(productId);
634
635			for (let i = 0; i < products.length; i++) {
636				if (String(products[i].id) === id) {
637					return products[i];
638				}
639			}
640
641			return null;
642		},
643
644		/**
645		 * Debounced fetch for add-to-cart events.
646		 * Each call resets the timer so rapid-fire triggers coalesce into one
647		 * fetch that drains the entire PHP queue.
648		 * If a fetch is already in flight, schedule another drain after it completes.
649		 */
650		fetchAndTrackAddToCart: function () {
651			var self = this;
652			if (!this.isPixelSDKReady()) return;
653
654			clearTimeout(this._atcTimer);
655			this._atcTimer = setTimeout(function () {
656				self._drainAddToCartQueue();
657			}, 600);
658		},
659
660		_drainAddToCartQueue: async function () {
661			if (this._atcFetching) return;
662			this._atcFetching = true;
663
664			try {
665				var res = await fetch(this.getRestBase() + 'pixel/atc', {
666					method: 'GET',
667					credentials: 'same-origin',
668					headers: { 'Accept': 'application/json' },
669				});
670
671				if (!res.ok) return;
672
673				var data = await res.json();
674				if (!data) return;
675
676				var items = Array.isArray(data) ? data : [data];
677				for (var i = 0; i < items.length; i++) {
678					this.sendProductAddedToCart(items[i]);
679				}
680			} catch (e) {
681				// no-op
682			} finally {
683				this._atcFetching = false;
684			}
685		},
686
687		/**
688		 * Debounced fetch for remove-from-cart events.
689		 * Same debounce pattern as add-to-cart.
690		 */
691		fetchAndTrackRemoveFromCart: function () {
692			var self = this;
693			if (!this.isPixelSDKReady()) return;
694
695			clearTimeout(this._rfcTimer);
696			this._rfcTimer = setTimeout(function () {
697				self._drainRemoveFromCartQueue();
698			}, 600);
699		},
700
701		_drainRemoveFromCartQueue: async function () {
702			if (this._rfcFetching) return;
703			this._rfcFetching = true;
704
705			try {
706				var res = await fetch(this.getRestBase() + 'pixel/rfc', {
707					method: 'GET',
708					credentials: 'same-origin',
709					headers: { 'Accept': 'application/json' },
710				});
711
712				if (!res.ok) return;
713
714				var data = await res.json();
715				if (!data) return;
716
717				var items = Array.isArray(data) ? data : [data];
718				for (var i = 0; i < items.length; i++) {
719					this.sendProductRemovedFromCart(items[i]);
720				}
721			} catch (e) {
722				// no-op
723			} finally {
724				this._rfcFetching = false;
725			}
726		},
727
728		/**
729		 * Attach DOM and jQuery event listeners for cart add/remove.
730		 * These serve as fallback triggers alongside the fetch interceptor.
731		 */
732		attachCartEventListeners: function () {
733			const self = this;
734
735			// Classic themes (jQuery events fired by WooCommerce's add-to-cart.js)
736			$(document.body).on('added_to_cart', function () {
737				self.fetchAndTrackAddToCart();
738			});
739			$(document.body).on('removed_from_cart', function () {
740				self.fetchAndTrackRemoveFromCart();
741			});
742
743			// WC Blocks DOM CustomEvents
744			document.body.addEventListener('wc-blocks_added_to_cart', function () {
745				self.fetchAndTrackAddToCart();
746			});
747			document.body.addEventListener('wc-blocks_removed_from_cart', function () {
748				self.fetchAndTrackRemoveFromCart();
749			});
750		},
751
752		/**
753		 * Intercept window.fetch to detect WooCommerce Store API cart mutations.
754		 *
755		 * This is the most reliable method for catching add-to-cart and remove-from-cart
756		 * in block-based WooCommerce setups, where DOM events may not fire consistently.
757		 * Watches for successful POST requests to the Store API cart endpoints.
758		 */
759		interceptStoreApiRequests: function () {
760			const self = this;
761			const originalFetch = window.fetch;
762
763			window.fetch = function (input, init) {
764				var promise = originalFetch.apply(this, arguments);
765
766				promise.then(function (response) {
767					try {
768						if (!response.ok) return;
769
770						var method = (init && init.method) ? init.method.toUpperCase() :
771							(input instanceof Request ? input.method.toUpperCase() : 'GET');
772						if (method !== 'POST') return;
773
774						var url = typeof input === 'string' ? input :
775							(input instanceof Request ? input.url : String(input));
776
777						if (/wc\/store\/v1\/cart\/add-item/.test(url) || /wc\/store\/v1\/cart\/update-item/.test(url)) {
778							self.fetchAndTrackAddToCart();
779						} else if (/wc\/store\/v1\/cart\/remove-item/.test(url)) {
780							self.fetchAndTrackRemoveFromCart();
781						}
782					} catch (e) {
783						// Silently ignore interceptor errors
784					}
785				}).catch(function () {
786					// Ignore - the original caller handles fetch errors
787				});
788
789				return promise;
790			};
791		},
792	};
793
794	// Initialize when DOM is ready
795	$(document).ready(function () {
796		MailchimpPixelTracking.init();
797	});
798
799})(jQuery);
800;

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.