@occluze/tether 0.3.6 → 0.4.0
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.
- package/README.md +9 -6
- package/dist/esm/chunks/chunk-GHIUMD5H.js +1 -0
- package/dist/esm/chunks/chunk-KYQ5OGBX.js +1 -0
- package/dist/esm/chunks/chunk-LI3YDMGF.js +4 -0
- package/dist/esm/chunks/diagnose-ZXXXJIQE.js +1 -0
- package/dist/esm/chunks/markets-ui-AG6Y5VUE.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/presets/barba.esm.js +4 -5
- package/dist/tether.diagnose.js +319 -0
- package/dist/tether.diagnose.min.js +1 -0
- package/dist/tether.esm.js +2 -2608
- package/dist/tether.js +416 -780
- package/dist/tether.markets.js +207 -0
- package/dist/tether.markets.min.js +1 -0
- package/dist/tether.min.js +3 -132
- package/dist/tether.min.js.map +4 -4
- package/package.json +29 -11
- package/src/cart.js +32 -0
- package/src/config.js +2 -0
- package/src/index.js +64 -156
- package/src/lazy-iife.js +43 -0
- package/src/lazy.js +9 -0
- package/src/loader.js +1 -1
- package/src/markets-ui.js +81 -0
- package/src/mounting.js +89 -0
- package/src/parts/diagnose.js +3 -0
- package/src/parts/markets.js +3 -0
- package/src/product.js +53 -52
- package/src/status.js +1 -0
- package/types/barba.d.ts +16 -0
- package/types/index.d.ts +332 -0
package/dist/tether.js
CHANGED
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/config.js
|
|
9
|
+
var config_exports = {};
|
|
10
|
+
__export(config_exports, {
|
|
11
|
+
DEFAULTS: () => DEFAULTS,
|
|
12
|
+
cfg: () => cfg,
|
|
13
|
+
configure: () => configure,
|
|
14
|
+
isConfigured: () => isConfigured,
|
|
15
|
+
log: () => log,
|
|
16
|
+
reportError: () => reportError,
|
|
17
|
+
warn: () => warn
|
|
18
|
+
});
|
|
9
19
|
var DEFAULTS = {
|
|
10
20
|
// Shopify Storefront API
|
|
11
21
|
domain: "",
|
|
@@ -41,6 +51,8 @@
|
|
|
41
51
|
// defaults to `tether:cart:<domain>`
|
|
42
52
|
cartDrawerOpenClass: "is-open",
|
|
43
53
|
openCartOnAdd: true,
|
|
54
|
+
cartFocus: true,
|
|
55
|
+
// move focus into the open cart, keep Tab inside it, restore it on close
|
|
44
56
|
checkoutTarget: "_self",
|
|
45
57
|
// '_self' or '_blank'
|
|
46
58
|
// Subscriptions
|
|
@@ -51,6 +63,8 @@
|
|
|
51
63
|
// mount document on DOMContentLoaded
|
|
52
64
|
observe: false,
|
|
53
65
|
// auto-mount nodes added later (CMS load-more, filters)
|
|
66
|
+
partsUrl: null,
|
|
67
|
+
// folder of tether.markets/diagnose(.min).js for the <script> build (default: next to it)
|
|
54
68
|
// Formatting
|
|
55
69
|
formatMoney: null,
|
|
56
70
|
// (money, locale) => string
|
|
@@ -98,6 +112,14 @@
|
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
// src/client.js
|
|
115
|
+
var client_exports = {};
|
|
116
|
+
__export(client_exports, {
|
|
117
|
+
TetherError: () => TetherError,
|
|
118
|
+
endpoint: () => endpoint,
|
|
119
|
+
gql: () => gql,
|
|
120
|
+
unwrap: () => unwrap,
|
|
121
|
+
withContext: () => withContext
|
|
122
|
+
});
|
|
101
123
|
var TetherError = class extends Error {
|
|
102
124
|
constructor(message, { status, errors, userErrors, code } = {}) {
|
|
103
125
|
super(message);
|
|
@@ -123,7 +145,6 @@
|
|
|
123
145
|
}
|
|
124
146
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
125
147
|
async function gql(query, variables = {}, { context = true } = {}) {
|
|
126
|
-
var _a;
|
|
127
148
|
if (!cfg.domain || !cfg.token) {
|
|
128
149
|
throw new TetherError("Tether is not configured: domain and token are required", { code: "NOT_CONFIGURED" });
|
|
129
150
|
}
|
|
@@ -160,16 +181,13 @@
|
|
|
160
181
|
} catch {
|
|
161
182
|
throw new TetherError(`Invalid response (${res.status})`, { status: res.status });
|
|
162
183
|
}
|
|
163
|
-
if (
|
|
184
|
+
if (json.errors?.length) {
|
|
164
185
|
const inventoryDenied = json.errors.some((e) => /quantityAvailable/.test(e.message || ""));
|
|
165
186
|
if (inventoryDenied && /\bquantityAvailable\b/.test(body)) {
|
|
166
187
|
log("no inventory permission, retrying without quantityAvailable");
|
|
167
188
|
return gql(query.replace(/\bquantityAvailable\b/g, ""), variables, { context });
|
|
168
189
|
}
|
|
169
|
-
const throttled = json.errors.some((e) =>
|
|
170
|
-
var _a2;
|
|
171
|
-
return ((_a2 = e.extensions) == null ? void 0 : _a2.code) === "THROTTLED";
|
|
172
|
-
});
|
|
190
|
+
const throttled = json.errors.some((e) => e.extensions?.code === "THROTTLED");
|
|
173
191
|
if (throttled && attempt++ < cfg.retries) {
|
|
174
192
|
await sleep(500 * 2 ** attempt);
|
|
175
193
|
continue;
|
|
@@ -192,7 +210,24 @@
|
|
|
192
210
|
return payload;
|
|
193
211
|
}
|
|
194
212
|
|
|
213
|
+
// src/cart.js
|
|
214
|
+
var cart_exports = {};
|
|
215
|
+
__export(cart_exports, {
|
|
216
|
+
Cart: () => Cart,
|
|
217
|
+
focusables: () => focusables,
|
|
218
|
+
toLineInput: () => toLineInput
|
|
219
|
+
});
|
|
220
|
+
|
|
195
221
|
// src/fragments.js
|
|
222
|
+
var fragments_exports = {};
|
|
223
|
+
__export(fragments_exports, {
|
|
224
|
+
ALLOCATION_FIELDS: () => ALLOCATION_FIELDS,
|
|
225
|
+
SELLING_PLAN_FIELDS: () => SELLING_PLAN_FIELDS,
|
|
226
|
+
cartFragment: () => cartFragment,
|
|
227
|
+
collectionFields: () => collectionFields,
|
|
228
|
+
metafieldIdentifiers: () => metafieldIdentifiers,
|
|
229
|
+
productFragment: () => productFragment
|
|
230
|
+
});
|
|
196
231
|
var MONEY = "amount currencyCode";
|
|
197
232
|
var IMAGE = "url altText width height";
|
|
198
233
|
function metafieldIdentifiers() {
|
|
@@ -208,112 +243,15 @@
|
|
|
208
243
|
const ids = metafieldIdentifiers();
|
|
209
244
|
if (!ids.length) return "";
|
|
210
245
|
const list = ids.map((m) => `{namespace: ${JSON.stringify(m.namespace)}, key: ${JSON.stringify(m.key)}}`).join(", ");
|
|
211
|
-
return `metafields(identifiers:
|
|
212
|
-
namespace key type value
|
|
213
|
-
reference { ... on MediaImage { image { ${IMAGE} } } }
|
|
214
|
-
}`;
|
|
215
|
-
}
|
|
216
|
-
var SELLING_PLAN_FIELDS = `
|
|
217
|
-
id name description recurringDeliveries
|
|
218
|
-
options { name value }
|
|
219
|
-
priceAdjustments {
|
|
220
|
-
orderCount
|
|
221
|
-
adjustmentValue {
|
|
222
|
-
__typename
|
|
223
|
-
... on SellingPlanPercentagePriceAdjustment { adjustmentPercentage }
|
|
224
|
-
... on SellingPlanFixedAmountPriceAdjustment { adjustmentAmount { ${MONEY} } }
|
|
225
|
-
... on SellingPlanFixedPriceAdjustment { price { ${MONEY} } }
|
|
226
|
-
}
|
|
227
|
-
}`;
|
|
228
|
-
var ALLOCATION_FIELDS = `
|
|
229
|
-
sellingPlan { id }
|
|
230
|
-
checkoutChargeAmount { ${MONEY} }
|
|
231
|
-
remainingBalanceChargeAmount { ${MONEY} }
|
|
232
|
-
priceAdjustments {
|
|
233
|
-
price { ${MONEY} }
|
|
234
|
-
compareAtPrice { ${MONEY} }
|
|
235
|
-
perDeliveryPrice { ${MONEY} }
|
|
236
|
-
unitPrice { ${MONEY} }
|
|
237
|
-
}`;
|
|
238
|
-
function productFragment() {
|
|
239
|
-
return `
|
|
240
|
-
fragment TetherProduct on Product {
|
|
241
|
-
id handle title description descriptionHtml productType vendor tags
|
|
242
|
-
availableForSale publishedAt onlineStoreUrl requiresSellingPlan
|
|
243
|
-
seo { title description }
|
|
244
|
-
featuredImage { ${IMAGE} }
|
|
245
|
-
images(first: ${cfg.imagesFirst}) { nodes { ${IMAGE} } }
|
|
246
|
-
options { name optionValues { name swatch { color image { previewImage { ${IMAGE} } } } } }
|
|
247
|
-
priceRange { minVariantPrice { ${MONEY} } maxVariantPrice { ${MONEY} } }
|
|
248
|
-
compareAtPriceRange { minVariantPrice { ${MONEY} } maxVariantPrice { ${MONEY} } }
|
|
249
|
-
${metafieldsField()}
|
|
250
|
-
sellingPlanGroups(first: ${cfg.sellingPlanGroupsFirst}) {
|
|
251
|
-
nodes {
|
|
252
|
-
name appName
|
|
253
|
-
options { name values }
|
|
254
|
-
sellingPlans(first: ${cfg.sellingPlansFirst}) { nodes { ${SELLING_PLAN_FIELDS} } }
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
variants(first: ${cfg.variantsFirst}) {
|
|
258
|
-
nodes {
|
|
259
|
-
id title availableForSale currentlyNotInStock quantityAvailable sku barcode
|
|
260
|
-
selectedOptions { name value }
|
|
261
|
-
image { ${IMAGE} }
|
|
262
|
-
price { ${MONEY} }
|
|
263
|
-
compareAtPrice { ${MONEY} }
|
|
264
|
-
unitPrice { ${MONEY} }
|
|
265
|
-
sellingPlanAllocations(first: ${cfg.sellingPlansFirst}) { nodes { ${ALLOCATION_FIELDS} } }
|
|
266
|
-
}
|
|
246
|
+
return `metafields(identifiers:[${list}]){namespace key type value reference{... on MediaImage{image{${IMAGE}}}}}`;
|
|
267
247
|
}
|
|
268
|
-
}`;
|
|
248
|
+
var SELLING_PLAN_FIELDS = ` id name description recurringDeliveries options{name value}priceAdjustments{orderCount adjustmentValue{__typename ... on SellingPlanPercentagePriceAdjustment{adjustmentPercentage}... on SellingPlanFixedAmountPriceAdjustment{adjustmentAmount{${MONEY}}}... on SellingPlanFixedPriceAdjustment{price{${MONEY}}}}}`;
|
|
249
|
+
var ALLOCATION_FIELDS = ` sellingPlan{id}checkoutChargeAmount{${MONEY}}remainingBalanceChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}unitPrice{${MONEY}}}`;
|
|
250
|
+
function productFragment() {
|
|
251
|
+
return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${cfg.imagesFirst}){nodes{${IMAGE}}}options{name optionValues{name swatch{color image{previewImage{${IMAGE}}}}}}priceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}compareAtPriceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}${metafieldsField()} sellingPlanGroups(first:${cfg.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${cfg.sellingPlansFirst}){nodes{${SELLING_PLAN_FIELDS}}}}}variants(first:${cfg.variantsFirst}){nodes{id title availableForSale currentlyNotInStock quantityAvailable sku barcode selectedOptions{name value}image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}unitPrice{${MONEY}}sellingPlanAllocations(first:${cfg.sellingPlansFirst}){nodes{${ALLOCATION_FIELDS}}}}}}`;
|
|
269
252
|
}
|
|
270
253
|
function cartFragment() {
|
|
271
|
-
return `
|
|
272
|
-
fragment TetherCart on Cart {
|
|
273
|
-
id checkoutUrl totalQuantity note createdAt updatedAt
|
|
274
|
-
attributes { key value }
|
|
275
|
-
buyerIdentity { countryCode email }
|
|
276
|
-
discountCodes { code applicable }
|
|
277
|
-
cost {
|
|
278
|
-
subtotalAmount { ${MONEY} }
|
|
279
|
-
totalAmount { ${MONEY} }
|
|
280
|
-
checkoutChargeAmount { ${MONEY} }
|
|
281
|
-
}
|
|
282
|
-
lines(first: 100) {
|
|
283
|
-
nodes {
|
|
284
|
-
id quantity
|
|
285
|
-
attributes { key value }
|
|
286
|
-
cost {
|
|
287
|
-
totalAmount { ${MONEY} }
|
|
288
|
-
subtotalAmount { ${MONEY} }
|
|
289
|
-
amountPerQuantity { ${MONEY} }
|
|
290
|
-
compareAtAmountPerQuantity { ${MONEY} }
|
|
291
|
-
}
|
|
292
|
-
discountAllocations { discountedAmount { ${MONEY} } }
|
|
293
|
-
sellingPlanAllocation {
|
|
294
|
-
sellingPlan { id name description recurringDeliveries options { name value } }
|
|
295
|
-
checkoutChargeAmount { ${MONEY} }
|
|
296
|
-
priceAdjustments { price { ${MONEY} } compareAtPrice { ${MONEY} } perDeliveryPrice { ${MONEY} } }
|
|
297
|
-
}
|
|
298
|
-
merchandise {
|
|
299
|
-
... on ProductVariant {
|
|
300
|
-
id title sku availableForSale requiresShipping
|
|
301
|
-
image { ${IMAGE} }
|
|
302
|
-
price { ${MONEY} }
|
|
303
|
-
compareAtPrice { ${MONEY} }
|
|
304
|
-
selectedOptions { name value }
|
|
305
|
-
product { id title handle vendor featuredImage { ${IMAGE} } }
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
... on ComponentizableCartLine {
|
|
309
|
-
lineComponents {
|
|
310
|
-
id quantity
|
|
311
|
-
merchandise { ... on ProductVariant { id title product { title handle } } }
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}`;
|
|
254
|
+
return ` fragment TetherCart on Cart{id checkoutUrl totalQuantity note createdAt updatedAt attributes{key value}buyerIdentity{countryCode email}discountCodes{code applicable}cost{subtotalAmount{${MONEY}}totalAmount{${MONEY}}checkoutChargeAmount{${MONEY}}}lines(first:100){nodes{id quantity attributes{key value}cost{totalAmount{${MONEY}}subtotalAmount{${MONEY}}amountPerQuantity{${MONEY}}compareAtAmountPerQuantity{${MONEY}}}discountAllocations{discountedAmount{${MONEY}}}sellingPlanAllocation{sellingPlan{id name description recurringDeliveries options{name value}}checkoutChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}}}merchandise{... on ProductVariant{id title sku availableForSale requiresShipping image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}selectedOptions{name value}product{id title handle vendor featuredImage{${IMAGE}}}}}... on ComponentizableCartLine{lineComponents{id quantity merchandise{... on ProductVariant{id title product{title handle}}}}}}}}`;
|
|
317
255
|
}
|
|
318
256
|
function collectionFields() {
|
|
319
257
|
return `
|
|
@@ -323,6 +261,15 @@ fragment TetherCart on Cart {
|
|
|
323
261
|
}
|
|
324
262
|
|
|
325
263
|
// src/loader.js
|
|
264
|
+
var loader_exports = {};
|
|
265
|
+
__export(loader_exports, {
|
|
266
|
+
clearProductCache: () => clearProductCache,
|
|
267
|
+
getProduct: () => getProduct,
|
|
268
|
+
getProducts: () => getProducts,
|
|
269
|
+
getRecommendations: () => getRecommendations,
|
|
270
|
+
normalizeProductKey: () => normalizeProductKey,
|
|
271
|
+
primeProduct: () => primeProduct
|
|
272
|
+
});
|
|
326
273
|
var productCache = /* @__PURE__ */ new Map();
|
|
327
274
|
var pending = [];
|
|
328
275
|
var resolvers = /* @__PURE__ */ new Map();
|
|
@@ -330,7 +277,7 @@ fragment TetherCart on Cart {
|
|
|
330
277
|
var generation = 0;
|
|
331
278
|
var isGid = (v) => typeof v === "string" && v.startsWith("gid://");
|
|
332
279
|
function normalizeProductKey(raw) {
|
|
333
|
-
const v = String(raw
|
|
280
|
+
const v = String(raw ?? "").trim();
|
|
334
281
|
if (!v) return null;
|
|
335
282
|
if (isGid(v)) return v;
|
|
336
283
|
if (/^\d+$/.test(v)) return `gid://shopify/Product/${v}`;
|
|
@@ -366,22 +313,19 @@ fragment TetherCart on Cart {
|
|
|
366
313
|
const ids = keys.filter(isGid);
|
|
367
314
|
const handles = keys.filter((k) => !isGid(k));
|
|
368
315
|
const defs = [];
|
|
369
|
-
const
|
|
316
|
+
const parts2 = [];
|
|
370
317
|
const vars = {};
|
|
371
318
|
if (ids.length) {
|
|
372
319
|
defs.push("$ids: [ID!]!");
|
|
373
320
|
vars.ids = ids;
|
|
374
|
-
|
|
321
|
+
parts2.push("byId: nodes(ids: $ids) { ... on Product { ...TetherProduct } }");
|
|
375
322
|
}
|
|
376
323
|
handles.forEach((h, i) => {
|
|
377
324
|
defs.push(`$h${i}: String!`);
|
|
378
325
|
vars[`h${i}`] = h;
|
|
379
|
-
|
|
326
|
+
parts2.push(`h${i}: product(handle: $h${i}) { ...TetherProduct }`);
|
|
380
327
|
});
|
|
381
|
-
const query = `query TetherProducts(${defs.join(",
|
|
382
|
-
${parts.join("\n ")}
|
|
383
|
-
}
|
|
384
|
-
${productFragment()}`;
|
|
328
|
+
const query = `query TetherProducts(${defs.join(",")}){${parts2.join(" ")}}${productFragment()}`;
|
|
385
329
|
log("fetch products", keys);
|
|
386
330
|
const settle = (k, fn) => {
|
|
387
331
|
const r = resolvers.get(k);
|
|
@@ -428,16 +372,7 @@ ${productFragment()}`;
|
|
|
428
372
|
} = {}) {
|
|
429
373
|
const fragment = productFragment();
|
|
430
374
|
if (collection) {
|
|
431
|
-
const data2 = await gql(`query TetherCollection($handle:
|
|
432
|
-
collection(handle: $handle) {
|
|
433
|
-
${collectionFields()}
|
|
434
|
-
products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse, filters: $filters) {
|
|
435
|
-
nodes { ...TetherProduct }
|
|
436
|
-
pageInfo { hasNextPage endCursor }
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
${fragment}`, {
|
|
375
|
+
const data2 = await gql(`query TetherCollection($handle:String!,$first:Int!,$after:String,$sortKey:ProductCollectionSortKeys,$reverse:Boolean,$filters:[ProductFilter!]){collection(handle:$handle){${collectionFields()} products(first:$first,after:$after,sortKey:$sortKey,reverse:$reverse,filters:$filters){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}}${fragment}`, {
|
|
441
376
|
handle: collection,
|
|
442
377
|
first,
|
|
443
378
|
after,
|
|
@@ -450,21 +385,12 @@ ${fragment}`, {
|
|
|
450
385
|
products.nodes.forEach(primeProduct);
|
|
451
386
|
return { collection: rest, products: products.nodes, pageInfo: products.pageInfo };
|
|
452
387
|
}
|
|
453
|
-
const data = await gql(`query TetherSearch($first:
|
|
454
|
-
products(first: $first, after: $after, query: $query, sortKey: $sortKey, reverse: $reverse) {
|
|
455
|
-
nodes { ...TetherProduct }
|
|
456
|
-
pageInfo { hasNextPage endCursor }
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
${fragment}`, { first, after, query: query || void 0, sortKey: sortKey || void 0, reverse });
|
|
388
|
+
const data = await gql(`query TetherSearch($first:Int!,$after:String,$query:String,$sortKey:ProductSortKeys,$reverse:Boolean){products(first:$first,after:$after,query:$query,sortKey:$sortKey,reverse:$reverse){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}${fragment}`, { first, after, query: query || void 0, sortKey: sortKey || void 0, reverse });
|
|
460
389
|
data.products.nodes.forEach(primeProduct);
|
|
461
390
|
return { collection: null, products: data.products.nodes, pageInfo: data.products.pageInfo };
|
|
462
391
|
}
|
|
463
392
|
async function getRecommendations(productId, intent = "RELATED") {
|
|
464
|
-
const data = await gql(`query TetherRecs($id:
|
|
465
|
-
productRecommendations(productId: $id, intent: $intent) { ...TetherProduct }
|
|
466
|
-
}
|
|
467
|
-
${productFragment()}`, { id: productId, intent });
|
|
393
|
+
const data = await gql(`query TetherRecs($id:ID!,$intent:ProductRecommendationIntent){productRecommendations(productId:$id,intent:$intent){...TetherProduct}}${productFragment()}`, { id: productId, intent });
|
|
468
394
|
const list = data.productRecommendations || [];
|
|
469
395
|
list.forEach(primeProduct);
|
|
470
396
|
return list;
|
|
@@ -491,49 +417,38 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
491
417
|
sellingPlan: (v) => toGid(v, "SellingPlan")
|
|
492
418
|
};
|
|
493
419
|
function toGid(value, type) {
|
|
494
|
-
const v = String(value
|
|
420
|
+
const v = String(value ?? "").trim();
|
|
495
421
|
if (!v) return null;
|
|
496
422
|
if (v.startsWith("gid://")) return v;
|
|
497
423
|
if (/^\d+$/.test(v)) return `gid://shopify/${type}/${v}`;
|
|
498
424
|
return v;
|
|
499
425
|
}
|
|
500
426
|
function allPlans(product) {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
return groups.flatMap((g) => {
|
|
504
|
-
var _a2;
|
|
505
|
-
return (((_a2 = g.sellingPlans) == null ? void 0 : _a2.nodes) || []).map((p) => ({ ...p, group: g }));
|
|
506
|
-
});
|
|
427
|
+
const groups = product?.sellingPlanGroups?.nodes || [];
|
|
428
|
+
return groups.flatMap((g) => (g.sellingPlans?.nodes || []).map((p) => ({ ...p, group: g })));
|
|
507
429
|
}
|
|
508
430
|
function hasPlans(product) {
|
|
509
431
|
return allPlans(product).length > 0;
|
|
510
432
|
}
|
|
511
433
|
function allocationFor(variant, planId) {
|
|
512
|
-
var _a;
|
|
513
434
|
if (!variant || !planId) return null;
|
|
514
|
-
return (
|
|
515
|
-
var _a2;
|
|
516
|
-
return ((_a2 = a.sellingPlan) == null ? void 0 : _a2.id) === planId;
|
|
517
|
-
}) || null;
|
|
435
|
+
return (variant.sellingPlanAllocations?.nodes || []).find((a) => a.sellingPlan?.id === planId) || null;
|
|
518
436
|
}
|
|
519
437
|
function plansForVariant(product, variant) {
|
|
520
438
|
return allPlans(product).filter((p) => allocationFor(variant, p.id));
|
|
521
439
|
}
|
|
522
440
|
function planPrice(variant, planId) {
|
|
523
|
-
var _a;
|
|
524
441
|
const a = allocationFor(variant, planId);
|
|
525
|
-
const adj =
|
|
526
|
-
return
|
|
442
|
+
const adj = a?.priceAdjustments?.[0];
|
|
443
|
+
return adj?.perDeliveryPrice || adj?.price || variant?.price || null;
|
|
527
444
|
}
|
|
528
445
|
function planCheckoutCharge(variant, planId) {
|
|
529
|
-
var _a, _b;
|
|
530
446
|
const a = allocationFor(variant, planId);
|
|
531
|
-
return
|
|
447
|
+
return a?.checkoutChargeAmount || a?.priceAdjustments?.[0]?.price || variant?.price || null;
|
|
532
448
|
}
|
|
533
449
|
function planSavings(variant, planId) {
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
const sub = parseFloat((_b = planPrice(variant, planId)) == null ? void 0 : _b.amount);
|
|
450
|
+
const base = parseFloat(variant?.price?.amount);
|
|
451
|
+
const sub = parseFloat(planPrice(variant, planId)?.amount);
|
|
537
452
|
if (!Number.isFinite(base) || !Number.isFinite(sub) || base <= 0 || sub >= base) {
|
|
538
453
|
return { amount: null, percentage: 0 };
|
|
539
454
|
}
|
|
@@ -543,8 +458,7 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
543
458
|
};
|
|
544
459
|
}
|
|
545
460
|
function planAdjustment(plan) {
|
|
546
|
-
|
|
547
|
-
const v = (_b = (_a = plan == null ? void 0 : plan.priceAdjustments) == null ? void 0 : _a[0]) == null ? void 0 : _b.adjustmentValue;
|
|
461
|
+
const v = plan?.priceAdjustments?.[0]?.adjustmentValue;
|
|
548
462
|
if (!v) return null;
|
|
549
463
|
switch (v.__typename) {
|
|
550
464
|
case "SellingPlanPercentagePriceAdjustment":
|
|
@@ -566,6 +480,35 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
566
480
|
}
|
|
567
481
|
|
|
568
482
|
// src/dom.js
|
|
483
|
+
var dom_exports = {};
|
|
484
|
+
__export(dom_exports, {
|
|
485
|
+
attr: () => attr,
|
|
486
|
+
busy: () => busy,
|
|
487
|
+
cdnUrl: () => cdnUrl,
|
|
488
|
+
dataKey: () => dataKey,
|
|
489
|
+
emit: () => emit,
|
|
490
|
+
formatDate: () => formatDate,
|
|
491
|
+
formatMoney: () => formatMoney,
|
|
492
|
+
get: () => get,
|
|
493
|
+
has: () => has,
|
|
494
|
+
hasTemplate: () => hasTemplate,
|
|
495
|
+
notifyLayoutChanged: () => notifyLayoutChanged,
|
|
496
|
+
parseJSONAttr: () => parseJSONAttr,
|
|
497
|
+
productUrl: () => productUrl,
|
|
498
|
+
qsa: () => qsa,
|
|
499
|
+
readText: () => readText,
|
|
500
|
+
repeat: () => repeat,
|
|
501
|
+
repeatKeyed: () => repeatKeyed,
|
|
502
|
+
scoped: () => scoped,
|
|
503
|
+
sel: () => sel,
|
|
504
|
+
setImage: () => setImage,
|
|
505
|
+
setSelected: () => setSelected,
|
|
506
|
+
setText: () => setText,
|
|
507
|
+
setTextTracked: () => setTextTracked,
|
|
508
|
+
show: () => show,
|
|
509
|
+
toAttributes: () => toAttributes,
|
|
510
|
+
watchImages: () => watchImages
|
|
511
|
+
});
|
|
569
512
|
var attr = (name) => `${cfg.prefix}-${name}`;
|
|
570
513
|
var sel = (name, value) => value === void 0 ? `[${attr(name)}]` : `[${attr(name)}="${value}"]`;
|
|
571
514
|
var get = (el, name) => el.getAttribute(attr(name));
|
|
@@ -666,19 +609,17 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
666
609
|
}
|
|
667
610
|
}
|
|
668
611
|
function setSelected(el, selected) {
|
|
669
|
-
var _a;
|
|
670
612
|
el.classList.toggle("is-selected", selected);
|
|
671
613
|
el.setAttribute("aria-pressed", selected ? "true" : "false");
|
|
672
614
|
if (el.tagName === "INPUT" && (el.type === "radio" || el.type === "checkbox")) el.checked = selected;
|
|
673
|
-
const input =
|
|
615
|
+
const input = el.querySelector?.('input[type="radio"],input[type="checkbox"]');
|
|
674
616
|
if (input) input.checked = selected;
|
|
675
617
|
}
|
|
676
618
|
var templates = /* @__PURE__ */ new WeakMap();
|
|
677
619
|
var hasTemplate = (container) => Boolean(templates.get(container));
|
|
678
620
|
function template(container) {
|
|
679
|
-
var _a, _b;
|
|
680
621
|
if (!templates.has(container)) {
|
|
681
|
-
const tpl =
|
|
622
|
+
const tpl = container.firstElementChild?.cloneNode(true) ?? null;
|
|
682
623
|
templates.set(container, tpl);
|
|
683
624
|
container.innerHTML = "";
|
|
684
625
|
}
|
|
@@ -760,9 +701,8 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
760
701
|
function notifyLayoutChanged(reason) {
|
|
761
702
|
clearTimeout(layoutTimer);
|
|
762
703
|
layoutTimer = setTimeout(() => {
|
|
763
|
-
var _a;
|
|
764
704
|
if (typeof cfg.onLayoutChange === "function") cfg.onLayoutChange(reason);
|
|
765
|
-
else if (
|
|
705
|
+
else if (window.ScrollTrigger?.refresh) window.ScrollTrigger.refresh();
|
|
766
706
|
emit("layout-changed", { reason });
|
|
767
707
|
}, 120);
|
|
768
708
|
}
|
|
@@ -795,20 +735,16 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
795
735
|
}
|
|
796
736
|
function toAttributes(obj) {
|
|
797
737
|
if (!obj) return [];
|
|
798
|
-
if (Array.isArray(obj)) return obj.map((a) => {
|
|
799
|
-
|
|
800
|
-
return { key: String(a.key), value: String((_a = a.value) != null ? _a : "") };
|
|
801
|
-
});
|
|
802
|
-
return Object.entries(obj).map(([key, value]) => ({ key, value: String(value != null ? value : "") }));
|
|
738
|
+
if (Array.isArray(obj)) return obj.map((a) => ({ key: String(a.key), value: String(a.value ?? "") }));
|
|
739
|
+
return Object.entries(obj).map(([key, value]) => ({ key, value: String(value ?? "") }));
|
|
803
740
|
}
|
|
804
741
|
|
|
805
742
|
// src/cart.js
|
|
806
743
|
function toLineInput(input, quantity = 1, opts = {}) {
|
|
807
|
-
var _a;
|
|
808
744
|
const src = typeof input === "object" && input !== null ? input : { variantId: input, quantity, ...opts };
|
|
809
745
|
const merchandiseId = GID.variant(src.merchandiseId || src.variantId || src.id);
|
|
810
746
|
if (!merchandiseId) throw new TetherError("add(): variantId is required");
|
|
811
|
-
const line = { merchandiseId, quantity: Math.max(1, parseInt(
|
|
747
|
+
const line = { merchandiseId, quantity: Math.max(1, parseInt(src.quantity ?? 1, 10) || 1) };
|
|
812
748
|
const plan = src.sellingPlanId || src.sellingPlan;
|
|
813
749
|
if (plan) line.sellingPlanId = GID.sellingPlan(plan);
|
|
814
750
|
const attrs = toAttributes(src.attributes);
|
|
@@ -816,12 +752,21 @@ ${productFragment()}`, { id: productId, intent });
|
|
|
816
752
|
return line;
|
|
817
753
|
}
|
|
818
754
|
function mutation(name, args, inner) {
|
|
819
|
-
return `mutation Tether_${name}(${args}) {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
755
|
+
return `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}}}${cartFragment()}`;
|
|
756
|
+
}
|
|
757
|
+
var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
758
|
+
var returnFocus = null;
|
|
759
|
+
var focusables = (d) => qsa(d, FOCUSABLE).filter((el) => el.offsetParent !== null || el === document.activeElement);
|
|
760
|
+
function ariaState(open) {
|
|
761
|
+
qsa(document, sel("cart-open")).forEach((b) => b.setAttribute("aria-expanded", String(open)));
|
|
762
|
+
qsa(document, sel("cart-drawer")).forEach((d) => {
|
|
763
|
+
if (!d.hasAttribute("role")) d.setAttribute("role", "dialog");
|
|
764
|
+
if (!d.hasAttribute("aria-label") && !d.hasAttribute("aria-labelledby")) d.setAttribute("aria-label", get(d, "cart-label") || "Cart");
|
|
765
|
+
d.setAttribute("aria-modal", String(open));
|
|
766
|
+
if (!d.classList.contains(cfg.cartDrawerOpenClass)) d.setAttribute("aria-hidden", "true");
|
|
767
|
+
});
|
|
823
768
|
}
|
|
824
|
-
var isMissingCart = (err) => /does not exist|not found|invalid id/i.test(
|
|
769
|
+
var isMissingCart = (err) => /does not exist|not found|invalid id/i.test(err?.message || "");
|
|
825
770
|
var Cart = {
|
|
826
771
|
state: null,
|
|
827
772
|
_chain: Promise.resolve(),
|
|
@@ -840,12 +785,10 @@ ${cartFragment()}`;
|
|
|
840
785
|
}
|
|
841
786
|
},
|
|
842
787
|
get lines() {
|
|
843
|
-
|
|
844
|
-
return ((_b = (_a = this.state) == null ? void 0 : _a.lines) == null ? void 0 : _b.nodes) || [];
|
|
788
|
+
return this.state?.lines?.nodes || [];
|
|
845
789
|
},
|
|
846
790
|
get count() {
|
|
847
|
-
|
|
848
|
-
return ((_a = this.state) == null ? void 0 : _a.totalQuantity) || 0;
|
|
791
|
+
return this.state?.totalQuantity || 0;
|
|
849
792
|
},
|
|
850
793
|
lastError: null,
|
|
851
794
|
_queue(fn) {
|
|
@@ -867,7 +810,7 @@ ${cartFragment()}`;
|
|
|
867
810
|
},
|
|
868
811
|
_commit(cart, reason) {
|
|
869
812
|
this.state = cart;
|
|
870
|
-
if (cart
|
|
813
|
+
if (cart?.id) this._setId(cart.id);
|
|
871
814
|
this.render();
|
|
872
815
|
emit("cart-updated", { cart, reason });
|
|
873
816
|
return cart;
|
|
@@ -880,8 +823,7 @@ ${cartFragment()}`;
|
|
|
880
823
|
return null;
|
|
881
824
|
}
|
|
882
825
|
try {
|
|
883
|
-
const data = await gql(`query TetherCart($id:
|
|
884
|
-
${cartFragment()}`, { id });
|
|
826
|
+
const data = await gql(`query TetherCart($id:ID!){cart(id:$id){...TetherCart}}${cartFragment()}`, { id });
|
|
885
827
|
if (!data.cart) {
|
|
886
828
|
this._setId(null);
|
|
887
829
|
this.state = null;
|
|
@@ -966,14 +908,13 @@ ${cartFragment()}`, { id });
|
|
|
966
908
|
},
|
|
967
909
|
setNote(note) {
|
|
968
910
|
return this._queue(async () => this._commit(
|
|
969
|
-
await this._mutate("cartNoteUpdate", "$note: String!", "note: $note", { note: note
|
|
911
|
+
await this._mutate("cartNoteUpdate", "$note: String!", "note: $note", { note: note ?? "" }, { note: note ?? "" }),
|
|
970
912
|
"note"
|
|
971
913
|
));
|
|
972
914
|
},
|
|
973
915
|
/** Cart-level attributes, merged with existing ones. Value '' removes a key. */
|
|
974
916
|
setAttributes(obj) {
|
|
975
|
-
|
|
976
|
-
const merged = new Map((((_a = this.state) == null ? void 0 : _a.attributes) || []).map((a) => [a.key, a.value]));
|
|
917
|
+
const merged = new Map((this.state?.attributes || []).map((a) => [a.key, a.value]));
|
|
977
918
|
toAttributes(obj).forEach((a) => merged.set(a.key, a.value));
|
|
978
919
|
const attributes = [...merged].filter(([, v]) => v !== "").map(([key, value]) => ({ key, value }));
|
|
979
920
|
return this._queue(async () => this._commit(
|
|
@@ -990,13 +931,11 @@ ${cartFragment()}`, { id });
|
|
|
990
931
|
));
|
|
991
932
|
},
|
|
992
933
|
applyDiscount(code) {
|
|
993
|
-
|
|
994
|
-
const current = (((_a = this.state) == null ? void 0 : _a.discountCodes) || []).map((d) => d.code);
|
|
934
|
+
const current = (this.state?.discountCodes || []).map((d) => d.code);
|
|
995
935
|
return this.setDiscountCodes([.../* @__PURE__ */ new Set([...current, code])]);
|
|
996
936
|
},
|
|
997
937
|
removeDiscount(code) {
|
|
998
|
-
|
|
999
|
-
return this.setDiscountCodes((((_a = this.state) == null ? void 0 : _a.discountCodes) || []).map((d) => d.code).filter((c) => c !== code));
|
|
938
|
+
return this.setDiscountCodes((this.state?.discountCodes || []).map((d) => d.code).filter((c) => c !== code));
|
|
1000
939
|
},
|
|
1001
940
|
/** { countryCode, email, phone } — changes currency/prices for that market. */
|
|
1002
941
|
setBuyerIdentity(buyerIdentity, { create = true } = {}) {
|
|
@@ -1015,7 +954,6 @@ ${cartFragment()}`, { id });
|
|
|
1015
954
|
* URL once we have it.
|
|
1016
955
|
*/
|
|
1017
956
|
async checkout(target = cfg.checkoutTarget) {
|
|
1018
|
-
var _a;
|
|
1019
957
|
const id = this.id();
|
|
1020
958
|
if (!id) return null;
|
|
1021
959
|
const tab = target === "_blank" ? window.open("", "_blank") : null;
|
|
@@ -1027,7 +965,7 @@ ${cartFragment()}`, { id });
|
|
|
1027
965
|
}
|
|
1028
966
|
try {
|
|
1029
967
|
const data = await gql("query TetherCheckout($id: ID!) { cart(id: $id) { checkoutUrl } }", { id });
|
|
1030
|
-
const url =
|
|
968
|
+
const url = data.cart?.checkoutUrl;
|
|
1031
969
|
if (!url) throw new TetherError("No checkoutUrl (cart empty or expired)");
|
|
1032
970
|
emit("checkout", { url, cart: this.state });
|
|
1033
971
|
if (tab && !tab.closed) tab.location.href = url;
|
|
@@ -1053,16 +991,29 @@ ${cartFragment()}`, { id });
|
|
|
1053
991
|
d.classList.add(cfg.cartDrawerOpenClass);
|
|
1054
992
|
d.setAttribute("aria-hidden", "false");
|
|
1055
993
|
});
|
|
994
|
+
ariaState(true);
|
|
995
|
+
if (cfg.cartFocus !== false && ds[0]) {
|
|
996
|
+
returnFocus = document.activeElement;
|
|
997
|
+
setTimeout(() => {
|
|
998
|
+
const target = focusables(ds[0])[0] || ds[0];
|
|
999
|
+
if (target === ds[0] && !ds[0].hasAttribute("tabindex")) ds[0].tabIndex = -1;
|
|
1000
|
+
target.focus?.({ preventScroll: true });
|
|
1001
|
+
}, 60);
|
|
1002
|
+
}
|
|
1056
1003
|
if (typeof cfg.onCartOpen === "function") cfg.onCartOpen(ds);
|
|
1057
1004
|
emit("cart-toggled", { open: true });
|
|
1058
1005
|
},
|
|
1059
1006
|
close() {
|
|
1060
1007
|
if (!this.isOpen()) return;
|
|
1061
1008
|
const ds = this.drawers();
|
|
1009
|
+
const hadFocus = ds.some((d) => d.contains(document.activeElement));
|
|
1062
1010
|
ds.forEach((d) => {
|
|
1063
1011
|
d.classList.remove(cfg.cartDrawerOpenClass);
|
|
1064
1012
|
d.setAttribute("aria-hidden", "true");
|
|
1065
1013
|
});
|
|
1014
|
+
ariaState(false);
|
|
1015
|
+
if (hadFocus && returnFocus?.isConnected) returnFocus.focus({ preventScroll: true });
|
|
1016
|
+
returnFocus = null;
|
|
1066
1017
|
if (typeof cfg.onCartClose === "function") cfg.onCartClose(ds);
|
|
1067
1018
|
emit("cart-toggled", { open: false });
|
|
1068
1019
|
},
|
|
@@ -1074,10 +1025,8 @@ ${cartFragment()}`, { id });
|
|
|
1074
1025
|
render() {
|
|
1075
1026
|
const cart = this.state;
|
|
1076
1027
|
const count = cart ? cart.totalQuantity : 0;
|
|
1077
|
-
const money = (k) =>
|
|
1078
|
-
|
|
1079
|
-
return cart ? formatMoney((_a = cart.cost) == null ? void 0 : _a[k]) : "";
|
|
1080
|
-
};
|
|
1028
|
+
const money = (k) => cart ? formatMoney(cart.cost?.[k]) : "";
|
|
1029
|
+
ariaState(this.isOpen());
|
|
1081
1030
|
qsa(document, sel("cart-count")).forEach((el) => setTextTracked(el, count));
|
|
1082
1031
|
qsa(document, sel("cart-subtotal")).forEach((el) => setTextTracked(el, money("subtotalAmount")));
|
|
1083
1032
|
qsa(document, sel("cart-total")).forEach((el) => setTextTracked(el, money("totalAmount")));
|
|
@@ -1088,7 +1037,7 @@ ${cartFragment()}`, { id });
|
|
|
1088
1037
|
"cart-filled": count > 0,
|
|
1089
1038
|
"cart-has-subscription": hasSub,
|
|
1090
1039
|
"cart-no-subscription": !hasSub,
|
|
1091
|
-
"cart-has-discount": (
|
|
1040
|
+
"cart-has-discount": (cart?.discountCodes || []).some((d) => d.applicable),
|
|
1092
1041
|
"cart-error": Boolean(this.lastError)
|
|
1093
1042
|
};
|
|
1094
1043
|
qsa(document, sel("show")).forEach((el) => {
|
|
@@ -1096,10 +1045,10 @@ ${cartFragment()}`, { id });
|
|
|
1096
1045
|
if (k in conds && !el.closest(sel("product"))) show(el, conds[k]);
|
|
1097
1046
|
});
|
|
1098
1047
|
qsa(document, `textarea${sel("cart-note")},input${sel("cart-note")}`).forEach((el) => {
|
|
1099
|
-
if (document.activeElement !== el) el.value =
|
|
1048
|
+
if (document.activeElement !== el) el.value = cart?.note || "";
|
|
1100
1049
|
});
|
|
1101
1050
|
qsa(document, sel("cart-discounts")).forEach((container) => {
|
|
1102
|
-
repeat(container,
|
|
1051
|
+
repeat(container, cart?.discountCodes || [], (node, d) => {
|
|
1103
1052
|
node.setAttribute(attr("discount-code"), d.code);
|
|
1104
1053
|
const t = node.querySelector(sel("discount", "code")) || (node.firstElementChild ? null : node);
|
|
1105
1054
|
if (t) setText(t, d.code);
|
|
@@ -1116,60 +1065,54 @@ ${cartFragment()}`, { id });
|
|
|
1116
1065
|
}
|
|
1117
1066
|
};
|
|
1118
1067
|
function lineValue(line, key) {
|
|
1119
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1120
1068
|
const m = line.merchandise || {};
|
|
1121
|
-
const plan =
|
|
1069
|
+
const plan = line.sellingPlanAllocation?.sellingPlan;
|
|
1122
1070
|
switch (key) {
|
|
1123
1071
|
case "title":
|
|
1124
|
-
return
|
|
1072
|
+
return m.product?.title;
|
|
1125
1073
|
case "product-title":
|
|
1126
|
-
return
|
|
1074
|
+
return m.product?.title;
|
|
1127
1075
|
case "variant-title":
|
|
1128
1076
|
return m.title === "Default Title" ? "" : m.title;
|
|
1129
1077
|
case "options":
|
|
1130
1078
|
return (m.selectedOptions || []).filter((o) => o.value !== "Default Title").map((o) => `${o.name}: ${o.value}`).join(", ");
|
|
1131
1079
|
case "vendor":
|
|
1132
|
-
return
|
|
1080
|
+
return m.product?.vendor;
|
|
1133
1081
|
case "sku":
|
|
1134
1082
|
return m.sku;
|
|
1135
1083
|
case "price":
|
|
1136
|
-
return formatMoney(
|
|
1084
|
+
return formatMoney(line.cost?.amountPerQuantity || m.price);
|
|
1137
1085
|
case "compare-at-price":
|
|
1138
|
-
return formatMoney(
|
|
1086
|
+
return formatMoney(line.cost?.compareAtAmountPerQuantity || m.compareAtPrice);
|
|
1139
1087
|
case "line-total":
|
|
1140
|
-
return formatMoney(
|
|
1088
|
+
return formatMoney(line.cost?.totalAmount);
|
|
1141
1089
|
case "line-subtotal":
|
|
1142
|
-
return formatMoney(
|
|
1090
|
+
return formatMoney(line.cost?.subtotalAmount);
|
|
1143
1091
|
case "quantity":
|
|
1144
1092
|
return line.quantity;
|
|
1145
1093
|
case "plan":
|
|
1146
|
-
return
|
|
1094
|
+
return plan?.name || "";
|
|
1147
1095
|
case "plan-description":
|
|
1148
|
-
return
|
|
1096
|
+
return plan?.description || "";
|
|
1149
1097
|
case "components":
|
|
1150
|
-
return (line.lineComponents || []).map((c) => {
|
|
1151
|
-
var _a2, _b2;
|
|
1152
|
-
return `${c.quantity} \xD7 ${((_b2 = (_a2 = c.merchandise) == null ? void 0 : _a2.product) == null ? void 0 : _b2.title) || ""}`;
|
|
1153
|
-
}).join(", ");
|
|
1098
|
+
return (line.lineComponents || []).map((c) => `${c.quantity} \xD7 ${c.merchandise?.product?.title || ""}`).join(", ");
|
|
1154
1099
|
default:
|
|
1155
1100
|
if (key.startsWith("attribute:")) {
|
|
1156
1101
|
const k = key.slice("attribute:".length);
|
|
1157
|
-
return (
|
|
1102
|
+
return (line.attributes || []).find((a) => a.key === k)?.value || "";
|
|
1158
1103
|
}
|
|
1159
1104
|
return void 0;
|
|
1160
1105
|
}
|
|
1161
1106
|
}
|
|
1162
1107
|
function renderLine(node, line) {
|
|
1163
|
-
var _a, _b, _c, _d;
|
|
1164
1108
|
const m = line.merchandise || {};
|
|
1165
1109
|
const isSub = Boolean(line.sellingPlanAllocation);
|
|
1166
1110
|
node.setAttribute(attr("line-id"), line.id);
|
|
1167
1111
|
node.classList.toggle("is-subscription", isSub);
|
|
1168
1112
|
qsa(node, sel("line")).forEach((el) => {
|
|
1169
|
-
var _a2, _b2, _c2, _d2, _e, _f;
|
|
1170
1113
|
const key = get(el, "line");
|
|
1171
|
-
if (key === "image") return setImage(el,
|
|
1172
|
-
if (key === "url") return el.setAttribute("href", productUrl(
|
|
1114
|
+
if (key === "image") return setImage(el, m.image?.url || m.product?.featuredImage?.url, m.product?.title);
|
|
1115
|
+
if (key === "url") return el.setAttribute("href", productUrl(m.product?.handle, m.product?.id));
|
|
1173
1116
|
const v = lineValue(line, key);
|
|
1174
1117
|
if (v !== void 0) setTextTracked(el, v);
|
|
1175
1118
|
return void 0;
|
|
@@ -1177,7 +1120,7 @@ ${cartFragment()}`, { id });
|
|
|
1177
1120
|
const lineConds = {
|
|
1178
1121
|
subscription: isSub,
|
|
1179
1122
|
"one-time": !isSub,
|
|
1180
|
-
"on-sale": parseFloat(
|
|
1123
|
+
"on-sale": parseFloat(line.cost?.compareAtAmountPerQuantity?.amount) > parseFloat(line.cost?.amountPerQuantity?.amount),
|
|
1181
1124
|
"has-components": (line.lineComponents || []).length > 0
|
|
1182
1125
|
};
|
|
1183
1126
|
qsa(node, sel("line-show")).forEach((el) => {
|
|
@@ -1185,11 +1128,9 @@ ${cartFragment()}`, { id });
|
|
|
1185
1128
|
if (k in lineConds) show(el, lineConds[k]);
|
|
1186
1129
|
});
|
|
1187
1130
|
qsa(node, `select${sel("line-plan")}`).forEach((select) => {
|
|
1188
|
-
|
|
1189
|
-
const handle = (_a2 = m.product) == null ? void 0 : _a2.handle;
|
|
1131
|
+
const handle = m.product?.handle;
|
|
1190
1132
|
if (!handle) return;
|
|
1191
1133
|
getProduct(handle).then((product) => {
|
|
1192
|
-
var _a3, _b2;
|
|
1193
1134
|
const plans = allPlans(product);
|
|
1194
1135
|
const allowOneTime = !product.requiresSellingPlan && get(select, "line-plan") !== "subscription-only";
|
|
1195
1136
|
const want = [
|
|
@@ -1207,21 +1148,37 @@ ${cartFragment()}`, { id });
|
|
|
1207
1148
|
});
|
|
1208
1149
|
select.dataset.tetherSig = sig;
|
|
1209
1150
|
}
|
|
1210
|
-
select.value =
|
|
1151
|
+
select.value = line.sellingPlanAllocation?.sellingPlan?.id || "";
|
|
1211
1152
|
show(select, plans.length > 0);
|
|
1212
1153
|
}).catch((err) => reportError(err, "line plan options"));
|
|
1213
1154
|
});
|
|
1214
1155
|
}
|
|
1215
1156
|
|
|
1157
|
+
// src/product.js
|
|
1158
|
+
var product_exports = {};
|
|
1159
|
+
__export(product_exports, {
|
|
1160
|
+
ProductScope: () => ProductScope,
|
|
1161
|
+
scopes: () => scopes
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1216
1164
|
// src/status.js
|
|
1165
|
+
var status_exports = {};
|
|
1166
|
+
__export(status_exports, {
|
|
1167
|
+
STATUS_KEYS: () => STATUS_KEYS,
|
|
1168
|
+
boundary: () => boundary,
|
|
1169
|
+
isNotFound: () => isNotFound,
|
|
1170
|
+
ownedBy: () => ownedBy,
|
|
1171
|
+
resolveProductKey: () => resolveProductKey,
|
|
1172
|
+
setScopeStatus: () => setScopeStatus
|
|
1173
|
+
});
|
|
1217
1174
|
function resolveProductKey(el, location = window.location) {
|
|
1218
1175
|
const raw = (get(el, "product") || "").trim();
|
|
1219
1176
|
if (!raw.toLowerCase().startsWith("url:")) return { key: raw || null, fromUrl: false, raw };
|
|
1220
1177
|
const what = raw.slice(4).trim();
|
|
1221
1178
|
let key = null;
|
|
1222
1179
|
if (what.toLowerCase() === "path") {
|
|
1223
|
-
const
|
|
1224
|
-
key =
|
|
1180
|
+
const parts2 = location.pathname.split("/").filter(Boolean);
|
|
1181
|
+
key = parts2.length ? safeDecode(parts2[parts2.length - 1]) : null;
|
|
1225
1182
|
} else {
|
|
1226
1183
|
key = new URLSearchParams(location.search).get(what || "handle");
|
|
1227
1184
|
}
|
|
@@ -1236,16 +1193,13 @@ ${cartFragment()}`, { id });
|
|
|
1236
1193
|
}
|
|
1237
1194
|
}
|
|
1238
1195
|
var STATUS_KEYS = ["loading", "ready", "error", "not-found"];
|
|
1239
|
-
var isNotFound = (err) =>
|
|
1196
|
+
var isNotFound = (err) => err?.code === "NOT_FOUND";
|
|
1240
1197
|
var boundary = () => ["product", "collection", "products", "recommendations"].map((n) => sel(n)).join(",");
|
|
1241
|
-
var ownedBy = (el, root) =>
|
|
1242
|
-
var _a;
|
|
1243
|
-
return el === root || ((_a = el.parentElement) == null ? void 0 : _a.closest(boundary())) === root;
|
|
1244
|
-
};
|
|
1198
|
+
var ownedBy = (el, root) => el === root || el.parentElement?.closest(boundary()) === root;
|
|
1245
1199
|
function setScopeStatus(root, status, err) {
|
|
1246
1200
|
const notFound = status === "not-found";
|
|
1247
1201
|
root.setAttribute(attr("state"), notFound ? "error" : status);
|
|
1248
|
-
if (status === "error" || notFound) root.setAttribute(attr("error"), notFound ? "not-found" :
|
|
1202
|
+
if (status === "error" || notFound) root.setAttribute(attr("error"), notFound ? "not-found" : err?.code || "error");
|
|
1249
1203
|
else root.removeAttribute(attr("error"));
|
|
1250
1204
|
scoped(root, sel("show"), boundary()).forEach((el) => {
|
|
1251
1205
|
const k = get(el, "show");
|
|
@@ -1259,6 +1213,7 @@ ${cartFragment()}`, { id });
|
|
|
1259
1213
|
if (status === "error" || notFound) {
|
|
1260
1214
|
scoped(root, sel("add-to-cart"), boundary()).forEach((btn) => {
|
|
1261
1215
|
btn.classList.add("is-disabled");
|
|
1216
|
+
btn.setAttribute("aria-disabled", "true");
|
|
1262
1217
|
if ("disabled" in btn) btn.disabled = true;
|
|
1263
1218
|
});
|
|
1264
1219
|
}
|
|
@@ -1276,12 +1231,11 @@ ${cartFragment()}`, { id });
|
|
|
1276
1231
|
}
|
|
1277
1232
|
var ProductScope = class {
|
|
1278
1233
|
constructor(root, product) {
|
|
1279
|
-
var _a;
|
|
1280
1234
|
this.root = root;
|
|
1281
1235
|
this.product = product;
|
|
1282
1236
|
this.selected = {};
|
|
1283
1237
|
this.variant = null;
|
|
1284
|
-
this.quantity = Math.max(0, parseInt(
|
|
1238
|
+
this.quantity = Math.max(0, parseInt(get(root, "quantity") ?? "1", 10) || (has(root, "quantity") ? 0 : 1));
|
|
1285
1239
|
this.plans = allPlans(product);
|
|
1286
1240
|
this.purchase = "one-time";
|
|
1287
1241
|
this.planId = null;
|
|
@@ -1299,11 +1253,10 @@ ${cartFragment()}`, { id });
|
|
|
1299
1253
|
}
|
|
1300
1254
|
/** New data for the same product (e.g. after a country switch). Keeps the selection. */
|
|
1301
1255
|
update(product) {
|
|
1302
|
-
|
|
1303
|
-
const variantId = (_a = this.variant) == null ? void 0 : _a.id;
|
|
1256
|
+
const variantId = this.variant?.id;
|
|
1304
1257
|
this.product = product;
|
|
1305
1258
|
this.plans = allPlans(product);
|
|
1306
|
-
if (this.planId && !this.plans.some((p) => p.id === this.planId)) this.planId =
|
|
1259
|
+
if (this.planId && !this.plans.some((p) => p.id === this.planId)) this.planId = this.plans[0]?.id || null;
|
|
1307
1260
|
if (!this.plans.length && this.purchase === "subscription") this.purchase = "one-time";
|
|
1308
1261
|
const v = product.variants.nodes.find((x) => x.id === variantId);
|
|
1309
1262
|
if (v) {
|
|
@@ -1425,8 +1378,7 @@ ${cartFragment()}`, { id });
|
|
|
1425
1378
|
this.renderVariant();
|
|
1426
1379
|
}
|
|
1427
1380
|
async addToCart() {
|
|
1428
|
-
|
|
1429
|
-
if (!((_a = this.variant) == null ? void 0 : _a.availableForSale)) return null;
|
|
1381
|
+
if (!this.variant?.availableForSale) return null;
|
|
1430
1382
|
const line = this.getLine();
|
|
1431
1383
|
if (!line || line.quantity < 1) return null;
|
|
1432
1384
|
const cart = await Cart.add(line);
|
|
@@ -1438,7 +1390,7 @@ ${cartFragment()}`, { id });
|
|
|
1438
1390
|
const own = (el) => el && el.closest(productSel()) === this.root;
|
|
1439
1391
|
this.root.addEventListener("click", (e) => {
|
|
1440
1392
|
const t = e.target;
|
|
1441
|
-
if (!
|
|
1393
|
+
if (!t?.closest) return;
|
|
1442
1394
|
const opt = t.closest(sel("option-value"));
|
|
1443
1395
|
if (own(opt) && opt.closest(sel("option-list"))) {
|
|
1444
1396
|
e.preventDefault();
|
|
@@ -1510,22 +1462,19 @@ ${cartFragment()}`, { id });
|
|
|
1510
1462
|
renderProduct() {
|
|
1511
1463
|
const p = this.product;
|
|
1512
1464
|
this.$(sel("bind")).forEach((el) => {
|
|
1513
|
-
|
|
1514
|
-
const
|
|
1465
|
+
const key = get(el, "bind") || "";
|
|
1466
|
+
const txt = {
|
|
1467
|
+
title: p.title,
|
|
1468
|
+
"description-text": p.description,
|
|
1469
|
+
vendor: p.vendor,
|
|
1470
|
+
type: p.productType,
|
|
1471
|
+
handle: p.handle
|
|
1472
|
+
}[key];
|
|
1473
|
+
if (txt !== void 0) return setText(el, txt);
|
|
1515
1474
|
switch (key) {
|
|
1516
|
-
case "title":
|
|
1517
|
-
return setText(el, p.title);
|
|
1518
1475
|
case "description":
|
|
1519
1476
|
el.innerHTML = p.descriptionHtml || "";
|
|
1520
1477
|
return void 0;
|
|
1521
|
-
case "description-text":
|
|
1522
|
-
return setText(el, p.description);
|
|
1523
|
-
case "vendor":
|
|
1524
|
-
return setText(el, p.vendor);
|
|
1525
|
-
case "type":
|
|
1526
|
-
return setText(el, p.productType);
|
|
1527
|
-
case "handle":
|
|
1528
|
-
return setText(el, p.handle);
|
|
1529
1478
|
case "published-at":
|
|
1530
1479
|
return setText(el, formatDate(p.publishedAt));
|
|
1531
1480
|
case "min-price":
|
|
@@ -1533,7 +1482,7 @@ ${cartFragment()}`, { id });
|
|
|
1533
1482
|
case "max-price":
|
|
1534
1483
|
return setText(el, formatMoney(p.priceRange.maxVariantPrice));
|
|
1535
1484
|
case "featured-image":
|
|
1536
|
-
return setImage(el,
|
|
1485
|
+
return setImage(el, p.featuredImage?.url, p.featuredImage?.altText || p.title);
|
|
1537
1486
|
case "url":
|
|
1538
1487
|
case "link":
|
|
1539
1488
|
el.setAttribute("href", productUrl(p.handle, p.id));
|
|
@@ -1549,19 +1498,15 @@ ${cartFragment()}`, { id });
|
|
|
1549
1498
|
return repeat(el, p.tags.filter((t) => !exclude.includes(t)), (node, tag) => setText(node, tag));
|
|
1550
1499
|
}
|
|
1551
1500
|
default:
|
|
1552
|
-
if (
|
|
1553
|
-
const img = p.images.nodes[
|
|
1554
|
-
return setImage(el, img
|
|
1501
|
+
if (/^image:\d+$/.test(key)) {
|
|
1502
|
+
const img = p.images.nodes[key.slice(6) - 1] || p.featuredImage;
|
|
1503
|
+
return setImage(el, img?.url, img?.altText || p.title);
|
|
1555
1504
|
}
|
|
1556
|
-
if (key
|
|
1557
|
-
const mf = metafieldValue(p, key.slice(
|
|
1505
|
+
if (key.startsWith("metafield:")) {
|
|
1506
|
+
const mf = metafieldValue(p, key.slice(10));
|
|
1558
1507
|
if (!mf) return setText(el, "");
|
|
1559
|
-
if (
|
|
1560
|
-
if (mf.type
|
|
1561
|
-
el.textContent = mf.value;
|
|
1562
|
-
el.style.whiteSpace = "pre-line";
|
|
1563
|
-
return void 0;
|
|
1564
|
-
}
|
|
1508
|
+
if (mf.reference?.image) return setImage(el, mf.reference.image.url, mf.reference.image.altText);
|
|
1509
|
+
if (/^(rich_text|multi_line_text)_field$/.test(mf.type)) el.style.whiteSpace = "pre-line";
|
|
1565
1510
|
return setText(el, mf.value);
|
|
1566
1511
|
}
|
|
1567
1512
|
return void 0;
|
|
@@ -1572,14 +1517,13 @@ ${cartFragment()}`, { id });
|
|
|
1572
1517
|
const opt = p.options.find((o) => o.name === name);
|
|
1573
1518
|
if (!opt || opt.optionValues.length <= 1 && has(container, "hide-single")) return show(container, false);
|
|
1574
1519
|
return repeat(container, opt.optionValues, (node, v) => {
|
|
1575
|
-
var _a, _b, _c;
|
|
1576
1520
|
node.setAttribute(attr("option-value"), v.name);
|
|
1577
1521
|
const t = node.querySelector(sel("bind", "option-value"));
|
|
1578
1522
|
if (t) setText(t, v.name);
|
|
1579
1523
|
else if (!node.firstElementChild) setText(node, v.name);
|
|
1580
1524
|
const sw = node.querySelector(sel("bind", "swatch"));
|
|
1581
|
-
if (sw &&
|
|
1582
|
-
if (sw &&
|
|
1525
|
+
if (sw && v.swatch?.color) sw.style.backgroundColor = v.swatch.color;
|
|
1526
|
+
if (sw && v.swatch?.image?.previewImage) setImage(sw, v.swatch.image.previewImage.url, v.name);
|
|
1583
1527
|
});
|
|
1584
1528
|
});
|
|
1585
1529
|
this.$(`select${sel("option")}`).forEach((s) => {
|
|
@@ -1611,7 +1555,6 @@ ${cartFragment()}`, { id });
|
|
|
1611
1555
|
});
|
|
1612
1556
|
}
|
|
1613
1557
|
renderVariant() {
|
|
1614
|
-
var _a, _b, _c;
|
|
1615
1558
|
const p = this.product;
|
|
1616
1559
|
let v = this.findVariant();
|
|
1617
1560
|
if (!v) {
|
|
@@ -1633,63 +1576,40 @@ ${cartFragment()}`, { id });
|
|
|
1633
1576
|
const oneTimePrice = v.price;
|
|
1634
1577
|
const subPrice = this.planId ? planPrice(v, this.planId) : null;
|
|
1635
1578
|
const price = sub ? subPrice : oneTimePrice;
|
|
1636
|
-
const compareAt = sub ?
|
|
1579
|
+
const compareAt = sub ? allocationFor(v, this.planId)?.priceAdjustments?.[0]?.compareAtPrice || v.compareAtPrice : v.compareAtPrice;
|
|
1637
1580
|
const onSale = compareAt && parseFloat(compareAt.amount) > parseFloat(price.amount);
|
|
1638
1581
|
const discountPct = onSale ? Math.round((1 - parseFloat(price.amount) / parseFloat(compareAt.amount)) * 100) : 0;
|
|
1639
1582
|
const savings = this.planId ? planSavings(v, this.planId) : { amount: null, percentage: 0 };
|
|
1640
1583
|
const plan = this.plan;
|
|
1641
1584
|
const total = { amount: (parseFloat(price.amount) * Math.max(1, this.quantity)).toFixed(2), currencyCode: price.currencyCode };
|
|
1585
|
+
const fm = formatMoney;
|
|
1586
|
+
const pct = (n) => n ? `${n}%` : "";
|
|
1587
|
+
const text = {
|
|
1588
|
+
price: () => fm(price),
|
|
1589
|
+
"total-price": () => fm(total),
|
|
1590
|
+
"one-time-price": () => fm(oneTimePrice),
|
|
1591
|
+
"subscription-price": () => fm(subPrice),
|
|
1592
|
+
"subscription-savings": () => pct(savings.percentage),
|
|
1593
|
+
"subscription-savings-amount": () => fm(savings.amount),
|
|
1594
|
+
"checkout-charge": () => fm(sub ? planCheckoutCharge(v, this.planId) : price),
|
|
1595
|
+
"plan-name": () => plan?.name,
|
|
1596
|
+
"plan-description": () => plan?.description,
|
|
1597
|
+
"plan-group": () => plan?.group?.name,
|
|
1598
|
+
"compare-at-price": () => onSale ? fm(compareAt) : "",
|
|
1599
|
+
"unit-price": () => fm(v.unitPrice),
|
|
1600
|
+
"discount-percentage": () => `${discountPct}%`,
|
|
1601
|
+
"discount-amount": () => onSale ? fm({ amount: String(parseFloat(compareAt.amount) - parseFloat(price.amount)), currencyCode: price.currencyCode }) : "",
|
|
1602
|
+
"variant-title": () => v.title,
|
|
1603
|
+
"variant-id": () => v.id,
|
|
1604
|
+
sku: () => v.sku,
|
|
1605
|
+
barcode: () => v.barcode,
|
|
1606
|
+
stock: () => v.quantityAvailable,
|
|
1607
|
+
quantity: () => this.quantity
|
|
1608
|
+
};
|
|
1642
1609
|
this.$(sel("bind")).forEach((el) => {
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
return setText(el, formatMoney(price));
|
|
1647
|
-
case "total-price":
|
|
1648
|
-
return setText(el, formatMoney(total));
|
|
1649
|
-
case "one-time-price":
|
|
1650
|
-
return setText(el, formatMoney(oneTimePrice));
|
|
1651
|
-
case "subscription-price":
|
|
1652
|
-
return setText(el, formatMoney(subPrice));
|
|
1653
|
-
case "subscription-savings":
|
|
1654
|
-
return setText(el, savings.percentage ? `${savings.percentage}%` : "");
|
|
1655
|
-
case "subscription-savings-amount":
|
|
1656
|
-
return setText(el, formatMoney(savings.amount));
|
|
1657
|
-
case "checkout-charge":
|
|
1658
|
-
return setText(el, formatMoney(sub ? planCheckoutCharge(v, this.planId) : price));
|
|
1659
|
-
case "plan-name":
|
|
1660
|
-
return setText(el, (plan == null ? void 0 : plan.name) || "");
|
|
1661
|
-
case "plan-description":
|
|
1662
|
-
return setText(el, (plan == null ? void 0 : plan.description) || "");
|
|
1663
|
-
case "plan-group":
|
|
1664
|
-
return setText(el, ((_a2 = plan == null ? void 0 : plan.group) == null ? void 0 : _a2.name) || "");
|
|
1665
|
-
case "compare-at-price":
|
|
1666
|
-
return setText(el, onSale ? formatMoney(compareAt) : "");
|
|
1667
|
-
case "unit-price":
|
|
1668
|
-
return setText(el, formatMoney(v.unitPrice));
|
|
1669
|
-
case "discount-percentage":
|
|
1670
|
-
return setText(el, `${discountPct}%`);
|
|
1671
|
-
case "discount-amount":
|
|
1672
|
-
return setText(el, onSale ? formatMoney({
|
|
1673
|
-
amount: String(parseFloat(compareAt.amount) - parseFloat(price.amount)),
|
|
1674
|
-
currencyCode: price.currencyCode
|
|
1675
|
-
}) : "");
|
|
1676
|
-
case "variant-title":
|
|
1677
|
-
return setText(el, v.title);
|
|
1678
|
-
case "variant-id":
|
|
1679
|
-
return setText(el, v.id);
|
|
1680
|
-
case "sku":
|
|
1681
|
-
return setText(el, v.sku);
|
|
1682
|
-
case "barcode":
|
|
1683
|
-
return setText(el, v.barcode);
|
|
1684
|
-
case "stock":
|
|
1685
|
-
return setText(el, (_b2 = v.quantityAvailable) != null ? _b2 : "");
|
|
1686
|
-
case "quantity":
|
|
1687
|
-
return setText(el, this.quantity);
|
|
1688
|
-
case "variant-image":
|
|
1689
|
-
return setImage(el, ((_c2 = v.image) == null ? void 0 : _c2.url) || ((_d = p.featuredImage) == null ? void 0 : _d.url), ((_e = v.image) == null ? void 0 : _e.altText) || p.title);
|
|
1690
|
-
default:
|
|
1691
|
-
return void 0;
|
|
1692
|
-
}
|
|
1610
|
+
const k = get(el, "bind");
|
|
1611
|
+
if (k === "variant-image") setImage(el, v.image?.url || p.featuredImage?.url, v.image?.altText || p.title);
|
|
1612
|
+
else if (text[k]) setText(el, text[k]());
|
|
1693
1613
|
});
|
|
1694
1614
|
this.$(sel("option-selected")).forEach((el) => setText(el, this.selected[get(el, "option-selected")] || ""));
|
|
1695
1615
|
this.$(sel("option-list")).forEach((container) => {
|
|
@@ -1727,23 +1647,16 @@ ${cartFragment()}`, { id });
|
|
|
1727
1647
|
qsa(node, sel("plan-bind")).forEach((el) => {
|
|
1728
1648
|
const adj = planAdjustment(planObj);
|
|
1729
1649
|
const s = planSavings(v, id);
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
return setText(el, formatMoney(s.amount));
|
|
1741
|
-
case "adjustment":
|
|
1742
|
-
if (!adj) return setText(el, "");
|
|
1743
|
-
return setText(el, adj.type === "percentage" ? `${adj.value}%` : formatMoney(adj.value));
|
|
1744
|
-
default:
|
|
1745
|
-
return void 0;
|
|
1746
|
-
}
|
|
1650
|
+
const k = get(el, "plan-bind");
|
|
1651
|
+
const val = {
|
|
1652
|
+
name: () => planObj?.name,
|
|
1653
|
+
description: () => planObj?.description,
|
|
1654
|
+
price: () => fm(planPrice(v, id)),
|
|
1655
|
+
savings: () => pct(s.percentage),
|
|
1656
|
+
"savings-amount": () => fm(s.amount),
|
|
1657
|
+
adjustment: () => !adj ? "" : adj.type === "percentage" ? `${adj.value}%` : fm(adj.value)
|
|
1658
|
+
}[k];
|
|
1659
|
+
if (val) setText(el, val());
|
|
1747
1660
|
});
|
|
1748
1661
|
});
|
|
1749
1662
|
this.$(`select${sel("plan")}`).forEach((s) => {
|
|
@@ -1782,16 +1695,17 @@ ${cartFragment()}`, { id });
|
|
|
1782
1695
|
this.$(sel("add-to-cart")).forEach((btn) => {
|
|
1783
1696
|
const disabled = !v.availableForSale;
|
|
1784
1697
|
btn.classList.toggle("is-disabled", disabled);
|
|
1698
|
+
btn.setAttribute("aria-disabled", String(disabled));
|
|
1785
1699
|
if ("disabled" in btn) btn.disabled = disabled;
|
|
1786
1700
|
const soldOut = get(btn, "soldout-text");
|
|
1787
1701
|
const subText = get(btn, "subscribe-text");
|
|
1788
1702
|
if (soldOut || subText) {
|
|
1789
1703
|
const label = btn.querySelector(sel("add-label")) || btn;
|
|
1790
1704
|
if (label.dataset.tetherLabel === void 0) label.dataset.tetherLabel = label.tagName === "INPUT" ? label.value : label.textContent;
|
|
1791
|
-
let
|
|
1792
|
-
if (sub && subText)
|
|
1793
|
-
if (disabled && soldOut)
|
|
1794
|
-
if (btn.getAttribute("aria-busy") !== "true") setText(label,
|
|
1705
|
+
let text2 = label.dataset.tetherLabel;
|
|
1706
|
+
if (sub && subText) text2 = subText;
|
|
1707
|
+
if (disabled && soldOut) text2 = soldOut;
|
|
1708
|
+
if (btn.getAttribute("aria-busy") !== "true") setText(label, text2);
|
|
1795
1709
|
}
|
|
1796
1710
|
});
|
|
1797
1711
|
emit("variant-changed", {
|
|
@@ -1805,6 +1719,11 @@ ${cartFragment()}`, { id });
|
|
|
1805
1719
|
};
|
|
1806
1720
|
|
|
1807
1721
|
// src/collection.js
|
|
1722
|
+
var collection_exports = {};
|
|
1723
|
+
__export(collection_exports, {
|
|
1724
|
+
ListScope: () => ListScope,
|
|
1725
|
+
listSelector: () => listSelector
|
|
1726
|
+
});
|
|
1808
1727
|
var listSelector = () => `${sel("collection")},${sel("products")},${sel("recommendations")}`;
|
|
1809
1728
|
var ListScope = class {
|
|
1810
1729
|
constructor(root) {
|
|
@@ -1814,8 +1733,7 @@ ${cartFragment()}`, { id });
|
|
|
1814
1733
|
this.collection = null;
|
|
1815
1734
|
setScopeStatus(root, "loading");
|
|
1816
1735
|
root.addEventListener("click", (e) => {
|
|
1817
|
-
|
|
1818
|
-
const more = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, sel("load-more"));
|
|
1736
|
+
const more = e.target.closest?.(sel("load-more"));
|
|
1819
1737
|
if (more && more.closest(listSelector()) === root) {
|
|
1820
1738
|
e.preventDefault();
|
|
1821
1739
|
busy(more, () => this.load(true));
|
|
@@ -1837,7 +1755,6 @@ ${cartFragment()}`, { id });
|
|
|
1837
1755
|
return opts;
|
|
1838
1756
|
}
|
|
1839
1757
|
async load(more = false) {
|
|
1840
|
-
var _a;
|
|
1841
1758
|
const token = this.loadToken = (this.loadToken || 0) + 1;
|
|
1842
1759
|
const stale = () => token !== this.loadToken;
|
|
1843
1760
|
try {
|
|
@@ -1849,7 +1766,7 @@ ${cartFragment()}`, { id });
|
|
|
1849
1766
|
const limit = parseInt(get(this.root, "limit") || "", 10);
|
|
1850
1767
|
this.products = limit ? recs.slice(0, limit) : recs;
|
|
1851
1768
|
} else {
|
|
1852
|
-
const res = await getProducts({ ...this.options(), after: more ?
|
|
1769
|
+
const res = await getProducts({ ...this.options(), after: more ? this.pageInfo?.endCursor : null });
|
|
1853
1770
|
if (stale()) return;
|
|
1854
1771
|
this.collection = res.collection;
|
|
1855
1772
|
this.pageInfo = res.pageInfo;
|
|
@@ -1868,15 +1785,14 @@ ${cartFragment()}`, { id });
|
|
|
1868
1785
|
render() {
|
|
1869
1786
|
const c = this.collection;
|
|
1870
1787
|
this.$(sel("collection-bind")).forEach((el) => {
|
|
1871
|
-
var _a, _b;
|
|
1872
1788
|
switch (get(el, "collection-bind")) {
|
|
1873
1789
|
case "title":
|
|
1874
|
-
return setText(el,
|
|
1790
|
+
return setText(el, c?.title || "");
|
|
1875
1791
|
case "description":
|
|
1876
|
-
el.innerHTML =
|
|
1792
|
+
el.innerHTML = c?.descriptionHtml || "";
|
|
1877
1793
|
return void 0;
|
|
1878
1794
|
case "image":
|
|
1879
|
-
return setImage(el,
|
|
1795
|
+
return setImage(el, c?.image?.url, c?.image?.altText || c?.title);
|
|
1880
1796
|
case "count":
|
|
1881
1797
|
return setText(el, this.products.length);
|
|
1882
1798
|
default:
|
|
@@ -1897,330 +1813,24 @@ ${cartFragment()}`, { id });
|
|
|
1897
1813
|
if (k === "list-filled") el.style.display = empty ? "none" : "";
|
|
1898
1814
|
});
|
|
1899
1815
|
this.$(sel("load-more")).forEach((el) => {
|
|
1900
|
-
|
|
1901
|
-
el.style.display = ((_a = this.pageInfo) == null ? void 0 : _a.hasNextPage) ? "" : "none";
|
|
1816
|
+
el.style.display = this.pageInfo?.hasNextPage ? "" : "none";
|
|
1902
1817
|
});
|
|
1903
1818
|
notifyLayoutChanged("list");
|
|
1904
1819
|
}
|
|
1905
1820
|
};
|
|
1906
1821
|
|
|
1907
|
-
// src/
|
|
1908
|
-
var
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
}
|
|
1919
|
-
function apiVersionStatus(version, now = /* @__PURE__ */ new Date()) {
|
|
1920
|
-
if (version === "unstable") return { version, ok: false, note: '"unstable" changes without notice: pin a dated version such as 2026-07' };
|
|
1921
|
-
const p = parse(version);
|
|
1922
|
-
if (!p) return { version, ok: false, note: "not a quarterly version (YYYY-01, -04, -07 or -10)" };
|
|
1923
|
-
const released = new Date(Date.UTC(p.y, p.m - 1, 1));
|
|
1924
|
-
const supportedUntil = new Date(Date.UTC(p.y + 1, p.m - 1, 1));
|
|
1925
|
-
const monthsLeft = (supportedUntil - now) / (30.44 * 24 * 3600 * 1e3);
|
|
1926
|
-
let note = `supported until about ${supportedUntil.toISOString().slice(0, 7)}`;
|
|
1927
|
-
if (released > now) note = "not released yet (release candidate at best)";
|
|
1928
|
-
else if (monthsLeft <= 0) note = "no longer supported: Shopify falls back to the oldest supported version. Upgrade (see README)";
|
|
1929
|
-
else if (monthsLeft < 3) note = `${note}: plan the upgrade to ${nextApiVersion(version)} or later (see README)`;
|
|
1930
|
-
return { version, ok: released <= now && monthsLeft > 0, monthsLeft: Math.round(monthsLeft), next: nextApiVersion(version), note };
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
// src/markets.js
|
|
1934
|
-
var COUNTRY = /^[A-Z]{2}$/;
|
|
1935
|
-
var REGIONAL_LANGUAGES = /* @__PURE__ */ new Set(["PT_BR", "PT_PT", "ZH_CN", "ZH_TW"]);
|
|
1936
|
-
var storageKey = () => `${cfg.prefix}:market:${cfg.domain}`;
|
|
1937
|
-
var defaultEnv = () => ({
|
|
1938
|
-
document: typeof document !== "undefined" ? document : null,
|
|
1939
|
-
location: typeof window !== "undefined" ? window.location : null,
|
|
1940
|
-
storage: (() => {
|
|
1941
|
-
try {
|
|
1942
|
-
return window.localStorage;
|
|
1943
|
-
} catch {
|
|
1944
|
-
return null;
|
|
1945
|
-
}
|
|
1946
|
-
})(),
|
|
1947
|
-
global: typeof window !== "undefined" ? window : {}
|
|
1822
|
+
// src/mounting.js
|
|
1823
|
+
var mounting_exports = {};
|
|
1824
|
+
__export(mounting_exports, {
|
|
1825
|
+
liveLists: () => liveLists,
|
|
1826
|
+
liveProducts: () => liveProducts,
|
|
1827
|
+
mountProduct: () => mountProduct,
|
|
1828
|
+
mountedLists: () => mountedLists,
|
|
1829
|
+
mountedProducts: () => mountedProducts,
|
|
1830
|
+
pruneLive: () => pruneLive,
|
|
1831
|
+
retryFailed: () => retryFailed,
|
|
1832
|
+
within: () => within
|
|
1948
1833
|
});
|
|
1949
|
-
function toCountry(v) {
|
|
1950
|
-
const c = String(v || "").trim().toUpperCase();
|
|
1951
|
-
return COUNTRY.test(c) && c !== "XX" && c !== "T1" ? c : null;
|
|
1952
|
-
}
|
|
1953
|
-
function toLanguage(v) {
|
|
1954
|
-
const raw = String(v || "").trim().replace("-", "_").toUpperCase();
|
|
1955
|
-
if (!raw) return null;
|
|
1956
|
-
if (REGIONAL_LANGUAGES.has(raw)) return raw;
|
|
1957
|
-
const primary = raw.split("_")[0];
|
|
1958
|
-
return /^[A-Z]{2,3}$/.test(primary) ? primary : null;
|
|
1959
|
-
}
|
|
1960
|
-
function readCookie(doc, name) {
|
|
1961
|
-
if (!doc || !name) return null;
|
|
1962
|
-
const hit = String(doc.cookie || "").split(";").map((c) => c.trim()).find((c) => c.startsWith(`${name}=`));
|
|
1963
|
-
return hit ? decodeURIComponent(hit.slice(name.length + 1)) : null;
|
|
1964
|
-
}
|
|
1965
|
-
var lookup = (locales, key) => {
|
|
1966
|
-
if (!locales || !key) return null;
|
|
1967
|
-
const k = String(key).toLowerCase();
|
|
1968
|
-
const found = Object.keys(locales).find((x) => x.toLowerCase() === k);
|
|
1969
|
-
return found ? locales[found] : null;
|
|
1970
|
-
};
|
|
1971
|
-
var readers = {
|
|
1972
|
-
saved(m, env) {
|
|
1973
|
-
if (m.remember === false || !env.storage) return null;
|
|
1974
|
-
try {
|
|
1975
|
-
return JSON.parse(env.storage.getItem(storageKey()) || "null");
|
|
1976
|
-
} catch {
|
|
1977
|
-
return null;
|
|
1978
|
-
}
|
|
1979
|
-
},
|
|
1980
|
-
server(m, env) {
|
|
1981
|
-
const meta = (name) => {
|
|
1982
|
-
var _a, _b, _c;
|
|
1983
|
-
return (_c = (_b = (_a = env.document) == null ? void 0 : _a.querySelector) == null ? void 0 : _b.call(_a, `meta[name="${name}"]`)) == null ? void 0 : _c.getAttribute("content");
|
|
1984
|
-
};
|
|
1985
|
-
const country = env.global.TETHER_COUNTRY || meta("tether-country") || readCookie(env.document, m.cookie || "tether_country");
|
|
1986
|
-
const language = env.global.TETHER_LANGUAGE || meta("tether-language");
|
|
1987
|
-
return { country, language };
|
|
1988
|
-
},
|
|
1989
|
-
webflow(m, env) {
|
|
1990
|
-
var _a, _b, _c;
|
|
1991
|
-
const segment = (((_a = env.location) == null ? void 0 : _a.pathname) || "/").split("/").filter(Boolean)[0];
|
|
1992
|
-
const fromPath = lookup(m.locales, segment);
|
|
1993
|
-
if (fromPath) return fromPath;
|
|
1994
|
-
const lang = (_c = (_b = env.document) == null ? void 0 : _b.documentElement) == null ? void 0 : _c.getAttribute("lang");
|
|
1995
|
-
if (!lang) return null;
|
|
1996
|
-
const mapped = lookup(m.locales, lang) || lookup(m.locales, lang.split("-")[0]);
|
|
1997
|
-
if (mapped) return { locale: lang, ...mapped };
|
|
1998
|
-
if (m.locales && m.strict) return null;
|
|
1999
|
-
const region = lang.split("-")[1];
|
|
2000
|
-
return { language: lang, country: region && region.length === 2 ? region : null, locale: lang };
|
|
2001
|
-
}
|
|
2002
|
-
};
|
|
2003
|
-
function detectMarket(markets = cfg.markets, env = defaultEnv()) {
|
|
2004
|
-
const m = markets || {};
|
|
2005
|
-
if (m.detect === false) return {};
|
|
2006
|
-
const list = !m.detect ? ["saved"] : m.detect === true ? ["saved", "server", "webflow"] : [].concat(m.detect);
|
|
2007
|
-
markets = m;
|
|
2008
|
-
const out = { sources: {} };
|
|
2009
|
-
list.forEach((name) => {
|
|
2010
|
-
var _a;
|
|
2011
|
-
const r = (_a = readers[name]) == null ? void 0 : _a.call(readers, markets, env);
|
|
2012
|
-
if (!r) return;
|
|
2013
|
-
const vals = { country: toCountry(r.country), language: toLanguage(r.language), locale: r.locale || null };
|
|
2014
|
-
Object.entries(vals).forEach(([k, v]) => {
|
|
2015
|
-
if (v && !out[k]) {
|
|
2016
|
-
out[k] = v;
|
|
2017
|
-
out.sources[k] = name;
|
|
2018
|
-
}
|
|
2019
|
-
});
|
|
2020
|
-
});
|
|
2021
|
-
return out;
|
|
2022
|
-
}
|
|
2023
|
-
function rememberMarket(market) {
|
|
2024
|
-
var _a;
|
|
2025
|
-
if (((_a = cfg.markets) == null ? void 0 : _a.remember) === false) return;
|
|
2026
|
-
try {
|
|
2027
|
-
localStorage.setItem(storageKey(), JSON.stringify({ country: market.country, language: market.language || void 0 }));
|
|
2028
|
-
} catch {
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
var localizationPromise = null;
|
|
2032
|
-
function clearLocalization() {
|
|
2033
|
-
localizationPromise = null;
|
|
2034
|
-
}
|
|
2035
|
-
function getLocalization() {
|
|
2036
|
-
if (!localizationPromise) {
|
|
2037
|
-
localizationPromise = gql(`query TetherLocalization {
|
|
2038
|
-
localization {
|
|
2039
|
-
country { isoCode name currency { isoCode symbol } }
|
|
2040
|
-
language { isoCode name }
|
|
2041
|
-
availableCountries { isoCode name currency { isoCode symbol } availableLanguages { isoCode name } }
|
|
2042
|
-
}
|
|
2043
|
-
}`).then((d) => d.localization).catch((err) => {
|
|
2044
|
-
localizationPromise = null;
|
|
2045
|
-
throw err;
|
|
2046
|
-
});
|
|
2047
|
-
}
|
|
2048
|
-
return localizationPromise;
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
// src/diagnose.js
|
|
2052
|
-
var mask = (t) => t ? `${String(t).slice(0, 4)}\u2026${String(t).slice(-4)} (${String(t).length} chars)` : "(missing)";
|
|
2053
|
-
function describe(el) {
|
|
2054
|
-
const id = el.id ? `#${el.id}` : "";
|
|
2055
|
-
const cls = el.className && typeof el.className === "string" ? `.${el.className.trim().split(/\s+/).slice(0, 2).join(".")}` : "";
|
|
2056
|
-
return `<${el.tagName.toLowerCase()}${id}${cls}>`;
|
|
2057
|
-
}
|
|
2058
|
-
function checkMarkup(root = document) {
|
|
2059
|
-
const problems = [];
|
|
2060
|
-
const add = (level, message, el) => problems.push({ level, message, element: el ? describe(el) : void 0, el });
|
|
2061
|
-
const inProduct = (el) => Boolean(el.closest(sel("product")));
|
|
2062
|
-
const listSel = `${sel("collection")},${sel("products")},${sel("recommendations")}`;
|
|
2063
|
-
qsa(root, sel("product")).forEach((el) => {
|
|
2064
|
-
if (el.closest(listSel) && el.closest(sel("list"))) return;
|
|
2065
|
-
const { key, fromUrl, raw } = resolveProductKey(el);
|
|
2066
|
-
if (!raw) add("error", `${attr("product")} has no value. Bind it to the Shopify handle (CMS field), or use "url:handle".`, el);
|
|
2067
|
-
else if (fromUrl && !key) add("warn", `${attr("product")}="${raw}" but this URL has no value for it (e.g. ?handle=my-product).`, el);
|
|
2068
|
-
});
|
|
2069
|
-
const productOnly = ["bind", "option-list", "option", "variant-select", "add-to-cart", "purchase", "plan-list", "plan", "qty", "line-attribute"];
|
|
2070
|
-
productOnly.forEach((name) => {
|
|
2071
|
-
qsa(root, sel(name)).forEach((el) => {
|
|
2072
|
-
if (inProduct(el)) return;
|
|
2073
|
-
if (name === "line-attribute" && el.closest(sel("group"))) return;
|
|
2074
|
-
add("warn", `${attr(name)} outside any ${attr("product")}: it does nothing here.`, el);
|
|
2075
|
-
});
|
|
2076
|
-
});
|
|
2077
|
-
qsa(root, sel("group-add")).forEach((el) => {
|
|
2078
|
-
if (!el.closest(sel("group"))) add("warn", `${attr("group-add")} outside a ${attr("group")}.`, el);
|
|
2079
|
-
});
|
|
2080
|
-
[...qsa(root, sel("option-list")), ...qsa(root, sel("plan-list")), ...qsa(root, sel("cart-lines")), ...qsa(root, sel("list"))].forEach((el) => {
|
|
2081
|
-
if (!el.firstElementChild && !hasTemplate(el)) {
|
|
2082
|
-
add("warn", "Repeater without a template child: put one example element inside it.", el);
|
|
2083
|
-
}
|
|
2084
|
-
});
|
|
2085
|
-
qsa(root, listSel).forEach((el) => {
|
|
2086
|
-
if (!qsa(el, sel("list")).length) add("error", `List without a ${attr("list")} child: products have nowhere to render.`, el);
|
|
2087
|
-
});
|
|
2088
|
-
const known = /* @__PURE__ */ new Set([
|
|
2089
|
-
"in-stock",
|
|
2090
|
-
"out-of-stock",
|
|
2091
|
-
"backorder",
|
|
2092
|
-
"low-stock",
|
|
2093
|
-
"on-sale",
|
|
2094
|
-
"not-on-sale",
|
|
2095
|
-
"has-variants",
|
|
2096
|
-
"single-variant",
|
|
2097
|
-
"subscription-available",
|
|
2098
|
-
"subscription-unavailable",
|
|
2099
|
-
"subscription-only",
|
|
2100
|
-
"one-time-available",
|
|
2101
|
-
"subscription-selected",
|
|
2102
|
-
"one-time-selected",
|
|
2103
|
-
"has-savings",
|
|
2104
|
-
"loading",
|
|
2105
|
-
"ready",
|
|
2106
|
-
"error",
|
|
2107
|
-
"not-found",
|
|
2108
|
-
"cart-empty",
|
|
2109
|
-
"cart-filled",
|
|
2110
|
-
"cart-has-subscription",
|
|
2111
|
-
"cart-no-subscription",
|
|
2112
|
-
"cart-has-discount",
|
|
2113
|
-
"cart-error",
|
|
2114
|
-
"list-empty",
|
|
2115
|
-
"list-filled"
|
|
2116
|
-
]);
|
|
2117
|
-
qsa(root, sel("show")).forEach((el) => {
|
|
2118
|
-
const k = get(el, "show");
|
|
2119
|
-
if (!known.has(k)) add("warn", `Unknown ${attr("show")}="${k}".`, el);
|
|
2120
|
-
});
|
|
2121
|
-
qsa(root, sel("bind")).forEach((el) => {
|
|
2122
|
-
const k = get(el, "bind") || "";
|
|
2123
|
-
if (!k.startsWith("metafield:")) return;
|
|
2124
|
-
const ref = k.slice(10);
|
|
2125
|
-
const listed = (cfg.metafields || []).some((m) => (typeof m === "string" ? m : `${m.namespace}.${m.key}`) === ref);
|
|
2126
|
-
if (!listed) add("error", `${attr("bind")}="${k}" but "${ref}" is not in config.metafields, so it stays empty.`, el);
|
|
2127
|
-
});
|
|
2128
|
-
if (qsa(root, sel("cart-open")).length && !qsa(root, sel("cart-drawer")).length) {
|
|
2129
|
-
add("warn", `${attr("cart-open")} exists but there is no ${attr("cart-drawer")} on this page.`);
|
|
2130
|
-
}
|
|
2131
|
-
if (qsa(root, sel("cart-drawer")).length > 1) add("warn", `More than one ${attr("cart-drawer")}.`);
|
|
2132
|
-
qsa(root, `[${attr("state")}="error"]`).forEach((el) => {
|
|
2133
|
-
if (el.matches(boundary())) add("error", `Scope failed to load (${get(el, "error") || "error"}).`, el);
|
|
2134
|
-
});
|
|
2135
|
-
return problems;
|
|
2136
|
-
}
|
|
2137
|
-
async function diagnose({ root = document, silent = false, version, now = /* @__PURE__ */ new Date() } = {}) {
|
|
2138
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
2139
|
-
const report = {
|
|
2140
|
-
version,
|
|
2141
|
-
config: {
|
|
2142
|
-
domain: cfg.domain || "(missing)",
|
|
2143
|
-
token: mask(cfg.token),
|
|
2144
|
-
apiVersion: cfg.apiVersion,
|
|
2145
|
-
endpoint: cfg.domain ? endpoint() : null,
|
|
2146
|
-
country: cfg.country,
|
|
2147
|
-
language: cfg.language,
|
|
2148
|
-
locale: cfg.locale,
|
|
2149
|
-
markets: cfg.markets || null,
|
|
2150
|
-
prefix: cfg.prefix,
|
|
2151
|
-
productPath: cfg.productPath,
|
|
2152
|
-
metafields: cfg.metafields,
|
|
2153
|
-
autoMount: cfg.autoMount,
|
|
2154
|
-
observe: cfg.observe,
|
|
2155
|
-
onError: typeof cfg.onError === "function"
|
|
2156
|
-
},
|
|
2157
|
-
apiVersion: apiVersionStatus(cfg.apiVersion, now),
|
|
2158
|
-
market: ((_a = cfg.markets) == null ? void 0 : _a.detect) ? detectMarket(cfg.markets) : null,
|
|
2159
|
-
reachable: false,
|
|
2160
|
-
shop: null,
|
|
2161
|
-
latencyMs: null,
|
|
2162
|
-
products: [],
|
|
2163
|
-
sellingPlans: [],
|
|
2164
|
-
problems: []
|
|
2165
|
-
};
|
|
2166
|
-
if (!isConfigured()) {
|
|
2167
|
-
report.problems.push({ level: "error", message: "TETHER_CONFIG needs domain and token." });
|
|
2168
|
-
} else {
|
|
2169
|
-
const t0 = Date.now();
|
|
2170
|
-
try {
|
|
2171
|
-
const data = await gql("query TetherDiagnose { shop { name primaryDomain { url } paymentSettings { currencyCode } } }", {}, { context: false });
|
|
2172
|
-
report.reachable = true;
|
|
2173
|
-
report.shop = { name: (_b = data.shop) == null ? void 0 : _b.name, url: (_d = (_c = data.shop) == null ? void 0 : _c.primaryDomain) == null ? void 0 : _d.url, currency: (_f = (_e = data.shop) == null ? void 0 : _e.paymentSettings) == null ? void 0 : _f.currencyCode };
|
|
2174
|
-
} catch (err) {
|
|
2175
|
-
const hint = err.status === 401 || err.status === 403 ? "Token rejected: use the public Storefront token from the Headless channel, not an Admin token." : err.status === 404 ? "Store or API version not found: check domain (x.myshopify.com) and apiVersion." : "";
|
|
2176
|
-
report.problems.push({ level: "error", message: `Storefront API not reachable: ${err.message}${hint ? `. ${hint}` : ""}` });
|
|
2177
|
-
}
|
|
2178
|
-
report.latencyMs = Date.now() - t0;
|
|
2179
|
-
if (report.reachable) {
|
|
2180
|
-
const keys = [...new Set(qsa(root, sel("product")).map((el) => resolveProductKey(el).key).filter(Boolean))].slice(0, 20);
|
|
2181
|
-
const results = await Promise.allSettled(keys.map((k) => getProduct(k)));
|
|
2182
|
-
results.forEach((r, i) => {
|
|
2183
|
-
var _a2, _b2;
|
|
2184
|
-
if (r.status === "fulfilled") {
|
|
2185
|
-
const p = r.value;
|
|
2186
|
-
const plans = allPlans(p);
|
|
2187
|
-
report.products.push({ key: keys[i], handle: p.handle, title: p.title, plans: plans.length, requiresSellingPlan: p.requiresSellingPlan });
|
|
2188
|
-
plans.forEach((pl) => {
|
|
2189
|
-
var _a3;
|
|
2190
|
-
return report.sellingPlans.push({ product: p.handle, group: (_a3 = pl.group) == null ? void 0 : _a3.name, plan: pl.name, id: pl.id });
|
|
2191
|
-
});
|
|
2192
|
-
} else {
|
|
2193
|
-
report.products.push({ key: keys[i], error: (_a2 = r.reason) == null ? void 0 : _a2.message });
|
|
2194
|
-
report.problems.push({ level: "error", message: `Product "${keys[i]}": ${(_b2 = r.reason) == null ? void 0 : _b2.message}. Is it published to the Headless channel?` });
|
|
2195
|
-
}
|
|
2196
|
-
});
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
if (!report.apiVersion.ok || ((_g = report.apiVersion.monthsLeft) != null ? _g : 99) < 3) {
|
|
2200
|
-
report.problems.push({ level: report.apiVersion.ok ? "warn" : "error", message: `apiVersion ${cfg.apiVersion}: ${report.apiVersion.note}` });
|
|
2201
|
-
}
|
|
2202
|
-
if (typeof document !== "undefined") report.problems.push(...checkMarkup(root));
|
|
2203
|
-
if (!silent) print(report);
|
|
2204
|
-
return report;
|
|
2205
|
-
}
|
|
2206
|
-
function print(r) {
|
|
2207
|
-
var _a, _b, _c;
|
|
2208
|
-
const c = console;
|
|
2209
|
-
const errors = r.problems.filter((p) => p.level === "error").length;
|
|
2210
|
-
(_a = c.group) == null ? void 0 : _a.call(c, `[${cfg.prefix}] diagnose: ${errors ? `${errors} error(s)` : "no errors"}, ${r.problems.length - errors} warning(s)`);
|
|
2211
|
-
c.log("version", r.version, "| API", r.apiVersion.version, `(${r.apiVersion.note})`);
|
|
2212
|
-
c.log("config", r.config);
|
|
2213
|
-
if (r.market) c.log("market detected", r.market, "(in use:", r.config.country, r.config.language, ")");
|
|
2214
|
-
c.log(r.reachable ? `Storefront API reachable: ${(_b = r.shop) == null ? void 0 : _b.name} (${r.latencyMs} ms)` : "Storefront API NOT reachable");
|
|
2215
|
-
if (r.products.length) (c.table || c.log)(r.products);
|
|
2216
|
-
if (r.sellingPlans.length) (c.table || c.log)(r.sellingPlans);
|
|
2217
|
-
else if (r.reachable) c.log("No selling plans on the products of this page.");
|
|
2218
|
-
r.problems.forEach((p) => (p.level === "error" ? c.error : c.warn)(p.message, p.el || ""));
|
|
2219
|
-
(_c = c.groupEnd) == null ? void 0 : _c.call(c);
|
|
2220
|
-
}
|
|
2221
|
-
|
|
2222
|
-
// src/index.js
|
|
2223
|
-
var VERSION = "0.3.6";
|
|
2224
1834
|
var mountedProducts = /* @__PURE__ */ new WeakSet();
|
|
2225
1835
|
var mountedLists = /* @__PURE__ */ new WeakSet();
|
|
2226
1836
|
var liveProducts = /* @__PURE__ */ new Set();
|
|
@@ -2234,35 +1844,8 @@ ${cartFragment()}`, { id });
|
|
|
2234
1844
|
if (!l.root.isConnected) liveLists.delete(l);
|
|
2235
1845
|
});
|
|
2236
1846
|
}
|
|
2237
|
-
var initPromise = null;
|
|
2238
|
-
var observer = null;
|
|
2239
1847
|
function within(root, selector) {
|
|
2240
|
-
|
|
2241
|
-
return [...((_a = root.matches) == null ? void 0 : _a.call(root, selector)) ? [root] : [], ...qsa(root, selector)];
|
|
2242
|
-
}
|
|
2243
|
-
function mount(root = document) {
|
|
2244
|
-
if (!isConfigured()) return 0;
|
|
2245
|
-
let count = 0;
|
|
2246
|
-
pruneLive();
|
|
2247
|
-
within(root, listSelector()).forEach((el) => {
|
|
2248
|
-
if (mountedLists.has(el)) return;
|
|
2249
|
-
mountedLists.add(el);
|
|
2250
|
-
count++;
|
|
2251
|
-
const list = new ListScope(el);
|
|
2252
|
-
liveLists.add(list);
|
|
2253
|
-
el.addEventListener(`${cfg.prefix}:list-rendered`, (e) => mount(e.detail.container));
|
|
2254
|
-
list.load();
|
|
2255
|
-
});
|
|
2256
|
-
within(root, sel("product")).forEach((el) => {
|
|
2257
|
-
if (mountProduct(el)) count++;
|
|
2258
|
-
});
|
|
2259
|
-
renderCountrySelects(root);
|
|
2260
|
-
qsa(root, sel("account-link")).forEach((a) => {
|
|
2261
|
-
a.setAttribute("href", `https://${cfg.domain}/account`);
|
|
2262
|
-
});
|
|
2263
|
-
Cart.render();
|
|
2264
|
-
log(`mounted ${count} scope(s)`, root);
|
|
2265
|
-
return count;
|
|
1848
|
+
return [...root.matches?.(selector) ? [root] : [], ...qsa(root, selector)];
|
|
2266
1849
|
}
|
|
2267
1850
|
function mountProduct(el) {
|
|
2268
1851
|
if (mountedProducts.has(el)) return false;
|
|
@@ -2305,74 +1888,91 @@ ${cartFragment()}`, { id });
|
|
|
2305
1888
|
});
|
|
2306
1889
|
return true;
|
|
2307
1890
|
}
|
|
2308
|
-
function
|
|
2309
|
-
const
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
const current = (cfg.country || ((_a = loc.country) == null ? void 0 : _a.isoCode) || "").toUpperCase();
|
|
2315
|
-
const countries = [...loc.availableCountries || []].sort((a, b) => a.name.localeCompare(b.name, cfg.locale || void 0));
|
|
2316
|
-
const info = countries.find((c) => c.isoCode === current) || loc.country;
|
|
2317
|
-
const fill = (tpl, c) => {
|
|
2318
|
-
var _a2, _b;
|
|
2319
|
-
return tpl.replace(/\{name\}/g, c.name).replace(/\{code\}/g, c.isoCode).replace(/\{currency\}/g, ((_a2 = c.currency) == null ? void 0 : _a2.isoCode) || "").replace(/\{symbol\}/g, ((_b = c.currency) == null ? void 0 : _b.symbol) || "");
|
|
2320
|
-
};
|
|
2321
|
-
selects.forEach((s) => {
|
|
2322
|
-
const tpl = s.getAttribute(attr("country-label")) || "{name} ({currency})";
|
|
2323
|
-
s.innerHTML = "";
|
|
2324
|
-
countries.forEach((c) => s.appendChild(new Option(fill(tpl, c), c.isoCode)));
|
|
2325
|
-
s.value = current;
|
|
2326
|
-
if (s.value !== current) s.selectedIndex = -1;
|
|
2327
|
-
s.disabled = countries.length < 2 && !s.hasAttribute(attr("always-enabled"));
|
|
2328
|
-
});
|
|
2329
|
-
binds.forEach((el) => {
|
|
2330
|
-
var _a2, _b;
|
|
2331
|
-
const k = get(el, "country-bind") || "name";
|
|
2332
|
-
const v = { name: info == null ? void 0 : info.name, code: info == null ? void 0 : info.isoCode, currency: (_a2 = info == null ? void 0 : info.currency) == null ? void 0 : _a2.isoCode, symbol: (_b = info == null ? void 0 : info.currency) == null ? void 0 : _b.symbol }[k];
|
|
2333
|
-
el.textContent = v || "";
|
|
2334
|
-
});
|
|
2335
|
-
}).catch((err) => reportError(err, "country select"));
|
|
2336
|
-
}
|
|
2337
|
-
async function setCountry(code, { language, remember = true } = {}) {
|
|
2338
|
-
const country = toCountry(code);
|
|
2339
|
-
if (!country) throw new TetherError(`Not a country code: ${code}`, { code: "INVALID_COUNTRY" });
|
|
2340
|
-
const lang = language === void 0 ? cfg.language : toLanguage(language);
|
|
2341
|
-
const changed = country !== String(cfg.country || "").toUpperCase() || lang !== cfg.language;
|
|
2342
|
-
cfg.country = country;
|
|
2343
|
-
cfg.language = lang || void 0;
|
|
2344
|
-
if (remember) rememberMarket({ country, language: lang });
|
|
2345
|
-
if (!changed) {
|
|
2346
|
-
renderCountrySelects();
|
|
2347
|
-
return Cart.state;
|
|
2348
|
-
}
|
|
2349
|
-
clearProductCache();
|
|
2350
|
-
const jobs = [];
|
|
2351
|
-
if (language !== void 0) clearLocalization();
|
|
2352
|
-
if (Cart.id()) jobs.push(Cart.setBuyerIdentity({ countryCode: country }, { create: false }).catch((err) => reportError(err, "cart country")));
|
|
1891
|
+
function applyPageTitle(el, p) {
|
|
1892
|
+
const tpl = get(el, "page-title");
|
|
1893
|
+
if (!tpl || typeof document === "undefined") return;
|
|
1894
|
+
document.title = tpl.replace(/\{title\}/g, p.title).replace(/\{vendor\}/g, p.vendor || "").replace(/\{type\}/g, p.productType || "");
|
|
1895
|
+
}
|
|
1896
|
+
function retryFailed() {
|
|
2353
1897
|
pruneLive();
|
|
2354
|
-
liveLists.forEach((list) => jobs.push(list.load()));
|
|
2355
1898
|
liveProducts.forEach((el) => {
|
|
2356
|
-
if (el.
|
|
1899
|
+
if (el.getAttribute(attr("state")) !== "error" || el.getAttribute(attr("error")) === "not-found") return;
|
|
2357
1900
|
mountedProducts.delete(el);
|
|
2358
1901
|
mountProduct(el);
|
|
2359
1902
|
});
|
|
2360
|
-
renderCountrySelects();
|
|
2361
|
-
emit("country-changed", { country, language: cfg.language });
|
|
2362
|
-
await Promise.all(jobs);
|
|
2363
|
-
return Cart.state;
|
|
2364
1903
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
1904
|
+
|
|
1905
|
+
// src/lazy-iife.js
|
|
1906
|
+
var { cfg: cfg2 } = config_exports;
|
|
1907
|
+
var coreModules = {
|
|
1908
|
+
"config.js": config_exports,
|
|
1909
|
+
"client.js": client_exports,
|
|
1910
|
+
"dom.js": dom_exports,
|
|
1911
|
+
"fragments.js": fragments_exports,
|
|
1912
|
+
"loader.js": loader_exports,
|
|
1913
|
+
"selling-plans.js": selling_plans_exports,
|
|
1914
|
+
"status.js": status_exports,
|
|
1915
|
+
"cart.js": cart_exports,
|
|
1916
|
+
"product.js": product_exports,
|
|
1917
|
+
"collection.js": collection_exports,
|
|
1918
|
+
"mounting.js": mounting_exports
|
|
1919
|
+
};
|
|
1920
|
+
var self = typeof document !== "undefined" ? document.currentScript?.src || "" : "";
|
|
1921
|
+
var parts = {};
|
|
1922
|
+
function loadPart(name) {
|
|
1923
|
+
if (parts[name]) return parts[name];
|
|
1924
|
+
const base = cfg2.partsUrl || self.replace(/[^/]*$/, "");
|
|
1925
|
+
const min = /\.min\.js(\?|$)/.test(self) || !self ? ".min" : "";
|
|
1926
|
+
const url = `${base.replace(/\/?$/, "/")}tether.${name}${min}.js`;
|
|
1927
|
+
parts[name] = new Promise((resolve, reject) => {
|
|
1928
|
+
const s = document.createElement("script");
|
|
1929
|
+
s.src = url;
|
|
1930
|
+
s.async = true;
|
|
1931
|
+
s.onload = () => window.Tether?.__parts?.[name] ? resolve(window.Tether.__parts[name]) : reject(new Error(`${url} did not register`));
|
|
1932
|
+
s.onerror = () => {
|
|
1933
|
+
delete parts[name];
|
|
1934
|
+
reject(new Error(`Could not load ${url}. Host it next to the Tether script, or set partsUrl.`));
|
|
1935
|
+
};
|
|
1936
|
+
document.head.appendChild(s);
|
|
1937
|
+
});
|
|
1938
|
+
return parts[name];
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// src/index.js
|
|
1942
|
+
var VERSION = "0.4.0";
|
|
1943
|
+
var initPromise = null;
|
|
1944
|
+
var observer = null;
|
|
1945
|
+
function mount(root = document) {
|
|
1946
|
+
if (!isConfigured()) return 0;
|
|
1947
|
+
let count = 0;
|
|
1948
|
+
pruneLive();
|
|
1949
|
+
within(root, listSelector()).forEach((el) => {
|
|
1950
|
+
if (mountedLists.has(el)) return;
|
|
1951
|
+
mountedLists.add(el);
|
|
1952
|
+
count++;
|
|
1953
|
+
const list = new ListScope(el);
|
|
1954
|
+
liveLists.add(list);
|
|
1955
|
+
el.addEventListener(`${cfg.prefix}:list-rendered`, (e) => mount(e.detail.container));
|
|
1956
|
+
list.load();
|
|
1957
|
+
});
|
|
1958
|
+
within(root, sel("product")).forEach((el) => {
|
|
1959
|
+
if (mountProduct(el)) count++;
|
|
1960
|
+
});
|
|
1961
|
+
if (within(root, `${sel("country-select")},${sel("country-bind")}`).length) {
|
|
1962
|
+
loadPart("markets").then((m) => m.renderCountrySelects(root)).catch((err) => reportError(err, "markets"));
|
|
1963
|
+
}
|
|
1964
|
+
qsa(root, sel("account-link")).forEach((a) => {
|
|
1965
|
+
a.setAttribute("href", `https://${cfg.domain}/account`);
|
|
1966
|
+
});
|
|
1967
|
+
Cart.render();
|
|
1968
|
+
log(`mounted ${count} scope(s)`, root);
|
|
1969
|
+
return count;
|
|
2369
1970
|
}
|
|
2370
1971
|
function unmount() {
|
|
2371
1972
|
Cart.close();
|
|
2372
1973
|
}
|
|
2373
1974
|
function getScope(el) {
|
|
2374
|
-
|
|
2375
|
-
const root = (_a = el == null ? void 0 : el.closest) == null ? void 0 : _a.call(el, sel("product"));
|
|
1975
|
+
const root = el?.closest?.(sel("product"));
|
|
2376
1976
|
return root ? scopes.get(root) || null : null;
|
|
2377
1977
|
}
|
|
2378
1978
|
var controlsBound = false;
|
|
@@ -2380,9 +1980,8 @@ ${cartFragment()}`, { id });
|
|
|
2380
1980
|
if (controlsBound) return;
|
|
2381
1981
|
controlsBound = true;
|
|
2382
1982
|
document.addEventListener("click", (e) => {
|
|
2383
|
-
var _a;
|
|
2384
1983
|
const t = e.target;
|
|
2385
|
-
if (!
|
|
1984
|
+
if (!t?.closest) return;
|
|
2386
1985
|
if (t.closest(sel("cart-open"))) {
|
|
2387
1986
|
e.preventDefault();
|
|
2388
1987
|
Cart.toggle();
|
|
@@ -2420,7 +2019,7 @@ ${cartFragment()}`, { id });
|
|
|
2420
2019
|
const removeCode = t.closest(sel("discount-remove"));
|
|
2421
2020
|
if (removeCode) {
|
|
2422
2021
|
e.preventDefault();
|
|
2423
|
-
const code =
|
|
2022
|
+
const code = removeCode.closest(sel("discount-code"))?.getAttribute(attr("discount-code"));
|
|
2424
2023
|
if (code) Cart.removeDiscount(code).catch((err) => reportError(err, "discount"));
|
|
2425
2024
|
return;
|
|
2426
2025
|
}
|
|
@@ -2432,9 +2031,9 @@ ${cartFragment()}`, { id });
|
|
|
2432
2031
|
});
|
|
2433
2032
|
document.addEventListener("change", (e) => {
|
|
2434
2033
|
const t = e.target;
|
|
2435
|
-
if (!
|
|
2034
|
+
if (!t?.closest) return;
|
|
2436
2035
|
const lineEl = t.closest(dataKeySel());
|
|
2437
|
-
const id = lineEl
|
|
2036
|
+
const id = lineEl?.getAttribute(`data-${cfg.prefix}-key`);
|
|
2438
2037
|
if (id && t.matches(`input${sel("line", "quantity")}`)) {
|
|
2439
2038
|
Cart.updateLine(id, parseInt(t.value, 10) || 0).catch((err) => reportError(err, "cart line"));
|
|
2440
2039
|
} else if (id && t.matches(`select${sel("line-plan")}`)) {
|
|
@@ -2446,18 +2045,16 @@ ${cartFragment()}`, { id });
|
|
|
2446
2045
|
}
|
|
2447
2046
|
});
|
|
2448
2047
|
document.addEventListener("submit", (e) => {
|
|
2449
|
-
|
|
2450
|
-
const form = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, sel("discount-form"));
|
|
2048
|
+
const form = e.target.closest?.(sel("discount-form"));
|
|
2451
2049
|
if (!form) return;
|
|
2452
2050
|
e.preventDefault();
|
|
2453
2051
|
const input = form.querySelector(sel("discount-input")) || form.querySelector("input");
|
|
2454
|
-
const code = input
|
|
2052
|
+
const code = input?.value.trim();
|
|
2455
2053
|
if (!code) return;
|
|
2456
2054
|
busy(form, async () => {
|
|
2457
|
-
var _a2;
|
|
2458
2055
|
try {
|
|
2459
2056
|
const cart = await Cart.applyDiscount(code);
|
|
2460
|
-
const ok = (
|
|
2057
|
+
const ok = (cart?.discountCodes || []).find((d) => d.code.toLowerCase() === code.toLowerCase())?.applicable;
|
|
2461
2058
|
form.classList.toggle("is-invalid", !ok);
|
|
2462
2059
|
emit(ok ? "discount-applied" : "discount-invalid", { code }, form);
|
|
2463
2060
|
if (ok && input) input.value = "";
|
|
@@ -2467,8 +2064,28 @@ ${cartFragment()}`, { id });
|
|
|
2467
2064
|
}
|
|
2468
2065
|
});
|
|
2469
2066
|
});
|
|
2067
|
+
window.addEventListener("online", () => {
|
|
2068
|
+
retryFailed();
|
|
2069
|
+
liveLists.forEach((l) => {
|
|
2070
|
+
if (l.root.getAttribute(attr("state")) === "error") l.load();
|
|
2071
|
+
});
|
|
2072
|
+
});
|
|
2470
2073
|
document.addEventListener("keydown", (e) => {
|
|
2471
|
-
if (
|
|
2074
|
+
if (!Cart.isOpen()) return;
|
|
2075
|
+
if (e.key === "Escape") {
|
|
2076
|
+
Cart.close();
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
if (e.key !== "Tab" || cfg.cartFocus === false) return;
|
|
2080
|
+
const d = Cart.drawers().find((x) => x.classList.contains(cfg.cartDrawerOpenClass));
|
|
2081
|
+
const f = d ? focusables(d) : [];
|
|
2082
|
+
if (!f.length) return;
|
|
2083
|
+
const first = f[0];
|
|
2084
|
+
const last = f[f.length - 1];
|
|
2085
|
+
if (!d.contains(document.activeElement) || e.shiftKey && document.activeElement === first || !e.shiftKey && document.activeElement === last) {
|
|
2086
|
+
e.preventDefault();
|
|
2087
|
+
(e.shiftKey ? last : first).focus();
|
|
2088
|
+
}
|
|
2472
2089
|
});
|
|
2473
2090
|
}
|
|
2474
2091
|
function dataKeySel() {
|
|
@@ -2480,10 +2097,7 @@ ${cartFragment()}`, { id });
|
|
|
2480
2097
|
const name = get(group, "group");
|
|
2481
2098
|
const extra = toAttributes(parseJSONAttr(group, "line-attributes"));
|
|
2482
2099
|
const groupAttrs = name ? [{ key: "_group", value: name }, ...extra] : extra;
|
|
2483
|
-
const lines = qsa(group, sel("product")).map((el) => scopes.get(el)).filter((s) => {
|
|
2484
|
-
var _a;
|
|
2485
|
-
return s && ((_a = s.variant) == null ? void 0 : _a.availableForSale) && s.quantity > 0;
|
|
2486
|
-
}).map((s) => {
|
|
2100
|
+
const lines = qsa(group, sel("product")).map((el) => scopes.get(el)).filter((s) => s && s.variant?.availableForSale && s.quantity > 0).map((s) => {
|
|
2487
2101
|
const l = s.getLine();
|
|
2488
2102
|
l.attributes = [...l.attributes || [], ...groupAttrs];
|
|
2489
2103
|
return l;
|
|
@@ -2502,7 +2116,6 @@ ${cartFragment()}`, { id });
|
|
|
2502
2116
|
}
|
|
2503
2117
|
}
|
|
2504
2118
|
function init(overrides = {}) {
|
|
2505
|
-
var _a;
|
|
2506
2119
|
if (initPromise && !Object.keys(overrides).length) return initPromise;
|
|
2507
2120
|
configure({ ...window.TETHER_CONFIG || {}, ...overrides });
|
|
2508
2121
|
if (!isConfigured()) {
|
|
@@ -2510,17 +2123,11 @@ ${cartFragment()}`, { id });
|
|
|
2510
2123
|
initPromise = Promise.resolve(null);
|
|
2511
2124
|
return initPromise;
|
|
2512
2125
|
}
|
|
2513
|
-
const market = detectMarket(cfg.markets);
|
|
2514
|
-
if (market.country) cfg.country = market.country;
|
|
2515
|
-
if (market.language) cfg.language = market.language;
|
|
2516
|
-
if (market.locale && !overrides.locale && !((_a = window.TETHER_CONFIG) == null ? void 0 : _a.locale)) cfg.locale = market.locale;
|
|
2517
|
-
if (market.sources && Object.keys(market.sources).length) log("market", market);
|
|
2518
2126
|
bindControls();
|
|
2519
2127
|
if (cfg.observe) startObserver();
|
|
2520
|
-
initPromise = Cart.init().then(async (cart) => {
|
|
2521
|
-
var _a2;
|
|
2128
|
+
initPromise = applyMarket(overrides).then(() => Cart.init()).then(async (cart) => {
|
|
2522
2129
|
const want = cfg.country ? String(cfg.country).toUpperCase() : null;
|
|
2523
|
-
if (cart && want &&
|
|
2130
|
+
if (cart && want && cart.buyerIdentity?.countryCode && cart.buyerIdentity.countryCode !== want) {
|
|
2524
2131
|
try {
|
|
2525
2132
|
cart = await Cart.setBuyerIdentity({ countryCode: want }, { create: false });
|
|
2526
2133
|
} catch (err) {
|
|
@@ -2532,6 +2139,33 @@ ${cartFragment()}`, { id });
|
|
|
2532
2139
|
});
|
|
2533
2140
|
return initPromise;
|
|
2534
2141
|
}
|
|
2142
|
+
async function applyMarket(overrides) {
|
|
2143
|
+
const m = cfg.markets || {};
|
|
2144
|
+
if (m.detect === false) return;
|
|
2145
|
+
let market = null;
|
|
2146
|
+
if (m.detect) {
|
|
2147
|
+
try {
|
|
2148
|
+
market = (await loadPart("markets")).detectMarket(m);
|
|
2149
|
+
} catch (err) {
|
|
2150
|
+
reportError(err, "markets");
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
} else if (m.remember !== false) {
|
|
2154
|
+
try {
|
|
2155
|
+
market = JSON.parse(localStorage.getItem(`${cfg.prefix}:market:${cfg.domain}`) || "null");
|
|
2156
|
+
} catch {
|
|
2157
|
+
}
|
|
2158
|
+
if (market && !/^[A-Z]{2}$/.test(market.country || "")) market = null;
|
|
2159
|
+
}
|
|
2160
|
+
if (!market) return;
|
|
2161
|
+
if (market.country) cfg.country = market.country;
|
|
2162
|
+
if (market.language) cfg.language = market.language;
|
|
2163
|
+
if (market.locale && !overrides.locale && !window.TETHER_CONFIG?.locale) cfg.locale = market.locale;
|
|
2164
|
+
log("market", market);
|
|
2165
|
+
}
|
|
2166
|
+
function setCountry(code, opts) {
|
|
2167
|
+
return loadPart("markets").then((m) => m.setCountry(code, opts));
|
|
2168
|
+
}
|
|
2535
2169
|
function startObserver() {
|
|
2536
2170
|
if (observer || typeof MutationObserver === "undefined") return;
|
|
2537
2171
|
let t = null;
|
|
@@ -2577,9 +2211,11 @@ ${cartFragment()}`, { id });
|
|
|
2577
2211
|
TetherError,
|
|
2578
2212
|
resolveProductKey,
|
|
2579
2213
|
setCountry,
|
|
2580
|
-
detectMarket: (markets) => detectMarket(markets || cfg.markets),
|
|
2581
|
-
getLocalization,
|
|
2582
|
-
diagnose: (opts) => diagnose({ ...opts, version: VERSION }),
|
|
2214
|
+
detectMarket: (markets) => loadPart("markets").then((m) => m.detectMarket(markets || cfg.markets)),
|
|
2215
|
+
getLocalization: () => loadPart("markets").then((m) => m.getLocalization()),
|
|
2216
|
+
diagnose: (opts) => loadPart("diagnose").then((m) => m.diagnose({ ...opts, version: VERSION })),
|
|
2217
|
+
__core: coreModules,
|
|
2218
|
+
__parts: {},
|
|
2583
2219
|
on(name, fn) {
|
|
2584
2220
|
const h = (e) => fn(e.detail, e);
|
|
2585
2221
|
document.addEventListener(`${cfg.prefix}:${name}`, h);
|