Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
14 kB
1
Indexable
Never

((w) => {
	"use strict";

	let tag = "cv-pjs",
		debug = document.cookie.includes("cfQA"),
		window = typeof unsafeWindow !== "undefined" ? unsafeWindow : w,
		utils;

	if (window[tag]) return;

	window[tag] = {
		init: () => {
			try {
				const urlParams = new URLSearchParams(window.location.search),
					cfQaValue = urlParams.get("cfQA");

				if (cfQaValue) {
					utils.createCookie("cfQA", cfQaValue);
					utils.log(`QA mode: ${cfQaValue}`);

					debug = true;
				}

				if (debug && !document.title.includes("CONV QA")) {
					document.title = `[CONV QA] ${document.title}`;
				}

				utils.initMetrics();

				utils.log("ProjectJS initialised");
			} catch (err) {
				utils.log(err);
			}
		},

		initMetrics: () => {
			const isPageCheckout = window.location.href.includes("cart.wearewild.com") && !window.location.search.includes("previous_step="),
				isPageConfirmation = window.location.pathname.includes("thank_you");

			if (!!isPageConfirmation) {
				utils.waitForElement(".product-table tbody", (prodTable) => {
					const elsPrices = prodTable.querySelectorAll("tr[class='product'][data-product-type] .product__price span:last-of-type");

					elsPrices.forEach((priceElem) => {
						const elTableRow = priceElem.closest("tr"),
							isSubscpn = elTableRow.textContent.includes("initial_delivery"),
							isOneTime = elTableRow.textContent.includes("one_off_purchase"),
							isFullMty = elTableRow.textContent.includes("the_full_monty");

						if (isSubscpn) utils.sendEvt(`[CONV] Subscription purchases`, "Custom");
						else if (isOneTime) utils.sendEvt(`[CONV] OTP purchases`, "Custom");
						else if (isFullMty) utils.sendEvt(`[CONV] Full Monty purchases`, "Custom");
					});
				});
			} else if (!!isPageCheckout) {
				utils.sendEvt(`[CONV] Checkout 1 [Details]`, "Pageview");
			} else {
				let isTimedOut,
					timeout = setTimeout(() => {
						isTimedOut = true;
					}, 5000),
					checkForDataLayer = () => {
						try {
							const isLoaded = typeof window.dataLayer !== "undefined" && typeof window.dataLayer.push !== "undefined";

							if (isTimedOut) return;
							else if (!isLoaded) window.requestAnimationFrame(checkForDataLayer);
							else {
								clearTimeout(timeout);

								// Check existing items in datalayer
								dataLayer.forEach((dlItem) => {
									checkDlItem(dlItem);
								});

								// Check new items pushed to datalayer
								const getPushData = window.dataLayer.push;

								window.dataLayer.push = function (dlItem) {
									try {
										getPushData.apply(this, arguments);

										checkDlItem(dlItem);
									} catch (err) {
										utils.log(err);
									}
								};
							}
						} catch (err) {
							utils.log(err);
						}
					};

				window.requestAnimationFrame(checkForDataLayer);
			}

			const checkDlItem = (dlItem) => {
				try {
					// prettier-ignore
					const isPageView =
							dlItem.pageUrl &&
							dlItem.pageType &&
							dlItem.pageType === "PageView",
						isBuilderProdAdded =
							dlItem.event &&
							dlItem.event === "click_item" &&
							dlItem.ecommerce &&
							dlItem.ecommerce.click &&
							dlItem.ecommerce.click.products &&
							dlItem.ecommerce.click.products[0] &&
							"category" in dlItem.ecommerce.click.products[0] &&
							"variant" in dlItem.ecommerce.click.products[0],
						isCaseAdded =
							isBuilderProdAdded &&
							dlItem.ecommerce.click.products[0].category === "Case" && 
							dlItem.ecommerce.click.products[0].variant !== "W_ENGRAVED",
						isPlanAdded =
							isBuilderProdAdded &&
							dlItem.ecommerce.click.products[0].category === "payment_plan",
						isContCtaClick =
							dlItem.event &&
							dlItem.event === "wild.customEvent" &&
							dlItem["wild_label"] &&
							dlItem["wild_label"] === "Continue";

					// Regular pageview
					if (isPageView) {
						const metrics = [
								{
									name: "Step 1 [Case]",
									url: "/get-started",
									contains: true,
								},
								{
									name: "PLP Pageviews",
									url: "/shop/",
									contains: true,
								},
								{
									name: "PDP Pageviews",
									url: "/products/",
									contains: true,
								},
								{
									name: "Bundles PLP",
									url: "/shop/bundles",
								},
								{
									name: "Cases PLP",
									url: "/shop/deodorant-cases",
								},
								{
									name: "Refills PLP",
									url: "/shop/refills",
								},
								{
									name: "Cart pageview",
									url: "/cart",
								},
							],
							pageUrl = dlItem.pageUrl.split("?")[0].split("#")[0];

						metrics.forEach((metric) => {
							const isMatch = metric.url === pageUrl || (metric.contains && pageUrl.includes(metric.url));

							if (isMatch) {
								utils.sendEvt(`[CONV] ${metric.name}`, "Pageview");
							}
						});
					}

					// Builder flow - step 2 activated
					else if (isCaseAdded) {
						const caseName = dlItem.ecommerce.click.products[0].variant.replace("- Limited Edition", "").split("(+£")[0].trim();

						utils.sendEvt(`[CONV] Case selected - ${caseName}`, "Custom");
						utils.sendEvt("[CONV] Step 2 [Plan]", "Pageview");
					}

					// Builder flow - step 3 activated
					else if (isPlanAdded) {
						utils.sendEvt("[CONV] Step 3 [Scent]", "Pageview");
					}

					// Builder flow - step 4 activated
					else if (isContCtaClick) {
						utils.sendEvt("[CONV] Step 4 [Extras]", "Pageview");
					}
				} catch (err) {
					utils.log(err);
				}
			};

			["mousedown", "touchstart"].forEach((action) => {
				utils.on(action, ".configurator-layout__controls button", (e) => {
					const elBtn = e.target.nodeName === "BUTTON" ? e.target : e.target.closest("button"),
						isProgressingToStep2 = elBtn && elBtn.textContent.toLowerCase().includes("select plan"),
						selCase = document.querySelector(".step-optionList li.--is-selected h2");

					if (isProgressingToStep2 && selCase)
						utils.sendEvt(`[CONV] Case selected and progressed - ${selCase.textContent.trim()}`, "Custom");
				});

				utils.on(action, ".RefillsPage button[icon='plus']", () => {
					const isRefillsPlp = window.location.pathname === "/shop/refills";

					if (isRefillsPlp) utils.sendEvt(`[CONV] Refills ATB`, "Custom");
				});
			});
		},

		sendEvt: (label, action, expTag = tag, isOnlyGA4) => {
			try {
				let isTimedOut,
					timeout = setTimeout(() => {
						isTimedOut = true;
						utils.log("GA failed to load");
					}, 5000),
					checkForGa = () => {
						try {
							const isGaLoaded = !isOnlyGA4
								? typeof window.ga !== "undefined" && typeof window.ga.getAll !== "undefined"
								: typeof window.gtagDataLayer !== "undefined";

							if (isTimedOut) return;
							else if (!isGaLoaded) {
								window.requestAnimationFrame(checkForGa);
							} else {
								clearTimeout(timeout);

								if (!!isOnlyGA4 || expTag === tag) {
									// GA4 only, or any metric in PJS
									function ga4Tag() {
										gtagDataLayer.push(arguments);
									}

									ga4Tag("event", label, {});

									utils.log(`metric triggered (GA4): \n - label: ${label}`, expTag);
								}

								if (!!isOnlyGA4) return;

								// UA only
								const gaObj = {
										hitType: "event",
										eventCategory: "[CONV] Experiments",
										eventAction: action,
										eventLabel: label,
									},
									trackers = ga.getAll();

								trackers.some((tracker) => {
									const isMatch = tracker.get("trackingId") && tracker.get("trackingId") === "UA-141350614-1";

									if (isMatch) {
										window.ga(() => {
											tracker.send("event", gaObj.eventCategory, gaObj.eventAction, gaObj.eventLabel);

											utils.log(
												`metric triggered (UA): \n - category: ${gaObj.eventCategory} \n - action  : ${gaObj.eventAction} \n - label   : ${gaObj.eventLabel}`,
												expTag
											);
										});

										return true;
									}
								});
							}
						} catch (err) {
							utils.log(err, expTag);
						}
					};

				window.requestAnimationFrame(checkForGa);
			} catch (err) {
				utils.log(err, expTag);
			}
		},

		initHotjar: (expTag, label, isSurvey) => {
			if (!debug || isSurvey) {
				label = label || "CONV_" + expTag.replace("cv-", "") + "_" + window[expTag].variation;

				const maxCalls = 10,
					waitForHj = setInterval(() => {
						try {
							if (typeof window.hj === "function") {
								clearInterval(waitForHj);

								hj("event", label);

								utils.log("Hotjar initialised: " + label, expTag);
							}

							if (--maxCalls < 0) {
								clearInterval(waitForHj);

								utils.log("Hotjar failed to load", expTag);
							}
						} catch (err) {
							utils.log(err, expTag);
						}
					}, 500);
			}
		},

		log: (msg, expTag = tag) => {
			debug && console.log(`[CONV] ${expTag} --> ${msg}`);
		},

		waitForElement: (cssSelector, cb, timeout = 5000) => {
			let elementCached,
				stop,
				timer = setTimeout(() => {
					stop = true;
				}, timeout),
				checkForElem = () => {
					try {
						elementCached = document.querySelector(cssSelector);

						if (stop) return;

						if (elementCached) {
							cb(elementCached);
							clearTimeout(timer);
						} else {
							window.requestAnimationFrame(checkForElem);
						}
					} catch (err) {
						utils.log(err);
					}
				};

			window.requestAnimationFrame(checkForElem);
		},

		waitUntil: (cond, cb, expTag = tag, timeout = 5000) => {
			let stop;

			const timer = setTimeout(() => {
				stop = true;
			}, timeout);

			const end = () => {
				clearTimeout(timer);
				stop = true;
			};

			const check = () => {
				try {
					const elCached = typeof cond === "string" ? document.querySelector(cond) : undefined,
						condSuccess = typeof cond === "function" ? cond() : undefined;

					if (stop) {
						return;
					} else if (elCached) {
						cb(elCached);
						end();
					} else if (condSuccess) {
						cb();
						end();
					}

					window.requestAnimationFrame(check);
				} catch (err) {
					utils.log(err, expTag);
				}
			};

			window.requestAnimationFrame(check);
		},

		applyChanges: (sel, cb, timeout = 5000) => {
			let stop,
				timer = setTimeout(() => {
					stop = true;
				}, timeout),
				check = () => {
					try {
						const elem = document.querySelector(sel);

						if (stop) return;
						else if (!elem) cb();

						window.requestAnimationFrame(check);
					} catch (err) {
						utils.log(err);
					}
				};

			window.requestAnimationFrame(check);
		},

		apply: (cond, cb, timeout = 5000) => {
			let stop,
				timer = setTimeout(() => {
					stop = true;
				}, timeout),
				check = () => {
					try {
						const success = cond();

						if (stop) return;
						else if (!success) cb();

						window.requestAnimationFrame(check);
					} catch (err) {
						utils.log(err);
					}
				};

			window.requestAnimationFrame(check);
		},

		skipToStep(stepNo) {
			utils.waitUntil(".footer-no-margin #step0 .step-optionList li", (elFirstCase) => {
				elFirstCase.click();

				utils.waitUntil(".configurator-layout__controls button", (elContCta) => {
					elContCta.click();

					if (stepNo === 2) return;

					utils.waitUntil("#step1 .step-optionList li", (elFirstPlan) => {
						elFirstPlan.click();
						elContCta.click();

						utils.waitUntil(".cv-1-3_cta button", (elFauxCta) => {
							elFauxCta.click();
						});

						if (stepNo === 3) return;

						utils.waitUntil("#step2 .step-optionList li", (elFirstScent) => {
							elFirstScent.click();
							elContCta.click();
						});
					});
				});
			});
		},

		on: (n, e, o) => {
			document.addEventListener(n, (n) => {
				try {
					let r = n.target;
					if (r.matches(e)) o(n);
					else
						for (; r && r.parentNode !== document; ) {
							if (!(r = r.parentNode)) return;
							r.matches(e) && o(n);
						}
				} catch (err) {
					utils.log(err);
				}
			});
		},

		createCookie: (name, value) => {
			document.cookie = `${name}=${value};domain=${window.location.host.replace("www", "")};path=/;secure;`;
		},

		deleteCookie: (name) => {
			document.cookie = `${name}=;domain=${window.location.host.replace("www", "")};path=/;secure;expires=Thu, 01 Jan 1970 00:00:00 GMT`;
		},

		observeNavigation: (expObj, expTag) => {
			let isTimedOut,
				timeout = setTimeout(() => {
					isTimedOut = true;
				}, 5000),
				checkForDataLayer = () => {
					try {
						const isLoaded = typeof window.dataLayer !== "undefined" && typeof window.dataLayer.push !== "undefined";

						if (isTimedOut) return;
						else if (!isLoaded) window.requestAnimationFrame(checkForDataLayer);
						else {
							clearTimeout(timeout);

							const getPushData = window.dataLayer.push;

							window.dataLayer.push = function (dlItem) {
								getPushData.apply(this, arguments);

								const isPageView = dlItem.pageUrl && dlItem.pageType && dlItem.pageType === "PageView";

								if (!isPageView) return;

								const url = dlItem.pageUrl.split("?")[0].split("#")[0],
									isTargetPage = expObj.targetUrls && expObj.targetUrls.includes(url),
									isActivated = document.body.classList.contains(expTag);

								if (isTargetPage && !isActivated) {
									expObj.activate();
									utils.log("reactivated", expTag);
								} else if (!isTargetPage && isActivated && isPageView) {
									expObj.deactivate();
									utils.log("deactivated", expTag);
								}
							};
						}
					} catch (err) {
						utils.log(err, expTag);
					}
				};

			window.requestAnimationFrame(checkForDataLayer);
		},

		addStyles: (expTag, styles) => {
			const alreadyAdded = document.querySelector(`style#${expTag}`);

			if (!alreadyAdded) {
				const link = document.createElement("style");

				link.textContent = styles;
				link.id = expTag;

				document.querySelector("head").insertAdjacentElement("beforeend", link);
			}
		},
	};

	utils = window[tag];
	utils.init();
})(window);