@occluze/tether 0.6.6 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +3 -3
  2. package/dist/esm/astro.js +2 -2
  3. package/dist/esm/chunks/account-C3NP6HFB.js +1 -0
  4. package/dist/esm/chunks/account-pages-WLK374FO.js +1 -0
  5. package/dist/esm/chunks/analytics-G3SAWMNL.js +1 -0
  6. package/dist/esm/chunks/chunk-27PJJZGC.js +1 -0
  7. package/dist/esm/chunks/chunk-2FSXS55U.js +1 -0
  8. package/dist/esm/chunks/chunk-FCSWQDVV.js +1 -0
  9. package/dist/esm/chunks/chunk-FOMSMIAM.js +1 -0
  10. package/dist/esm/chunks/chunk-GZJTCLLW.js +1 -0
  11. package/dist/esm/chunks/chunk-HEQILZVR.js +1 -0
  12. package/dist/esm/chunks/chunk-JOOEXYF3.js +4 -0
  13. package/dist/esm/chunks/chunk-MPTQYLSM.js +1 -0
  14. package/dist/esm/chunks/chunk-NEHZ37TU.js +3 -0
  15. package/dist/esm/chunks/chunk-V74KGEJF.js +1 -0
  16. package/dist/esm/chunks/chunk-VT4CDUJJ.js +1 -0
  17. package/dist/esm/chunks/chunk-YJLWX2HK.js +1 -0
  18. package/dist/esm/chunks/diagnose-6VVGZNTY.js +14 -0
  19. package/dist/esm/chunks/extras-GJVRDFM3.js +1 -0
  20. package/dist/esm/chunks/markets-ui-UNB4PTA7.js +1 -0
  21. package/dist/esm/chunks/search-OU75RNQL.js +1 -0
  22. package/dist/esm/index.js +1 -1
  23. package/dist/esm/server.js +2 -1
  24. package/dist/presets/barba.esm.js +56 -36
  25. package/dist/tether.account-pages.js +776 -432
  26. package/dist/tether.account-pages.min.js +1 -1
  27. package/dist/tether.account.js +655 -321
  28. package/dist/tether.account.min.js +1 -1
  29. package/dist/tether.analytics.js +318 -191
  30. package/dist/tether.analytics.min.js +1 -1
  31. package/dist/tether.diagnose.js +334 -243
  32. package/dist/tether.diagnose.min.js +2 -2
  33. package/dist/tether.extras.js +78 -33
  34. package/dist/tether.extras.min.js +1 -1
  35. package/dist/tether.js +3171 -1998
  36. package/dist/tether.markets.js +231 -126
  37. package/dist/tether.markets.min.js +1 -1
  38. package/dist/tether.min.js +0 -0
  39. package/dist/tether.min.js.map +4 -4
  40. package/dist/tether.search.js +221 -126
  41. package/dist/tether.search.min.js +1 -1
  42. package/package.json +23 -4
  43. package/src/account/addresses-view.js +92 -0
  44. package/src/account/browser-session.js +359 -0
  45. package/src/account/markup.js +48 -0
  46. package/src/account/operations.js +180 -0
  47. package/src/account/order-lines.js +96 -0
  48. package/src/account/order-list.js +91 -0
  49. package/src/account/order-page.js +192 -0
  50. package/src/account/page-events.js +288 -0
  51. package/src/account/server-client.js +172 -0
  52. package/src/account/shared.js +111 -0
  53. package/src/account/shopify-auth.js +150 -0
  54. package/src/account/subscriptions-view.js +158 -0
  55. package/src/account-api.js +118 -0
  56. package/src/account-links.js +35 -0
  57. package/src/account-pages.js +68 -284
  58. package/src/account.js +281 -317
  59. package/src/amounts.js +36 -0
  60. package/src/analytics-shopify.js +241 -0
  61. package/src/analytics.js +304 -184
  62. package/src/api-version.js +75 -25
  63. package/src/astro.js +233 -68
  64. package/src/cart/checkout.js +97 -0
  65. package/src/cart/drawer.js +138 -0
  66. package/src/cart/mutations.js +234 -0
  67. package/src/cart/render.js +294 -0
  68. package/src/cart/store.js +326 -0
  69. package/src/cart.js +322 -481
  70. package/src/client.js +214 -78
  71. package/src/collection.js +126 -57
  72. package/src/config.js +323 -59
  73. package/src/controls/cart-controls.js +155 -0
  74. package/src/controls/discount.js +96 -0
  75. package/src/controls/extras.js +63 -0
  76. package/src/controls/navigation.js +83 -0
  77. package/src/diagnose-markup.js +333 -0
  78. package/src/diagnose.js +251 -202
  79. package/src/dom.js +219 -220
  80. package/src/extras.js +119 -37
  81. package/src/fragments.js +115 -22
  82. package/src/images.js +104 -0
  83. package/src/index.js +245 -285
  84. package/src/initial.js +78 -66
  85. package/src/lazy-iife.js +92 -22
  86. package/src/lazy.js +25 -4
  87. package/src/loader.js +263 -124
  88. package/src/market-codes.js +35 -0
  89. package/src/markets-ui.js +169 -55
  90. package/src/markets.js +286 -85
  91. package/src/markup.js +64 -0
  92. package/src/mounting.js +214 -91
  93. package/src/page-market.js +116 -0
  94. package/src/parts/account-pages.js +4 -1
  95. package/src/parts/account.js +4 -1
  96. package/src/parts/analytics.js +4 -1
  97. package/src/parts/diagnose.js +4 -1
  98. package/src/parts/extras.js +4 -1
  99. package/src/parts/markets.js +4 -1
  100. package/src/parts/search.js +4 -1
  101. package/src/presets/barba.js +120 -30
  102. package/src/product-events.js +130 -0
  103. package/src/product-render.js +257 -0
  104. package/src/product-state.js +318 -0
  105. package/src/product.js +233 -459
  106. package/src/repeaters.js +173 -0
  107. package/src/search.js +277 -107
  108. package/src/selling-plans.js +107 -47
  109. package/src/server.js +168 -205
  110. package/src/shopify-values.js +89 -0
  111. package/src/status.js +99 -39
  112. package/src/store-html.js +0 -0
  113. package/src/storefront/catalog.js +199 -0
  114. package/src/storefront/json-ld.js +167 -0
  115. package/src/variant-render.js +165 -0
  116. package/types/astro.d.ts +13 -2
  117. package/types/index.d.ts +178 -27
  118. package/types/server.d.ts +89 -17
  119. package/dist/esm/chunks/account-QUBHQG3C.js +0 -1
  120. package/dist/esm/chunks/account-pages-VWDHF6WA.js +0 -1
  121. package/dist/esm/chunks/analytics-SLATWPLC.js +0 -1
  122. package/dist/esm/chunks/chunk-64TUD3QR.js +0 -1
  123. package/dist/esm/chunks/chunk-AJLPU2JC.js +0 -4
  124. package/dist/esm/chunks/chunk-FIOZPPC5.js +0 -1
  125. package/dist/esm/chunks/chunk-K2YEQAIH.js +0 -1
  126. package/dist/esm/chunks/chunk-L2TRRKYV.js +0 -1
  127. package/dist/esm/chunks/chunk-RREHGH4A.js +0 -1
  128. package/dist/esm/chunks/chunk-VC46IEJQ.js +0 -1
  129. package/dist/esm/chunks/chunk-VCPI7MJ2.js +0 -1
  130. package/dist/esm/chunks/chunk-VPVJPNB4.js +0 -1
  131. package/dist/esm/chunks/chunk-ZIUIWEON.js +0 -1
  132. package/dist/esm/chunks/diagnose-VXQBSD3A.js +0 -14
  133. package/dist/esm/chunks/extras-R4X34VQL.js +0 -1
  134. package/dist/esm/chunks/markets-ui-F3R5T5EL.js +0 -1
  135. package/dist/esm/chunks/search-NILMGYXG.js +0 -1
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  (() => {
2
3
  var __defProp = Object.defineProperty;
3
4
  var __export = (target, all) => {
@@ -13,247 +14,373 @@
13
14
 
14
15
  // tether-core:config.js
15
16
  var m = window.Tether.__core["config.js"];
16
- var { DEFAULTS, cfg, configure, isConfigured, log, reportError, warn } = m;
17
-
18
- // tether-core:client.js
19
- var m2 = window.Tether.__core["client.js"];
20
- var { TetherError, endpoint, gql, request, unwrap, withContext } = m2;
17
+ var { DEFAULTS, cfg, configure, isConfigured, log, reportError, resolveConfig, toMetafieldIdentifier, warn } = m;
21
18
 
22
19
  // tether-core:dom.js
23
- var m3 = window.Tether.__core["dom.js"];
24
- var { attr, busy, cdnUrl, dataKey, emit, formatDate, formatMoney, formatPercent, get, has, hasTemplate, imageUrl, notifyLayoutChanged, parseJSONAttr, productUrl, qsa, readText, repeat, repeatKeyed, sameImage, scoped, sel, setImage, setSelected, setText, setTextTracked, show, toAttributes, watchImages } = m3;
20
+ var m2 = window.Tether.__core["dom.js"];
21
+ var { attr, busy, cdnUrl, emit, formatDate, formatMoney, formatPercent, get, has, hasTemplate, imageUrl, notifyLayoutChanged, parseJSONAttr, productUrl, qsa, repeat, repeatKeyed, sameImage, scoped, sel, setImage, setSelected, setStoreHtml, setText, setTextTracked, show, toAttributes, watchImages } = m2;
25
22
 
26
23
  // tether-core:cart.js
27
- var m4 = window.Tether.__core["cart.js"];
28
- var { Cart, focusables, toLineInput } = m4;
24
+ var m3 = window.Tether.__core["cart.js"];
25
+ var { Cart, trapFocus } = m3;
29
26
 
30
- // src/analytics.js
31
- var num = (m5) => m5 ? parseFloat(m5.amount) : 0;
32
- var id = (gid) => String(gid || "").split("/").pop();
33
- function itemFromVariant(p, v, quantity = 1, index) {
34
- return {
35
- item_id: v?.sku || id(v?.id) || id(p.id),
36
- item_name: p.title,
37
- item_variant: v?.title === "Default Title" ? void 0 : v?.title,
38
- item_brand: p.vendor || void 0,
39
- item_category: p.productType || void 0,
40
- price: num(v?.price || p.priceRange?.minVariantPrice),
41
- quantity,
42
- ...index != null ? { index } : {}
43
- };
27
+ // tether-core:client.js
28
+ var m4 = window.Tether.__core["client.js"];
29
+ var { TetherError, endpoint, gql, request, unwrap, withContext } = m4;
30
+
31
+ // src/analytics-shopify.js
32
+ var BUFFER_SIZE = 500;
33
+ var CONSENT_SCRIPTS = {
34
+ "default-banner": "https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js",
35
+ other: "https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js"
36
+ };
37
+ var ANALYTICS_SCRIPT = "https://cdn.shopify.com/storefront/analytics/shopify.js";
38
+ function shopifyGlobal() {
39
+ const shopify = Reflect.get(window, "Shopify");
40
+ return (
41
+ /** @type {ShopifyGlobal | undefined} */
42
+ shopify
43
+ );
44
44
  }
45
- function itemFromLine(l, quantity = l.quantity) {
46
- const m5 = l.merchandise || {};
47
- return itemFromVariant({ ...m5.product || {}, title: m5.product?.title }, m5, quantity);
45
+ function shopifyOptions() {
46
+ const shopify = cfg.analytics?.shopify;
47
+ return typeof shopify === "object" ? shopify : {};
48
48
  }
49
- var value = (items) => Math.round(items.reduce((s, i) => s + (i.price || 0) * (i.quantity || 1), 0) * 100) / 100;
50
- function ga(event, ecommerce) {
51
- const name = typeof cfg.analytics.dataLayer === "string" ? cfg.analytics.dataLayer : "dataLayer";
52
- const dl = window[name] = window[name] || [];
53
- if (ecommerce) dl.push({ ecommerce: null });
54
- dl.push(ecommerce ? { event, ecommerce } : { event });
49
+ function trackingAllowed() {
50
+ try {
51
+ const privacy = shopifyGlobal()?.customerPrivacy;
52
+ return privacy?.consentStatus === "loaded" && privacy.currentVisitorConsent?.()?.analytics !== "no" && Boolean(privacy.analyticsProcessingAllowed?.());
53
+ } catch {
54
+ return false;
55
+ }
55
56
  }
56
- var bus = null;
57
- function shopifyBus(shopId) {
57
+ function destinationContext(name) {
58
+ const tag = `hydrogen:${name}`;
59
+ return {
60
+ getTrackingValues: () => {
61
+ const internal = trackingAllowed() ? shopifyGlobal()?.customerPrivacy?.__internal : void 0;
62
+ return {
63
+ uniqueToken: internal?.uniqueToken?.({ generateFallback: true, tag }) ?? "",
64
+ visitToken: internal?.visitToken?.({ generateFallback: true, tag }) ?? ""
65
+ };
66
+ }
67
+ };
68
+ }
69
+ var subscriberOf = (destination) => (event, subscriber) => {
70
+ const subscribers = destination.subscribers.get(event) ?? /* @__PURE__ */ new Set();
71
+ destination.subscribers.set(event, subscribers);
72
+ subscribers.add(subscriber);
73
+ return () => destination.subscribers.get(event)?.delete(subscriber);
74
+ };
75
+ function createBus(shopId) {
58
76
  const destinations = [];
59
77
  const buffer = [];
60
- const privacy = () => window.Shopify?.customerPrivacy;
61
- const canTrack = () => {
62
- try {
63
- const p = privacy();
64
- return p?.consentStatus === "loaded" && p.currentVisitorConsent?.()?.analytics !== "no" && Boolean(p.analyticsProcessingAllowed?.());
65
- } catch {
66
- return false;
67
- }
78
+ const config = {
79
+ shop: { shopId, channel: "headless" },
80
+ consent: { mode: shopifyOptions().consent || "no-banner" }
68
81
  };
69
- const config = { shop: { shopId, channel: "headless" }, consent: { mode: cfg.analytics.shopify?.consent || "no-banner" } };
70
82
  const deliver = () => {
71
- if (!canTrack()) return;
72
- destinations.forEach((d) => {
73
- while (d.next < buffer.length) {
74
- const { event, payload } = buffer[d.next++];
75
- (d.subs.get(event) || []).forEach((cb) => {
76
- try {
77
- cb(payload, d.context);
78
- } catch (err) {
79
- reportError(err, `analytics ${d.name}`);
80
- }
81
- });
83
+ if (!trackingAllowed()) return;
84
+ for (const destination of destinations) {
85
+ for (; destination.next < buffer.length; destination.next += 1) {
86
+ const buffered = buffer[destination.next];
87
+ if (buffered) deliverTo(destination, buffered);
82
88
  }
83
- });
89
+ }
84
90
  };
85
- const api = {
91
+ const bus2 = {
86
92
  getConfig: () => config,
87
93
  publish(event, payload = {}) {
88
94
  buffer.push({ event, payload: { shop: config.shop, url: window.location.href, ...payload } });
89
- if (buffer.length > 500) {
95
+ if (buffer.length > BUFFER_SIZE) {
90
96
  buffer.shift();
91
- destinations.forEach((d) => {
92
- d.next = Math.max(0, d.next - 1);
93
- });
97
+ for (const destination of destinations) destination.next = Math.max(0, destination.next - 1);
94
98
  }
95
99
  deliver();
96
100
  },
97
- addDestination(dest) {
98
- const tag = `hydrogen:${dest.name}`;
99
- const d = {
100
- name: dest.name,
101
- next: 0,
102
- subs: /* @__PURE__ */ new Map(),
103
- context: {
104
- getTrackingValues: () => {
105
- const i = canTrack() ? privacy()?.__internal : null;
106
- return { uniqueToken: i?.uniqueToken?.({ generateFallback: true, tag }) ?? "", visitToken: i?.visitToken?.({ generateFallback: true, tag }) ?? "" };
107
- }
108
- }
109
- };
110
- const subscribe = (event, cb) => {
111
- if (!d.subs.has(event)) d.subs.set(event, /* @__PURE__ */ new Set());
112
- d.subs.get(event).add(cb);
113
- return () => d.subs.get(event)?.delete(cb);
114
- };
115
- Promise.resolve(dest.setup?.({ subscribe, getConfig: api.getConfig })).then(() => {
116
- destinations.push(d);
101
+ addDestination(spec) {
102
+ const destination = { name: spec.name, next: 0, subscribers: /* @__PURE__ */ new Map(), context: destinationContext(spec.name) };
103
+ Promise.resolve(spec.setup?.({ subscribe: subscriberOf(destination), getConfig: bus2.getConfig })).then(() => {
104
+ destinations.push(destination);
117
105
  deliver();
118
- }).catch((err) => reportError(err, `analytics ${dest.name}`));
106
+ }).catch((error) => {
107
+ reportError(error, `analytics ${spec.name}`);
108
+ });
119
109
  return () => {
120
- const i = destinations.indexOf(d);
121
- if (i >= 0) destinations.splice(i, 1);
110
+ const index = destinations.indexOf(destination);
111
+ if (index >= 0) destinations.splice(index, 1);
122
112
  };
123
113
  }
124
114
  };
125
115
  document.addEventListener("consentTrackingApiLoaded", deliver);
126
116
  document.addEventListener("visitorConsentCollected", deliver);
127
- return api;
117
+ return bus2;
118
+ }
119
+ function deliverTo(destination, { event, payload }) {
120
+ destination.subscribers.get(event)?.forEach((subscriber) => {
121
+ try {
122
+ subscriber(payload, destination.context);
123
+ } catch (error) {
124
+ reportError(error, `analytics ${destination.name}`);
125
+ }
126
+ });
128
127
  }
129
128
  function loadScript(src) {
130
- const s = document.createElement("script");
131
- s.src = src;
132
- s.async = true;
133
- document.head.appendChild(s);
129
+ const script = document.createElement("script");
130
+ script.src = src;
131
+ script.async = true;
132
+ document.head.appendChild(script);
134
133
  }
135
- async function startShopify() {
136
- if (window.Shopify?.analytics) {
137
- bus = window.Shopify.analytics;
138
- return;
139
- }
134
+ async function startShopifyAnalytics(isProductPage) {
135
+ const existing = shopifyGlobal();
136
+ if (existing?.analytics) return existing.analytics;
140
137
  const data = await gql("query TetherShopId { shop { id } }", {}, { context: false });
141
- const country = String(cfg.country || "US").toUpperCase();
142
- const S = window.Shopify = window.Shopify || {};
143
- Object.assign(S, {
144
- country,
145
- locale: (cfg.locale || "en").split("-")[0].toLowerCase(),
146
- // required by Shopify's script
147
- shop: cfg.domain,
148
- routes: { root: "/", match: () => ({ pageTemplateName: mainProduct() ? "product" : "page" }), ...S.routes || {} }
149
- });
150
- const proxy = cfg.analytics.shopify?.proxy;
151
- S.customerPrivacy = {
152
- ...S.customerPrivacy || {},
138
+ const shopify = shopifyGlobal() ?? {};
139
+ Reflect.set(window, "Shopify", shopify);
140
+ shopify.country = (cfg.country || "US").toUpperCase();
141
+ shopify.locale = (cfg.locale || "en").split("-")[0]?.toLowerCase() ?? "en";
142
+ shopify.shop = cfg.domain;
143
+ shopify.routes = {
144
+ root: "/",
145
+ match: () => ({ pageTemplateName: isProductPage() ? "product" : "page" }),
146
+ ...shopify.routes
147
+ };
148
+ const { proxy, consent } = shopifyOptions();
149
+ shopify.customerPrivacy = {
150
+ ...shopify.customerPrivacy,
153
151
  config: proxy ? { isHeadless: true, asyncConsent: true, asyncVisitorState: true, consentDomain: window.location.host } : { isHeadless: true, storefrontAccessToken: cfg.token, checkoutRootDomain: cfg.domain }
154
152
  };
155
- bus = shopifyBus(data.shop.id);
156
- S.analytics = bus;
157
- loadScript(cfg.analytics.shopify?.consent === "default-banner" ? "https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js" : "https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js");
158
- loadScript("https://cdn.shopify.com/storefront/analytics/shopify.js");
153
+ const bus2 = createBus(data.shop.id);
154
+ shopify.analytics = bus2;
155
+ loadScript(consent === "default-banner" ? CONSENT_SCRIPTS["default-banner"] : CONSENT_SCRIPTS.other);
156
+ loadScript(ANALYTICS_SCRIPT);
159
157
  log("shopify analytics ready", data.shop.id);
158
+ return bus2;
159
+ }
160
+
161
+ // src/analytics.js
162
+ var DEFAULT_VARIANT_TITLE = "Default Title";
163
+ var CENTS_PER_UNIT = 100;
164
+ var MAX_LIST_ITEMS = 200;
165
+ var amountOf = (money) => money ? parseFloat(money.amount) : 0;
166
+ var numericId = (gid) => (gid ?? "").split("/").pop() ?? "";
167
+ function itemFromVariant(product, variant, quantity = 1, index) {
168
+ const item = {
169
+ item_id: variant?.sku || numericId(variant?.id) || numericId(product.id),
170
+ item_name: product.title,
171
+ item_variant: variant?.title === DEFAULT_VARIANT_TITLE ? void 0 : variant?.title,
172
+ item_brand: product.vendor || void 0,
173
+ item_category: product.productType || void 0,
174
+ price: amountOf(variant?.price ?? product.priceRange?.minVariantPrice),
175
+ quantity
176
+ };
177
+ if (index != null) item.index = index;
178
+ return item;
179
+ }
180
+ var itemFromLine = (line, quantity = line.quantity) => itemFromVariant(line.merchandise.product, line.merchandise, quantity);
181
+ function totalValue(items) {
182
+ const total = items.reduce((sum, item) => sum + item.price * (item.quantity || 1), 0);
183
+ return Math.round(total * CENTS_PER_UNIT) / CENTS_PER_UNIT;
160
184
  }
161
- var productPayload = (p, v, quantity = 1) => ({
162
- id: p.id,
163
- title: p.title,
164
- price: v.price,
165
- vendor: p.vendor || "",
166
- variantId: v.id,
167
- variantTitle: v.title,
185
+ var productPayload = (product, variant, quantity = 1) => ({
186
+ id: product.id,
187
+ title: product.title,
188
+ price: variant.price,
189
+ vendor: product.vendor || "",
190
+ variantId: variant.id,
191
+ variantTitle: variant.title,
168
192
  quantity,
169
- sku: v.sku || void 0,
170
- productType: p.productType || void 0
193
+ sku: variant.sku || void 0,
194
+ productType: product.productType || void 0
171
195
  });
172
- function analyticsLine(l) {
173
- const m5 = l.merchandise || {};
174
- return { id: l.id, quantity: l.quantity, merchandise: { id: m5.id, title: m5.title, price: m5.price, sku: m5.sku, product: { id: m5.product?.id, title: m5.product?.title, vendor: m5.product?.vendor || "", handle: m5.product?.handle } } };
196
+ var linePayload = (line) => ({
197
+ id: line.id,
198
+ quantity: line.quantity,
199
+ merchandise: {
200
+ id: line.merchandise.id,
201
+ title: line.merchandise.title,
202
+ price: line.merchandise.price,
203
+ sku: line.merchandise.sku,
204
+ product: {
205
+ id: line.merchandise.product.id,
206
+ title: line.merchandise.product.title,
207
+ vendor: line.merchandise.product.vendor || "",
208
+ handle: line.merchandise.product.handle
209
+ }
210
+ }
211
+ });
212
+ function dataLayer() {
213
+ const { dataLayer: option } = cfg.analytics ?? {};
214
+ const name = typeof option === "string" ? option : "dataLayer";
215
+ const existing = Reflect.get(window, name);
216
+ if (Array.isArray(existing)) return existing;
217
+ const created = [];
218
+ Reflect.set(window, name, created);
219
+ return created;
175
220
  }
176
- function mainProduct() {
177
- const el = document.querySelector(sel("product"));
178
- return el && !el.closest(`${sel("collection")},${sel("products")},${sel("recommendations")}`) ? window.Tether?.getScope(el) : null;
221
+ function pushToDataLayer(event, ecommerce) {
222
+ const layer = dataLayer();
223
+ if (!ecommerce) {
224
+ layer.push({ event });
225
+ return;
226
+ }
227
+ layer.push({ ecommerce: null });
228
+ layer.push({ event, ecommerce });
179
229
  }
180
- var send = (name, data, shopifyEvent, shopifyPayload) => {
181
- if (cfg.analytics.dataLayer) ga(name, data);
230
+ var bus = null;
231
+ var lastLines = null;
232
+ var lastCart = null;
233
+ var viewed = /* @__PURE__ */ new WeakSet();
234
+ function send(name, data, shopifyEvent, shopifyPayload) {
235
+ if (cfg.analytics?.dataLayer) pushToDataLayer(name, data);
182
236
  if (bus && shopifyEvent) bus.publish(shopifyEvent, shopifyPayload);
183
237
  try {
184
- cfg.analytics.onEvent?.(name, data);
185
- } catch (err) {
186
- reportError(err, "analytics onEvent");
238
+ cfg.analytics?.onEvent?.(name, data);
239
+ } catch (error) {
240
+ reportError(error, "analytics onEvent");
187
241
  }
188
- };
189
- var lastLines = null;
190
- var viewed = /* @__PURE__ */ new WeakSet();
191
- function handle(e) {
192
- const name = e.type.slice(cfg.prefix.length + 1);
193
- const d = e.detail || {};
194
- if (name === "product-ready") {
195
- const scope = d.scope;
196
- if (!scope || viewed.has(scope.root) || mainProduct() !== scope) return;
197
- viewed.add(scope.root);
198
- const v = scope.variant || d.product.variants.nodes[0];
199
- const items = [itemFromVariant(d.product, v)];
200
- send("view_item", { currency: v?.price?.currencyCode, value: value(items), items }, "product_viewed", { products: [productPayload(d.product, v)] });
201
- } else if (name === "list-ready") {
202
- const items = (d.products || []).slice(0, 200).map((p, i) => itemFromVariant(p, p.variants?.nodes?.[0], 1, i));
203
- const list = { item_list_id: d.collection?.handle || "products", item_list_name: d.collection?.title || "Products" };
204
- send("view_item_list", { ...list, items }, d.collection ? "collection_viewed" : null, d.collection ? { collection: { id: d.collection.id, handle: d.collection.handle } } : null);
205
- } else if (name === "cart-updated") {
206
- const cart = d.cart;
207
- const now = new Map((cart?.lines?.nodes || []).map((l) => [l.merchandise?.id, l]));
208
- if (lastLines && d.reason !== "load" && d.reason !== "pending") {
209
- const currency = cart?.cost?.totalAmount?.currencyCode;
210
- now.forEach((l, k) => {
211
- const diff = l.quantity - (lastLines.get(k)?.quantity || 0);
212
- if (diff > 0) {
213
- const items = [itemFromLine(l, diff)];
214
- send("add_to_cart", { currency, value: value(items), items }, "product_added_to_cart", {
215
- cart,
216
- prevCart: lastCart,
217
- currentLine: analyticsLine(l),
218
- prevLine: lastLines.get(k) ? analyticsLine(lastLines.get(k)) : void 0
219
- });
220
- }
221
- });
222
- lastLines.forEach((l, k) => {
223
- const diff = l.quantity - (now.get(k)?.quantity || 0);
224
- if (diff > 0) {
225
- const items = [itemFromLine(l, diff)];
226
- send("remove_from_cart", { currency, value: value(items), items });
227
- }
228
- });
242
+ }
243
+ function mainProduct() {
244
+ const el = document.querySelector(sel("product"));
245
+ if (!el || el.closest(`${sel("collection")},${sel("products")},${sel("recommendations")}`)) return null;
246
+ const tether = Reflect.get(window, "Tether");
247
+ return (
248
+ /** @type {{ getScope: (el: Element) => unknown }} */
249
+ tether.getScope(el)
250
+ );
251
+ }
252
+ function onProductReady({ product, scope }) {
253
+ if (!scope || viewed.has(scope.root) || mainProduct() !== scope) return;
254
+ viewed.add(scope.root);
255
+ const variant = scope.variant ?? product.variants.nodes[0];
256
+ const items = [itemFromVariant(product, variant)];
257
+ const products = variant ? [productPayload(product, variant)] : [];
258
+ send("view_item", { currency: variant?.price.currencyCode, value: totalValue(items), items }, "product_viewed", {
259
+ products
260
+ });
261
+ }
262
+ function onListReady({ products = [], collection }) {
263
+ const items = products.slice(0, MAX_LIST_ITEMS).map((product, index) => itemFromVariant(product, product.variants.nodes[0], 1, index));
264
+ const list = { item_list_id: collection?.handle || "products", item_list_name: collection?.title || "Products" };
265
+ if (!collection) {
266
+ send("view_item_list", { ...list, items });
267
+ return;
268
+ }
269
+ send("view_item_list", { ...list, items }, "collection_viewed", {
270
+ collection: { id: collection.id, handle: collection.handle }
271
+ });
272
+ }
273
+ function sendCartChanges(cart, now, before) {
274
+ const currency = cart?.cost.totalAmount.currencyCode;
275
+ now.forEach((line, variantId) => {
276
+ const previous = before.get(variantId);
277
+ const added = line.quantity - (previous?.quantity ?? 0);
278
+ if (added <= 0) return;
279
+ const items = [itemFromLine(line, added)];
280
+ send("add_to_cart", { currency, value: totalValue(items), items }, "product_added_to_cart", {
281
+ cart,
282
+ prevCart: lastCart,
283
+ currentLine: linePayload(line),
284
+ prevLine: previous ? linePayload(previous) : void 0
285
+ });
286
+ });
287
+ before.forEach((line, variantId) => {
288
+ const removed = line.quantity - (now.get(variantId)?.quantity ?? 0);
289
+ if (removed <= 0) return;
290
+ const items = [itemFromLine(line, removed)];
291
+ send("remove_from_cart", { currency, value: totalValue(items), items });
292
+ });
293
+ }
294
+ function onCartUpdated({ cart, reason }) {
295
+ const now = new Map((cart?.lines.nodes ?? []).map((line) => [line.merchandise.id, line]));
296
+ if (lastLines && reason !== "load" && reason !== "pending") sendCartChanges(cart, now, lastLines);
297
+ lastLines = now;
298
+ lastCart = cart;
299
+ }
300
+ function onCartOpened() {
301
+ const items = Cart.lines.map((line) => itemFromLine(line));
302
+ send("view_cart", { currency: Cart.state?.cost.totalAmount.currencyCode, value: totalValue(items), items });
303
+ }
304
+ function onCheckout({ cart }) {
305
+ const items = cart.lines.nodes.map((line) => itemFromLine(line));
306
+ const coupons = cart.discountCodes.filter((discount) => discount.applicable).map((discount) => discount.code);
307
+ send("begin_checkout", {
308
+ currency: cart.cost.totalAmount.currencyCode,
309
+ value: totalValue(items),
310
+ coupon: coupons.join(",") || void 0,
311
+ items
312
+ });
313
+ }
314
+ function handle(event) {
315
+ const name = event.type.slice(cfg.prefix.length + 1);
316
+ const detail = Reflect.get(event, "detail");
317
+ switch (name) {
318
+ case "product-ready":
319
+ onProductReady(
320
+ /** @type {ProductReadyDetail} */
321
+ detail
322
+ );
323
+ break;
324
+ case "list-ready":
325
+ onListReady(
326
+ /** @type {ListReadyDetail} */
327
+ detail
328
+ );
329
+ break;
330
+ case "cart-updated":
331
+ onCartUpdated(
332
+ /** @type {CartUpdatedDetail} */
333
+ detail
334
+ );
335
+ break;
336
+ case "cart-toggled":
337
+ if (
338
+ /** @type {{ open: boolean }} */
339
+ detail.open
340
+ ) onCartOpened();
341
+ break;
342
+ case "checkout":
343
+ onCheckout(
344
+ /** @type {{ cart: AnalyticsCart }} */
345
+ detail
346
+ );
347
+ break;
348
+ case "search": {
349
+ const { term } = (
350
+ /** @type {{ term: string }} */
351
+ detail
352
+ );
353
+ if (term) send("search", { search_term: term }, "search_viewed", { searchTerm: term });
354
+ break;
229
355
  }
230
- lastLines = now;
231
- lastCart = cart;
232
- } else if (name === "cart-toggled" && d.open) {
233
- const items = Cart.lines.map((l) => itemFromLine(l));
234
- send("view_cart", { currency: Cart.state?.cost?.totalAmount?.currencyCode, value: value(items), items });
235
- } else if (name === "checkout") {
236
- const items = (d.cart?.lines?.nodes || []).map((l) => itemFromLine(l));
237
- const coupon = (d.cart?.discountCodes || []).filter((c) => c.applicable).map((c) => c.code).join(",") || void 0;
238
- send("begin_checkout", { currency: d.cart?.cost?.totalAmount?.currencyCode, value: value(items), coupon, items });
239
- } else if (name === "search" && d.term) {
240
- send("search", { search_term: d.term }, "search_viewed", { searchTerm: d.term });
241
- } else if (name === "page") {
242
- viewed = /* @__PURE__ */ new WeakSet();
243
- if (bus) bus.publish("page_viewed", {});
356
+ case "page":
357
+ viewed = /* @__PURE__ */ new WeakSet();
358
+ bus?.publish("page_viewed", {});
359
+ break;
360
+ default:
361
+ break;
244
362
  }
245
363
  }
246
- var lastCart = null;
247
364
  async function startAnalytics(queue) {
248
- if (cfg.analytics.shopify) await startShopify().catch((err) => reportError(err, "shopify analytics"));
249
- if (bus) bus.publish("page_viewed", {});
365
+ if (cfg.analytics?.shopify) {
366
+ bus = await startShopifyAnalytics(() => mainProduct() != null).catch((error) => {
367
+ reportError(error, "shopify analytics");
368
+ return null;
369
+ });
370
+ }
371
+ bus?.publish("page_viewed", {});
250
372
  queue.splice(0).forEach(handle);
251
- queue.push = (e) => {
252
- handle(e);
253
- return 0;
373
+ queue.push = (...events) => {
374
+ events.forEach(handle);
375
+ return queue.length;
254
376
  };
255
377
  }
256
378
 
257
379
  // src/parts/analytics.js
258
- window.Tether.__parts.analytics = analytics_exports;
380
+ var { Tether } = (
381
+ /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */
382
+ window
383
+ );
384
+ if (!Tether) throw new Error("tether.analytics.js runs after the Tether script, which loads it");
385
+ Tether.__parts["analytics"] = analytics_exports;
259
386
  })();
@@ -1 +1 @@
1
- (()=>{var F=Object.defineProperty;var I=(n,t)=>{for(var e in t)F(n,e,{get:t[e],enumerable:!0})};var C={};I(C,{startAnalytics:()=>R});var U=window.Tether.__core["config.js"],{DEFAULTS:J,cfg:a,configure:z,isConfigured:G,log:x,reportError:y,warn:Q}=U;var $=window.Tether.__core["client.js"],{TetherError:Y,endpoint:Z,gql:L,request:tt,unwrap:et,withContext:nt}=$;var M=window.Tether.__core["dom.js"],{attr:it,busy:ct,cdnUrl:rt,dataKey:at,emit:st,formatDate:dt,formatMoney:lt,formatPercent:ut,get:pt,has:ft,hasTemplate:ht,imageUrl:mt,notifyLayoutChanged:yt,parseJSONAttr:gt,productUrl:wt,qsa:_t,readText:vt,repeat:Tt,repeatKeyed:bt,sameImage:kt,scoped:Ct,sel:g,setImage:St,setSelected:xt,setText:Lt,setTextTracked:qt,show:Et,toAttributes:At,watchImages:Pt}=M;var V=window.Tether.__core["cart.js"],{Cart:T,focusables:Dt,toLineInput:Ft}=V;var O=n=>n?parseFloat(n.amount):0,q=n=>String(n||"").split("/").pop();function k(n,t,e=1,o){return{item_id:t?.sku||q(t?.id)||q(n.id),item_name:n.title,item_variant:t?.title==="Default Title"?void 0:t?.title,item_brand:n.vendor||void 0,item_category:n.productType||void 0,price:O(t?.price||n.priceRange?.minVariantPrice),quantity:e,...o!=null?{index:o}:{}}}function _(n,t=n.quantity){let e=n.merchandise||{};return k({...e.product||{},title:e.product?.title},e,t)}var w=n=>Math.round(n.reduce((t,e)=>t+(e.price||0)*(e.quantity||1),0)*100)/100;function B(n,t){let e=typeof a.analytics.dataLayer=="string"?a.analytics.dataLayer:"dataLayer",o=window[e]=window[e]||[];t&&o.push({ecommerce:null}),o.push(t?{event:n,ecommerce:t}:{event:n})}var p=null;function H(n){let t=[],e=[],o=()=>window.Shopify?.customerPrivacy,r=()=>{try{let i=o();return i?.consentStatus==="loaded"&&i.currentVisitorConsent?.()?.analytics!=="no"&&!!i.analyticsProcessingAllowed?.()}catch{return!1}},c={shop:{shopId:n,channel:"headless"},consent:{mode:a.analytics.shopify?.consent||"no-banner"}},d=()=>{r()&&t.forEach(i=>{for(;i.next<e.length;){let{event:l,payload:u}=e[i.next++];(i.subs.get(l)||[]).forEach(v=>{try{v(u,i.context)}catch(s){y(s,`analytics ${i.name}`)}})}})},f={getConfig:()=>c,publish(i,l={}){e.push({event:i,payload:{shop:c.shop,url:window.location.href,...l}}),e.length>500&&(e.shift(),t.forEach(u=>{u.next=Math.max(0,u.next-1)})),d()},addDestination(i){let l=`hydrogen:${i.name}`,u={name:i.name,next:0,subs:new Map,context:{getTrackingValues:()=>{let s=r()?o()?.__internal:null;return{uniqueToken:s?.uniqueToken?.({generateFallback:!0,tag:l})??"",visitToken:s?.visitToken?.({generateFallback:!0,tag:l})??""}}}},v=(s,S)=>(u.subs.has(s)||u.subs.set(s,new Set),u.subs.get(s).add(S),()=>u.subs.get(s)?.delete(S));return Promise.resolve(i.setup?.({subscribe:v,getConfig:f.getConfig})).then(()=>{t.push(u),d()}).catch(s=>y(s,`analytics ${i.name}`)),()=>{let s=t.indexOf(u);s>=0&&t.splice(s,1)}}};return document.addEventListener("consentTrackingApiLoaded",d),document.addEventListener("visitorConsentCollected",d),f}function E(n){let t=document.createElement("script");t.src=n,t.async=!0,document.head.appendChild(t)}async function K(){if(window.Shopify?.analytics){p=window.Shopify.analytics;return}let n=await L("query TetherShopId { shop { id } }",{},{context:!1}),t=String(a.country||"US").toUpperCase(),e=window.Shopify=window.Shopify||{};Object.assign(e,{country:t,locale:(a.locale||"en").split("-")[0].toLowerCase(),shop:a.domain,routes:{root:"/",match:()=>({pageTemplateName:D()?"product":"page"}),...e.routes||{}}});let o=a.analytics.shopify?.proxy;e.customerPrivacy={...e.customerPrivacy||{},config:o?{isHeadless:!0,asyncConsent:!0,asyncVisitorState:!0,consentDomain:window.location.host}:{isHeadless:!0,storefrontAccessToken:a.token,checkoutRootDomain:a.domain}},p=H(n.shop.id),e.analytics=p,E(a.analytics.shopify?.consent==="default-banner"?"https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js":"https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js"),E("https://cdn.shopify.com/storefront/analytics/shopify.js"),x("shopify analytics ready",n.shop.id)}var N=(n,t,e=1)=>({id:n.id,title:n.title,price:t.price,vendor:n.vendor||"",variantId:t.id,variantTitle:t.title,quantity:e,sku:t.sku||void 0,productType:n.productType||void 0});function A(n){let t=n.merchandise||{};return{id:n.id,quantity:n.quantity,merchandise:{id:t.id,title:t.title,price:t.price,sku:t.sku,product:{id:t.product?.id,title:t.product?.title,vendor:t.product?.vendor||"",handle:t.product?.handle}}}}function D(){let n=document.querySelector(g("product"));return n&&!n.closest(`${g("collection")},${g("products")},${g("recommendations")}`)?window.Tether?.getScope(n):null}var h=(n,t,e,o)=>{a.analytics.dataLayer&&B(n,t),p&&e&&p.publish(e,o);try{a.analytics.onEvent?.(n,t)}catch(r){y(r,"analytics onEvent")}},m=null,b=new WeakSet;function P(n){let t=n.type.slice(a.prefix.length+1),e=n.detail||{};if(t==="product-ready"){let o=e.scope;if(!o||b.has(o.root)||D()!==o)return;b.add(o.root);let r=o.variant||e.product.variants.nodes[0],c=[k(e.product,r)];h("view_item",{currency:r?.price?.currencyCode,value:w(c),items:c},"product_viewed",{products:[N(e.product,r)]})}else if(t==="list-ready"){let o=(e.products||[]).slice(0,200).map((c,d)=>k(c,c.variants?.nodes?.[0],1,d)),r={item_list_id:e.collection?.handle||"products",item_list_name:e.collection?.title||"Products"};h("view_item_list",{...r,items:o},e.collection?"collection_viewed":null,e.collection?{collection:{id:e.collection.id,handle:e.collection.handle}}:null)}else if(t==="cart-updated"){let o=e.cart,r=new Map((o?.lines?.nodes||[]).map(c=>[c.merchandise?.id,c]));if(m&&e.reason!=="load"&&e.reason!=="pending"){let c=o?.cost?.totalAmount?.currencyCode;r.forEach((d,f)=>{let i=d.quantity-(m.get(f)?.quantity||0);if(i>0){let l=[_(d,i)];h("add_to_cart",{currency:c,value:w(l),items:l},"product_added_to_cart",{cart:o,prevCart:j,currentLine:A(d),prevLine:m.get(f)?A(m.get(f)):void 0})}}),m.forEach((d,f)=>{let i=d.quantity-(r.get(f)?.quantity||0);if(i>0){let l=[_(d,i)];h("remove_from_cart",{currency:c,value:w(l),items:l})}})}m=r,j=o}else if(t==="cart-toggled"&&e.open){let o=T.lines.map(r=>_(r));h("view_cart",{currency:T.state?.cost?.totalAmount?.currencyCode,value:w(o),items:o})}else if(t==="checkout"){let o=(e.cart?.lines?.nodes||[]).map(c=>_(c)),r=(e.cart?.discountCodes||[]).filter(c=>c.applicable).map(c=>c.code).join(",")||void 0;h("begin_checkout",{currency:e.cart?.cost?.totalAmount?.currencyCode,value:w(o),coupon:r,items:o})}else t==="search"&&e.term?h("search",{search_term:e.term},"search_viewed",{searchTerm:e.term}):t==="page"&&(b=new WeakSet,p&&p.publish("page_viewed",{}))}var j=null;async function R(n){a.analytics.shopify&&await K().catch(t=>y(t,"shopify analytics")),p&&p.publish("page_viewed",{}),n.splice(0).forEach(P),n.push=t=>(P(t),0)}window.Tether.__parts.analytics=C;})();
1
+ "use strict";(()=>{var U=Object.defineProperty;var D=(t,e)=>{for(var n in e)U(t,n,{get:e[n],enumerable:!0})};var T={};D(T,{startAnalytics:()=>it});var M=window.Tether.__core["config.js"],{DEFAULTS:at,cfg:c,configure:dt,isConfigured:ut,log:v,reportError:l,resolveConfig:lt,toMetafieldIdentifier:pt,warn:ft}=M;var N=window.Tether.__core["dom.js"],{attr:mt,busy:yt,cdnUrl:gt,emit:wt,formatDate:_t,formatMoney:bt,formatPercent:Tt,get:vt,has:Ct,hasTemplate:kt,imageUrl:St,notifyLayoutChanged:xt,parseJSONAttr:Et,productUrl:At,qsa:Lt,repeat:It,repeatKeyed:Pt,sameImage:Rt,scoped:qt,sel:p,setImage:jt,setSelected:Ft,setStoreHtml:Ut,setText:Dt,setTextTracked:Mt,show:Nt,toAttributes:Ot,watchImages:Vt}=N;var O=window.Tether.__core["cart.js"],{Cart:g,trapFocus:Bt}=O;var V=window.Tether.__core["client.js"],{TetherError:Wt,endpoint:Gt,gql:C,request:Jt,unwrap:Kt,withContext:Xt}=V;var $=500,k={"default-banner":"https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js",other:"https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js"},B="https://cdn.shopify.com/storefront/analytics/shopify.js";function m(){return Reflect.get(window,"Shopify")}function x(){let t=c.analytics?.shopify;return typeof t=="object"?t:{}}function E(){try{let t=m()?.customerPrivacy;return t?.consentStatus==="loaded"&&t.currentVisitorConsent?.()?.analytics!=="no"&&!!t.analyticsProcessingAllowed?.()}catch{return!1}}function H(t){let e=`hydrogen:${t}`;return{getTrackingValues:()=>{let n=E()?m()?.customerPrivacy?.__internal:void 0;return{uniqueToken:n?.uniqueToken?.({generateFallback:!0,tag:e})??"",visitToken:n?.visitToken?.({generateFallback:!0,tag:e})??""}}}}var W=t=>(e,n)=>{let o=t.subscribers.get(e)??new Set;return t.subscribers.set(e,o),o.add(n),()=>t.subscribers.get(e)?.delete(n)};function G(t){let e=[],n=[],o={shop:{shopId:t,channel:"headless"},consent:{mode:x().consent||"no-banner"}},r=()=>{if(E())for(let s of e)for(;s.next<n.length;s.next+=1){let i=n[s.next];i&&J(s,i)}},a={getConfig:()=>o,publish(s,i={}){if(n.push({event:s,payload:{shop:o.shop,url:window.location.href,...i}}),n.length>$){n.shift();for(let d of e)d.next=Math.max(0,d.next-1)}r()},addDestination(s){let i={name:s.name,next:0,subscribers:new Map,context:H(s.name)};return Promise.resolve(s.setup?.({subscribe:W(i),getConfig:a.getConfig})).then(()=>{e.push(i),r()}).catch(d=>{l(d,`analytics ${s.name}`)}),()=>{let d=e.indexOf(i);d>=0&&e.splice(d,1)}}};return document.addEventListener("consentTrackingApiLoaded",r),document.addEventListener("visitorConsentCollected",r),a}function J(t,{event:e,payload:n}){t.subscribers.get(e)?.forEach(o=>{try{o(n,t.context)}catch(r){l(r,`analytics ${t.name}`)}})}function S(t){let e=document.createElement("script");e.src=t,e.async=!0,document.head.appendChild(e)}async function A(t){let e=m();if(e?.analytics)return e.analytics;let n=await C("query TetherShopId { shop { id } }",{},{context:!1}),o=m()??{};Reflect.set(window,"Shopify",o),o.country=(c.country||"US").toUpperCase(),o.locale=(c.locale||"en").split("-")[0]?.toLowerCase()??"en",o.shop=c.domain,o.routes={root:"/",match:()=>({pageTemplateName:t()?"product":"page"}),...o.routes};let{proxy:r,consent:a}=x();o.customerPrivacy={...o.customerPrivacy,config:r?{isHeadless:!0,asyncConsent:!0,asyncVisitorState:!0,consentDomain:window.location.host}:{isHeadless:!0,storefrontAccessToken:c.token,checkoutRootDomain:c.domain}};let s=G(n.shop.id);return o.analytics=s,S(a==="default-banner"?k["default-banner"]:k.other),S(B),v("shopify analytics ready",n.shop.id),s}var K="Default Title",L=100,X=200,Y=t=>t?parseFloat(t.amount):0,I=t=>(t??"").split("/").pop()??"";function b(t,e,n=1,o){let r={item_id:e?.sku||I(e?.id)||I(t.id),item_name:t.title,item_variant:e?.title===K?void 0:e?.title,item_brand:t.vendor||void 0,item_category:t.productType||void 0,price:Y(e?.price??t.priceRange?.minVariantPrice),quantity:n};return o!=null&&(r.index=o),r}var y=(t,e=t.quantity)=>b(t.merchandise.product,t.merchandise,e);function f(t){let e=t.reduce((n,o)=>n+o.price*(o.quantity||1),0);return Math.round(e*L)/L}var Z=(t,e,n=1)=>({id:t.id,title:t.title,price:e.price,vendor:t.vendor||"",variantId:e.id,variantTitle:e.title,quantity:n,sku:e.sku||void 0,productType:t.productType||void 0}),P=t=>({id:t.id,quantity:t.quantity,merchandise:{id:t.merchandise.id,title:t.merchandise.title,price:t.merchandise.price,sku:t.merchandise.sku,product:{id:t.merchandise.product.id,title:t.merchandise.product.title,vendor:t.merchandise.product.vendor||"",handle:t.merchandise.product.handle}}});function z(){let{dataLayer:t}=c.analytics??{},e=typeof t=="string"?t:"dataLayer",n=Reflect.get(window,e);if(Array.isArray(n))return n;let o=[];return Reflect.set(window,e,o),o}function Q(t,e){let n=z();if(!e){n.push({event:t});return}n.push({ecommerce:null}),n.push({event:t,ecommerce:e})}var h=null,w=null,q=null,_=new WeakSet;function u(t,e,n,o){c.analytics?.dataLayer&&Q(t,e),h&&n&&h.publish(n,o);try{c.analytics?.onEvent?.(t,e)}catch(r){l(r,"analytics onEvent")}}function j(){let t=document.querySelector(p("product"));return!t||t.closest(`${p("collection")},${p("products")},${p("recommendations")}`)?null:Reflect.get(window,"Tether").getScope(t)}function tt({product:t,scope:e}){if(!e||_.has(e.root)||j()!==e)return;_.add(e.root);let n=e.variant??t.variants.nodes[0],o=[b(t,n)],r=n?[Z(t,n)]:[];u("view_item",{currency:n?.price.currencyCode,value:f(o),items:o},"product_viewed",{products:r})}function et({products:t=[],collection:e}){let n=t.slice(0,X).map((r,a)=>b(r,r.variants.nodes[0],1,a)),o={item_list_id:e?.handle||"products",item_list_name:e?.title||"Products"};if(!e){u("view_item_list",{...o,items:n});return}u("view_item_list",{...o,items:n},"collection_viewed",{collection:{id:e.id,handle:e.handle}})}function nt(t,e,n){let o=t?.cost.totalAmount.currencyCode;e.forEach((r,a)=>{let s=n.get(a),i=r.quantity-(s?.quantity??0);if(i<=0)return;let d=[y(r,i)];u("add_to_cart",{currency:o,value:f(d),items:d},"product_added_to_cart",{cart:t,prevCart:q,currentLine:P(r),prevLine:s?P(s):void 0})}),n.forEach((r,a)=>{let s=r.quantity-(e.get(a)?.quantity??0);if(s<=0)return;let i=[y(r,s)];u("remove_from_cart",{currency:o,value:f(i),items:i})})}function ot({cart:t,reason:e}){let n=new Map((t?.lines.nodes??[]).map(o=>[o.merchandise.id,o]));w&&e!=="load"&&e!=="pending"&&nt(t,n,w),w=n,q=t}function rt(){let t=g.lines.map(e=>y(e));u("view_cart",{currency:g.state?.cost.totalAmount.currencyCode,value:f(t),items:t})}function st({cart:t}){let e=t.lines.nodes.map(o=>y(o)),n=t.discountCodes.filter(o=>o.applicable).map(o=>o.code);u("begin_checkout",{currency:t.cost.totalAmount.currencyCode,value:f(e),coupon:n.join(",")||void 0,items:e})}function R(t){let e=t.type.slice(c.prefix.length+1),n=Reflect.get(t,"detail");switch(e){case"product-ready":tt(n);break;case"list-ready":et(n);break;case"cart-updated":ot(n);break;case"cart-toggled":n.open&&rt();break;case"checkout":st(n);break;case"search":{let{term:o}=n;o&&u("search",{search_term:o},"search_viewed",{searchTerm:o});break}case"page":_=new WeakSet,h?.publish("page_viewed",{});break;default:break}}async function it(t){c.analytics?.shopify&&(h=await A(()=>j()!=null).catch(e=>(l(e,"shopify analytics"),null))),h?.publish("page_viewed",{}),t.splice(0).forEach(R),t.push=(...e)=>(e.forEach(R),t.length)}var{Tether:F}=window;if(!F)throw new Error("tether.analytics.js runs after the Tether script, which loads it");F.__parts.analytics=T;})();