@occluze/tether 0.5.0 → 0.6.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 -7
- package/dist/esm/astro.js +1 -1
- package/dist/esm/chunks/account-WAWNASZU.js +1 -0
- package/dist/esm/chunks/analytics-22NMIVBV.js +1 -0
- package/dist/esm/chunks/chunk-4UR45XTC.js +1 -0
- package/dist/esm/chunks/chunk-6R752OYZ.js +1 -0
- package/dist/esm/chunks/chunk-BGIKQY6H.js +1 -0
- package/dist/esm/chunks/chunk-CISGFYOF.js +1 -0
- package/dist/esm/chunks/chunk-KARKXC6G.js +1 -0
- package/dist/esm/chunks/chunk-YXCXIPQV.js +4 -0
- package/dist/esm/chunks/{chunk-RBVRRQIU.js → chunk-YZMBXS6Y.js} +1 -1
- package/dist/esm/chunks/chunk-ZVUPVSLF.js +1 -0
- package/dist/esm/chunks/diagnose-6ZSK5DGE.js +1 -0
- package/dist/esm/chunks/extras-QDCSW6VU.js +1 -0
- package/dist/esm/chunks/markets-ui-HJCVLCKJ.js +1 -0
- package/dist/esm/chunks/search-LN2H6SXS.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +1 -1
- package/dist/tether.account.js +374 -0
- package/dist/tether.account.min.js +1 -0
- package/dist/tether.analytics.js +259 -0
- package/dist/tether.analytics.min.js +1 -0
- package/dist/tether.diagnose.js +11 -2
- package/dist/tether.diagnose.min.js +1 -1
- package/dist/tether.extras.js +87 -0
- package/dist/tether.extras.min.js +1 -0
- package/dist/tether.js +268 -75
- package/dist/tether.markets.js +3 -3
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +3 -3
- package/dist/tether.min.js.map +3 -3
- package/dist/tether.search.js +176 -0
- package/dist/tether.search.min.js +1 -0
- package/package.json +5 -2
- package/src/account.js +313 -0
- package/src/analytics.js +232 -0
- package/src/astro.js +4 -1
- package/src/cart.js +74 -11
- package/src/client.js +8 -3
- package/src/collection.js +15 -10
- package/src/config.js +3 -0
- package/src/diagnose.js +11 -2
- package/src/dom.js +7 -1
- package/src/extras.js +63 -0
- package/src/fragments.js +1 -1
- package/src/index.js +103 -41
- package/src/initial.js +13 -4
- package/src/lazy.js +9 -1
- package/src/loader.js +4 -1
- package/src/markets-ui.js +2 -1
- package/src/mounting.js +27 -2
- package/src/parts/account.js +3 -0
- package/src/parts/analytics.js +3 -0
- package/src/parts/extras.js +3 -0
- package/src/parts/search.js +3 -0
- package/src/product.js +19 -5
- package/src/search.js +153 -0
- package/src/server.js +78 -6
- package/src/status.js +2 -2
- package/types/index.d.ts +56 -3
- package/types/server.d.ts +10 -2
- package/dist/esm/chunks/chunk-HHMOPDZG.js +0 -1
- package/dist/esm/chunks/chunk-HXKV6QGB.js +0 -4
- package/dist/esm/chunks/chunk-RNZMTQTX.js +0 -1
- package/dist/esm/chunks/diagnose-J667GHGS.js +0 -1
- package/dist/esm/chunks/markets-ui-EGWIEUOZ.js +0 -1
package/dist/tether.js
CHANGED
|
@@ -60,6 +60,12 @@
|
|
|
60
60
|
// Subscriptions
|
|
61
61
|
defaultPurchase: "one-time",
|
|
62
62
|
// 'one-time' | 'subscription'
|
|
63
|
+
variantUrl: false,
|
|
64
|
+
// true: ?variant= follows the visitor's choice on the main product (shareable links)
|
|
65
|
+
maxAge: 60,
|
|
66
|
+
// seconds a Shopify answer may be cached (0 = Shopify's own caching, can be hours)
|
|
67
|
+
lowStock: 5,
|
|
68
|
+
// tether-show="low-stock" at this many or fewer (needs inventory access)
|
|
63
69
|
// Mounting
|
|
64
70
|
autoMount: true,
|
|
65
71
|
// mount document on DOMContentLoaded
|
|
@@ -152,7 +158,10 @@
|
|
|
152
158
|
if (!conf.domain || !conf.token) {
|
|
153
159
|
throw new TetherError("Tether is not configured: domain and token are required", { code: "NOT_CONFIGURED" });
|
|
154
160
|
}
|
|
155
|
-
|
|
161
|
+
let text = context ? withContext(query, conf) : query;
|
|
162
|
+
const age = conf.maxAge ?? 60;
|
|
163
|
+
if (age > 0 && !/^\s*mutation\b/.test(text)) text += ` #${Math.floor(Date.now() / (age * 1e3))}`;
|
|
164
|
+
const body = JSON.stringify({ query: text, variables });
|
|
156
165
|
let attempt = 0;
|
|
157
166
|
for (; ; ) {
|
|
158
167
|
let res;
|
|
@@ -191,8 +200,8 @@
|
|
|
191
200
|
log("no inventory permission, retrying without quantityAvailable");
|
|
192
201
|
return request(conf, query.replace(/\bquantityAvailable\b/g, ""), variables, { context });
|
|
193
202
|
}
|
|
194
|
-
if (json.errors.some((e) => /'warnings'/.test(e.message || "")) && /warnings \{ code message \}/.test(query)) {
|
|
195
|
-
return request(conf, query.replace(/ warnings \{ code message \}/g, ""), variables, { context });
|
|
203
|
+
if (json.errors.some((e) => /'warnings'/.test(e.message || "")) && /warnings \{ code message target \}/.test(query)) {
|
|
204
|
+
return request(conf, query.replace(/ warnings \{ code message target \}/g, ""), variables, { context });
|
|
196
205
|
}
|
|
197
206
|
const throttled = json.errors.some((e) => e.extensions?.code === "THROTTLED");
|
|
198
207
|
if (throttled && attempt++ < conf.retries) {
|
|
@@ -259,7 +268,7 @@
|
|
|
259
268
|
return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${o.imagesFirst}){nodes{${IMAGE}}}options{name optionValues{name swatch{color image{previewImage{${IMAGE}}}}}}priceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}compareAtPriceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}${metafieldsField(o)} sellingPlanGroups(first:${o.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${o.sellingPlansFirst}){nodes{${SELLING_PLAN_FIELDS}}}}}variants(first:${o.variantsFirst}){nodes{id title availableForSale currentlyNotInStock quantityAvailable sku barcode selectedOptions{name value}image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}unitPrice{${MONEY}}sellingPlanAllocations(first:${o.sellingPlansFirst}){nodes{${ALLOCATION_FIELDS}}}}}}`;
|
|
260
269
|
}
|
|
261
270
|
function cartFragment() {
|
|
262
|
-
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}}}}}}}}`;
|
|
271
|
+
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 quantityAvailable 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}}}}}}}}`;
|
|
263
272
|
}
|
|
264
273
|
function collectionFields() {
|
|
265
274
|
return `
|
|
@@ -362,7 +371,10 @@
|
|
|
362
371
|
}
|
|
363
372
|
r.resolve(p);
|
|
364
373
|
} else {
|
|
365
|
-
|
|
374
|
+
const miss = productCache.get(k);
|
|
375
|
+
if (gen === generation) setTimeout(() => {
|
|
376
|
+
if (productCache.get(k) === miss) productCache.delete(k);
|
|
377
|
+
}, 1e4);
|
|
366
378
|
r.reject(new TetherError(`Product not found: ${k}`, { code: "NOT_FOUND" }));
|
|
367
379
|
}
|
|
368
380
|
}));
|
|
@@ -536,6 +548,7 @@
|
|
|
536
548
|
emit: () => emit,
|
|
537
549
|
formatDate: () => formatDate,
|
|
538
550
|
formatMoney: () => formatMoney,
|
|
551
|
+
formatPercent: () => formatPercent,
|
|
539
552
|
get: () => get,
|
|
540
553
|
has: () => has,
|
|
541
554
|
hasTemplate: () => hasTemplate,
|
|
@@ -574,6 +587,14 @@
|
|
|
574
587
|
function emit(name, detail, target = document) {
|
|
575
588
|
target.dispatchEvent(new CustomEvent(`${cfg.prefix}:${name}`, { detail, bubbles: true }));
|
|
576
589
|
}
|
|
590
|
+
function formatPercent(n, locale = cfg.locale) {
|
|
591
|
+
if (!n) return "";
|
|
592
|
+
try {
|
|
593
|
+
return new Intl.NumberFormat(locale || void 0, { style: "percent", maximumFractionDigits: 0 }).format(n / 100);
|
|
594
|
+
} catch {
|
|
595
|
+
return `${n}%`;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
577
598
|
function formatMoney(money) {
|
|
578
599
|
if (!money) return "";
|
|
579
600
|
if (typeof cfg.formatMoney === "function") return cfg.formatMoney(money, cfg.locale);
|
|
@@ -690,7 +711,7 @@
|
|
|
690
711
|
function repeat(container, items, render) {
|
|
691
712
|
const tpl = template(container);
|
|
692
713
|
if (!tpl) {
|
|
693
|
-
warn("repeater without a template child", container);
|
|
714
|
+
if (items.length) warn("repeater without a template child", container);
|
|
694
715
|
return;
|
|
695
716
|
}
|
|
696
717
|
container.innerHTML = "";
|
|
@@ -814,7 +835,7 @@
|
|
|
814
835
|
return line;
|
|
815
836
|
}
|
|
816
837
|
function mutation(name, args, inner) {
|
|
817
|
-
return `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}warnings{code message}}}${cartFragment()}`;
|
|
838
|
+
return `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}warnings{code message target}}}${cartFragment()}`;
|
|
818
839
|
}
|
|
819
840
|
var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
820
841
|
var returnFocus = null;
|
|
@@ -825,7 +846,10 @@
|
|
|
825
846
|
if (!d.hasAttribute("role")) d.setAttribute("role", "dialog");
|
|
826
847
|
if (!d.hasAttribute("aria-label") && !d.hasAttribute("aria-labelledby")) d.setAttribute("aria-label", get(d, "cart-label") || "Cart");
|
|
827
848
|
d.setAttribute("aria-modal", String(open));
|
|
828
|
-
if (!d.classList.contains(cfg.cartDrawerOpenClass))
|
|
849
|
+
if (!d.classList.contains(cfg.cartDrawerOpenClass)) {
|
|
850
|
+
d.setAttribute("aria-hidden", "true");
|
|
851
|
+
if (cfg.cartFocus !== false) d.inert = true;
|
|
852
|
+
}
|
|
829
853
|
});
|
|
830
854
|
}
|
|
831
855
|
var isMissingCart = (err) => /does not exist|not found|invalid id/i.test(err?.message || "");
|
|
@@ -853,13 +877,17 @@
|
|
|
853
877
|
return this.state?.totalQuantity || 0;
|
|
854
878
|
},
|
|
855
879
|
lastError: null,
|
|
880
|
+
/** Set by the account part while a customer is logged in (Customer Account API token). */
|
|
881
|
+
customerAccessToken: null,
|
|
882
|
+
/** line id -> { qty, done }: what quick +/- clicks asked for, shown until Shopify has answered. */
|
|
883
|
+
pendingQty: /* @__PURE__ */ new Map(),
|
|
856
884
|
/** Shopify's notes on the last change, e.g. less stock than asked: [{ code, message }]. */
|
|
857
885
|
warnings: [],
|
|
858
886
|
/** true once the saved cart (or the absence of one) is known. */
|
|
859
887
|
loaded: false,
|
|
860
888
|
_take(payload, name) {
|
|
861
889
|
const p = unwrap(payload, name);
|
|
862
|
-
this.warnings = p.warnings || [];
|
|
890
|
+
this.warnings = (p.warnings || []).filter((w, i, all) => all.findIndex((x) => x.message === w.message) === i).map((w) => ({ ...w, message: typeof cfg.warningText === "function" && cfg.warningText(w) || w.message }));
|
|
863
891
|
if (this.warnings.length) emit("cart-warning", { warnings: this.warnings });
|
|
864
892
|
return p.cart;
|
|
865
893
|
},
|
|
@@ -883,6 +911,10 @@
|
|
|
883
911
|
_commit(cart, reason) {
|
|
884
912
|
this.state = cart;
|
|
885
913
|
if (cart?.id) this._setId(cart.id);
|
|
914
|
+
try {
|
|
915
|
+
localStorage.setItem(`${cfg.storageKey}:seen`, `${Date.now()}|${cart?.totalQuantity || 0}`);
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
886
918
|
this.render();
|
|
887
919
|
emit("cart-updated", { cart, reason });
|
|
888
920
|
return cart;
|
|
@@ -896,6 +928,15 @@
|
|
|
896
928
|
emit("cart-updated", { cart: null, reason: "load" });
|
|
897
929
|
return null;
|
|
898
930
|
}
|
|
931
|
+
if (!this.loaded) {
|
|
932
|
+
try {
|
|
933
|
+
const n = (localStorage.getItem(`${cfg.storageKey}:seen`) || "").split("|")[1];
|
|
934
|
+
if (n) qsa(document, sel("cart-count")).forEach((el) => {
|
|
935
|
+
if (/^\s*0?\s*$/.test(el.textContent)) el.textContent = n;
|
|
936
|
+
});
|
|
937
|
+
} catch {
|
|
938
|
+
}
|
|
939
|
+
}
|
|
899
940
|
try {
|
|
900
941
|
const data = await gql(`query TetherCart($id:ID!){cart(id:$id){...TetherCart}}${cartFragment()}`, { id });
|
|
901
942
|
if (!data.cart) {
|
|
@@ -914,7 +955,10 @@
|
|
|
914
955
|
return this.state;
|
|
915
956
|
},
|
|
916
957
|
async _create(input = {}) {
|
|
917
|
-
const buyerIdentity = cfg.country
|
|
958
|
+
const buyerIdentity = cfg.country || this.customerAccessToken ? {
|
|
959
|
+
...cfg.country ? { countryCode: String(cfg.country).toUpperCase() } : {},
|
|
960
|
+
...this.customerAccessToken ? { customerAccessToken: this.customerAccessToken } : {}
|
|
961
|
+
} : void 0;
|
|
918
962
|
const data = await gql(
|
|
919
963
|
mutation("cartCreate", "$input: CartInput!", "input: $input"),
|
|
920
964
|
{ input: { buyerIdentity, ...input } }
|
|
@@ -970,6 +1014,30 @@
|
|
|
970
1014
|
"update"
|
|
971
1015
|
));
|
|
972
1016
|
},
|
|
1017
|
+
/**
|
|
1018
|
+
* +1 / -1 for a line. Quick clicks add up: each builds on the quantity the last click asked for,
|
|
1019
|
+
* which shows at once (tether-line="quantity", Cart.lines) until Shopify has answered.
|
|
1020
|
+
*/
|
|
1021
|
+
changeQuantity(lineId, delta) {
|
|
1022
|
+
const current = this.lines.find((l) => l.id === lineId);
|
|
1023
|
+
if (!current) return Promise.resolve(this.state);
|
|
1024
|
+
const target = Math.max(0, (this.pendingQty.get(lineId)?.qty ?? current.quantity) + delta);
|
|
1025
|
+
const job = { qty: target, done: false };
|
|
1026
|
+
this.pendingQty.set(lineId, job);
|
|
1027
|
+
this.render();
|
|
1028
|
+
emit("cart-updated", { cart: this.state, reason: "pending" });
|
|
1029
|
+
return this.updateLine(lineId, target).finally(() => {
|
|
1030
|
+
if (this.pendingQty.get(lineId) === job) {
|
|
1031
|
+
this.pendingQty.delete(lineId);
|
|
1032
|
+
this.render();
|
|
1033
|
+
emit("cart-updated", { cart: this.state, reason: "settled" });
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
},
|
|
1037
|
+
/** Quantity to show for a line: the one asked for while an update is on its way. */
|
|
1038
|
+
quantityOf(line) {
|
|
1039
|
+
return this.pendingQty.get(line.id)?.qty ?? line.quantity;
|
|
1040
|
+
},
|
|
973
1041
|
removeLine(lineIds) {
|
|
974
1042
|
const ids = Array.isArray(lineIds) ? lineIds : [lineIds];
|
|
975
1043
|
return this._queue(async () => this._commit(
|
|
@@ -1006,9 +1074,18 @@
|
|
|
1006
1074
|
"discount"
|
|
1007
1075
|
));
|
|
1008
1076
|
},
|
|
1009
|
-
|
|
1077
|
+
/**
|
|
1078
|
+
* Adds a code. A code Shopify doesn't accept isn't kept in the cart (it would warn on every change):
|
|
1079
|
+
* it's removed again and the promise rejects with code INVALID_DISCOUNT.
|
|
1080
|
+
*/
|
|
1081
|
+
async applyDiscount(code) {
|
|
1010
1082
|
const current = (this.state?.discountCodes || []).map((d) => d.code);
|
|
1011
|
-
|
|
1083
|
+
const cart = await this.setDiscountCodes([.../* @__PURE__ */ new Set([...current, code])]);
|
|
1084
|
+
const hit = (cart?.discountCodes || []).find((d) => d.code.toLowerCase() === String(code).toLowerCase());
|
|
1085
|
+
if (hit?.applicable) return cart;
|
|
1086
|
+
await this.removeDiscount(code).catch(() => {
|
|
1087
|
+
});
|
|
1088
|
+
throw new TetherError(`Discount code not valid: ${code}`, { code: "INVALID_DISCOUNT" });
|
|
1012
1089
|
},
|
|
1013
1090
|
removeDiscount(code) {
|
|
1014
1091
|
const k = String(code).toLowerCase();
|
|
@@ -1042,7 +1119,8 @@
|
|
|
1042
1119
|
}
|
|
1043
1120
|
try {
|
|
1044
1121
|
const data = await gql("query TetherCheckout($id: ID!) { cart(id: $id) { checkoutUrl } }", { id });
|
|
1045
|
-
|
|
1122
|
+
let url = this.beforeCheckout && await this.beforeCheckout().catch(() => null) || data.cart?.checkoutUrl;
|
|
1123
|
+
if (url && this.customerAccessToken) url += `${url.includes("?") ? "&" : "?"}logged_in=true`;
|
|
1046
1124
|
if (!url) throw new TetherError("No checkoutUrl (cart empty or expired)");
|
|
1047
1125
|
emit("checkout", { url, cart: this.state });
|
|
1048
1126
|
if (tab && !tab.closed) tab.location.href = url;
|
|
@@ -1058,13 +1136,18 @@
|
|
|
1058
1136
|
drawers() {
|
|
1059
1137
|
return qsa(document, sel("cart-drawer"));
|
|
1060
1138
|
},
|
|
1139
|
+
/** Without a tether-cart-drawer (a React/Svelte/Vue cart), open/close still work: as state for subscribe. */
|
|
1140
|
+
_open: false,
|
|
1061
1141
|
isOpen() {
|
|
1062
|
-
|
|
1142
|
+
const ds = this.drawers();
|
|
1143
|
+
return ds.length ? ds.some((d) => d.classList.contains(cfg.cartDrawerOpenClass)) : this._open;
|
|
1063
1144
|
},
|
|
1064
1145
|
open() {
|
|
1065
1146
|
if (this.isOpen()) return;
|
|
1147
|
+
this._open = true;
|
|
1066
1148
|
const ds = this.drawers().filter((d) => get(d, "cart-drawer") !== "manual");
|
|
1067
1149
|
ds.forEach((d) => {
|
|
1150
|
+
d.inert = false;
|
|
1068
1151
|
d.classList.add(cfg.cartDrawerOpenClass);
|
|
1069
1152
|
d.setAttribute("aria-hidden", "false");
|
|
1070
1153
|
});
|
|
@@ -1082,11 +1165,13 @@
|
|
|
1082
1165
|
},
|
|
1083
1166
|
close() {
|
|
1084
1167
|
if (!this.isOpen()) return;
|
|
1168
|
+
this._open = false;
|
|
1085
1169
|
const ds = this.drawers();
|
|
1086
1170
|
const hadFocus = ds.some((d) => d.contains(document.activeElement));
|
|
1087
1171
|
ds.forEach((d) => {
|
|
1088
1172
|
d.classList.remove(cfg.cartDrawerOpenClass);
|
|
1089
1173
|
d.setAttribute("aria-hidden", "true");
|
|
1174
|
+
if (cfg.cartFocus !== false) d.inert = true;
|
|
1090
1175
|
});
|
|
1091
1176
|
ariaState(false);
|
|
1092
1177
|
if (hadFocus && returnFocus?.isConnected) returnFocus.focus({ preventScroll: true });
|
|
@@ -1183,7 +1268,7 @@
|
|
|
1183
1268
|
case "line-subtotal":
|
|
1184
1269
|
return formatMoney(line.cost?.subtotalAmount);
|
|
1185
1270
|
case "quantity":
|
|
1186
|
-
return line
|
|
1271
|
+
return Cart.quantityOf(line);
|
|
1187
1272
|
case "plan":
|
|
1188
1273
|
return plan?.name || "";
|
|
1189
1274
|
case "plan-description":
|
|
@@ -1219,11 +1304,19 @@
|
|
|
1219
1304
|
if (v !== void 0) setTextTracked(el, v);
|
|
1220
1305
|
return void 0;
|
|
1221
1306
|
});
|
|
1307
|
+
const warning = Cart.warnings.find((w) => w.target === line.id);
|
|
1308
|
+
node.classList.toggle("is-warning", Boolean(warning));
|
|
1309
|
+
const pending2 = Cart.pendingQty.has(line.id);
|
|
1310
|
+
node.classList.toggle("is-busy", pending2);
|
|
1311
|
+
if (pending2) node.setAttribute("aria-busy", "true");
|
|
1312
|
+
else node.removeAttribute("aria-busy");
|
|
1313
|
+
qsa(node, sel("line", "warning")).forEach((el) => setTextTracked(el, warning?.message || ""));
|
|
1222
1314
|
const lineConds = {
|
|
1223
1315
|
subscription: isSub,
|
|
1224
1316
|
"one-time": !isSub,
|
|
1225
1317
|
"on-sale": parseFloat(line.cost?.compareAtAmountPerQuantity?.amount) > parseFloat(line.cost?.amountPerQuantity?.amount),
|
|
1226
|
-
"has-components": (line.lineComponents || []).length > 0
|
|
1318
|
+
"has-components": (line.lineComponents || []).length > 0,
|
|
1319
|
+
warning: Boolean(warning)
|
|
1227
1320
|
};
|
|
1228
1321
|
qsa(node, sel("line-show")).forEach((el) => {
|
|
1229
1322
|
const k = get(el, "line-show");
|
|
@@ -1274,8 +1367,8 @@
|
|
|
1274
1367
|
resolveProductKey: () => resolveProductKey,
|
|
1275
1368
|
setScopeStatus: () => setScopeStatus
|
|
1276
1369
|
});
|
|
1277
|
-
function resolveProductKey(el, location = window.location) {
|
|
1278
|
-
const raw = (get(el,
|
|
1370
|
+
function resolveProductKey(el, location = window.location, name = "product") {
|
|
1371
|
+
const raw = (get(el, name) || "").trim();
|
|
1279
1372
|
if (!raw.toLowerCase().startsWith("url:")) return { key: raw || null, fromUrl: false, raw };
|
|
1280
1373
|
const what = raw.slice(4).trim();
|
|
1281
1374
|
let key = null;
|
|
@@ -1457,7 +1550,17 @@
|
|
|
1457
1550
|
// ------------------------------------------------------------ actions
|
|
1458
1551
|
select(name, value) {
|
|
1459
1552
|
if (this.selected[name] === value) return;
|
|
1460
|
-
this.
|
|
1553
|
+
const vs = this.product.variants.nodes.filter((v) => v.selectedOptions.some((o) => o.name === name && o.value === value));
|
|
1554
|
+
if (!vs.length) return;
|
|
1555
|
+
const next = { ...this.selected, [name]: value };
|
|
1556
|
+
if (!vs.some((v) => v.selectedOptions.every((o) => next[o.name] === o.value))) {
|
|
1557
|
+
const score = (v) => v.selectedOptions.filter((o) => o.name !== name && this.selected[o.name] === o.value).length * 2 + (v.availableForSale ? 1 : 0);
|
|
1558
|
+
const best = vs.reduce((a, b) => score(b) > score(a) ? b : a);
|
|
1559
|
+
best.selectedOptions.forEach((o) => {
|
|
1560
|
+
next[o.name] = o.value;
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
this.selected = next;
|
|
1461
1564
|
this.renderVariant();
|
|
1462
1565
|
}
|
|
1463
1566
|
selectVariant(id) {
|
|
@@ -1704,7 +1807,7 @@
|
|
|
1704
1807
|
const plan = this.plan;
|
|
1705
1808
|
const total = { amount: (parseFloat(price.amount) * Math.max(1, this.quantity)).toFixed(2), currencyCode: price.currencyCode };
|
|
1706
1809
|
const fm = formatMoney;
|
|
1707
|
-
const pct = (n) => n
|
|
1810
|
+
const pct = (n) => formatPercent(n);
|
|
1708
1811
|
const text = {
|
|
1709
1812
|
price: () => fm(price),
|
|
1710
1813
|
"total-price": () => fm(total),
|
|
@@ -1712,7 +1815,11 @@
|
|
|
1712
1815
|
"subscription-price": () => fm(subPrice),
|
|
1713
1816
|
"subscription-savings": () => pct(savings.percentage),
|
|
1714
1817
|
"subscription-savings-amount": () => fm(savings.amount),
|
|
1715
|
-
|
|
1818
|
+
// What the visitor pays today for the chosen quantity (prepaid plans charge more up front).
|
|
1819
|
+
"checkout-charge": () => {
|
|
1820
|
+
const c = sub ? planCheckoutCharge(v, this.planId) : price;
|
|
1821
|
+
return c && fm({ amount: (parseFloat(c.amount) * Math.max(1, this.quantity)).toFixed(2), currencyCode: c.currencyCode });
|
|
1822
|
+
},
|
|
1716
1823
|
"plan-name": () => plan?.name,
|
|
1717
1824
|
"plan-description": () => plan?.description,
|
|
1718
1825
|
"plan-group": () => plan?.group?.name,
|
|
@@ -1771,6 +1878,8 @@
|
|
|
1771
1878
|
const k = get(el, "plan-bind");
|
|
1772
1879
|
const val = {
|
|
1773
1880
|
name: () => planObj?.name,
|
|
1881
|
+
option: () => (planObj?.options || []).map((o) => o.value).join(", "),
|
|
1882
|
+
// e.g. "1 month", without the store's price text
|
|
1774
1883
|
description: () => planObj?.description,
|
|
1775
1884
|
price: () => fm(planPrice(v, id)),
|
|
1776
1885
|
savings: () => pct(s.percentage),
|
|
@@ -1783,6 +1892,7 @@
|
|
|
1783
1892
|
this.$(`select${sel("plan")}`).forEach((s) => {
|
|
1784
1893
|
s.value = sub ? this.planId : "";
|
|
1785
1894
|
});
|
|
1895
|
+
const low = v.quantityAvailable != null && v.quantityAvailable > 0 && v.quantityAvailable <= (cfg.lowStock ?? 5);
|
|
1786
1896
|
const conds = {
|
|
1787
1897
|
"in-stock": v.availableForSale,
|
|
1788
1898
|
"out-of-stock": !v.availableForSale,
|
|
@@ -1791,7 +1901,8 @@
|
|
|
1791
1901
|
"not-on-sale": !onSale,
|
|
1792
1902
|
"has-variants": p.variants.nodes.length > 1,
|
|
1793
1903
|
"single-variant": p.variants.nodes.length === 1,
|
|
1794
|
-
"low-stock":
|
|
1904
|
+
"low-stock": low,
|
|
1905
|
+
"plenty-in-stock": v.availableForSale && !low,
|
|
1795
1906
|
"subscription-available": this.subscriptionAvailable,
|
|
1796
1907
|
"subscription-unavailable": !this.subscriptionAvailable,
|
|
1797
1908
|
"subscription-only": Boolean(p.requiresSellingPlan),
|
|
@@ -1880,9 +1991,11 @@
|
|
|
1880
1991
|
const stale2 = () => token !== this.loadToken;
|
|
1881
1992
|
try {
|
|
1882
1993
|
if (has(this.root, "recommendations")) {
|
|
1883
|
-
|
|
1884
|
-
const
|
|
1885
|
-
|
|
1994
|
+
let { key } = resolveProductKey(this.root, window.location, "recommendations");
|
|
1995
|
+
const scope = this.root.closest(sel("product"));
|
|
1996
|
+
if (!key && scope) ({ key } = resolveProductKey(scope));
|
|
1997
|
+
if (!key) throw new TetherError("No product for recommendations", { code: "NOT_FOUND" });
|
|
1998
|
+
const recs = await getRecommendations(key, get(this.root, "intent") || "RELATED");
|
|
1886
1999
|
if (stale2()) return;
|
|
1887
2000
|
const limit = parseInt(get(this.root, "limit") || "", 10);
|
|
1888
2001
|
this.products = limit ? recs.slice(0, limit) : recs;
|
|
@@ -1899,7 +2012,7 @@
|
|
|
1899
2012
|
} catch (err) {
|
|
1900
2013
|
if (stale2()) return;
|
|
1901
2014
|
if (!more) setScopeStatus(this.root, isNotFound(err) ? "not-found" : "error", err);
|
|
1902
|
-
reportError(err, "list load failed");
|
|
2015
|
+
if (!(isNotFound(err) && has(this.root, "recommendations"))) reportError(err, "list load failed");
|
|
1903
2016
|
emit("error", { error: err, context: "list" }, this.root);
|
|
1904
2017
|
}
|
|
1905
2018
|
}
|
|
@@ -1930,12 +2043,10 @@
|
|
|
1930
2043
|
const empty = this.products.length === 0;
|
|
1931
2044
|
this.$(sel("show")).forEach((el) => {
|
|
1932
2045
|
const k = get(el, "show");
|
|
1933
|
-
if (k === "list-empty") el
|
|
1934
|
-
if (k === "list-filled") el
|
|
1935
|
-
});
|
|
1936
|
-
this.$(sel("load-more")).forEach((el) => {
|
|
1937
|
-
el.style.display = this.pageInfo?.hasNextPage ? "" : "none";
|
|
2046
|
+
if (k === "list-empty") show(el, empty);
|
|
2047
|
+
if (k === "list-filled") show(el, !empty);
|
|
1938
2048
|
});
|
|
2049
|
+
this.$(sel("load-more")).forEach((el) => show(el, Boolean(this.pageInfo?.hasNextPage)));
|
|
1939
2050
|
notifyLayoutChanged("list");
|
|
1940
2051
|
}
|
|
1941
2052
|
};
|
|
@@ -1951,6 +2062,8 @@
|
|
|
1951
2062
|
mountedProducts: () => mountedProducts,
|
|
1952
2063
|
pruneLive: () => pruneLive,
|
|
1953
2064
|
retryFailed: () => retryFailed,
|
|
2065
|
+
settleMarket: () => settleMarket,
|
|
2066
|
+
trackMarket: () => trackMarket,
|
|
1954
2067
|
within: () => within
|
|
1955
2068
|
});
|
|
1956
2069
|
var mountedProducts = /* @__PURE__ */ new WeakSet();
|
|
@@ -1989,7 +2102,9 @@
|
|
|
1989
2102
|
const current = () => requestToken.get(el) === token;
|
|
1990
2103
|
if (!scopes.get(el)) setScopeStatus(el, "loading");
|
|
1991
2104
|
else el.setAttribute(attr("state"), "updating");
|
|
1992
|
-
getProduct(key)
|
|
2105
|
+
const loading = getProduct(key);
|
|
2106
|
+
marketPending?.add(loading);
|
|
2107
|
+
loading.then((p) => {
|
|
1993
2108
|
if (!current()) return;
|
|
1994
2109
|
const existing = scopes.get(el);
|
|
1995
2110
|
if (existing) existing.update(p);
|
|
@@ -2043,6 +2158,22 @@
|
|
|
2043
2158
|
}).catch((err) => reportError(err, `refresh ${keys[i]}`)));
|
|
2044
2159
|
}
|
|
2045
2160
|
var readScripts = /* @__PURE__ */ new WeakSet();
|
|
2161
|
+
var marketPending = null;
|
|
2162
|
+
var trackMarket = (p) => {
|
|
2163
|
+
marketPending?.add(p);
|
|
2164
|
+
return p;
|
|
2165
|
+
};
|
|
2166
|
+
function settleMarket() {
|
|
2167
|
+
if (!marketPending) return;
|
|
2168
|
+
const waits = [...marketPending];
|
|
2169
|
+
Promise.allSettled(waits).then(() => {
|
|
2170
|
+
if (!marketPending || waits.some((w) => !marketPending.has(w))) return;
|
|
2171
|
+
if (waits.length === marketPending.size) {
|
|
2172
|
+
marketPending = null;
|
|
2173
|
+
document.documentElement.classList.remove(`${cfg.prefix}-market-pending`);
|
|
2174
|
+
} else settleMarket();
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2046
2177
|
function hydrateFromPage(root = document) {
|
|
2047
2178
|
qsa(root.ownerDocument || root, 'script[type="application/json"][data-tether-hydrate]').forEach((s) => {
|
|
2048
2179
|
if (readScripts.has(s)) return;
|
|
@@ -2052,7 +2183,13 @@
|
|
|
2052
2183
|
rememberOptionNames(data.products);
|
|
2053
2184
|
const same = (a, b) => String(a || "").toUpperCase() === String(b || "").toUpperCase();
|
|
2054
2185
|
if (same(data.country, cfg.country) && same(data.language, cfg.language)) hydrateProducts(data.products);
|
|
2055
|
-
else
|
|
2186
|
+
else {
|
|
2187
|
+
log("hydration skipped: other market");
|
|
2188
|
+
if (!marketPending) {
|
|
2189
|
+
marketPending = /* @__PURE__ */ new Set();
|
|
2190
|
+
document.documentElement.classList.add(`${cfg.prefix}-market-pending`);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2056
2193
|
} catch (err) {
|
|
2057
2194
|
warn("bad data-tether-hydrate", err);
|
|
2058
2195
|
}
|
|
@@ -2096,7 +2233,7 @@
|
|
|
2096
2233
|
}
|
|
2097
2234
|
|
|
2098
2235
|
// src/index.js
|
|
2099
|
-
var VERSION = "0.
|
|
2236
|
+
var VERSION = "0.6.0";
|
|
2100
2237
|
var initPromise = null;
|
|
2101
2238
|
var observer = null;
|
|
2102
2239
|
function mount(root = document) {
|
|
@@ -2111,7 +2248,7 @@
|
|
|
2111
2248
|
const list = new ListScope(el);
|
|
2112
2249
|
liveLists.add(list);
|
|
2113
2250
|
el.addEventListener(`${cfg.prefix}:list-rendered`, (e) => mount(e.detail.container));
|
|
2114
|
-
list.load();
|
|
2251
|
+
trackMarket(list.load());
|
|
2115
2252
|
});
|
|
2116
2253
|
within(root, sel("product")).forEach((el) => {
|
|
2117
2254
|
if (mountProduct(el)) count++;
|
|
@@ -2119,9 +2256,16 @@
|
|
|
2119
2256
|
if (within(root, `${sel("country-select")},${sel("country-bind")}`).length) {
|
|
2120
2257
|
loadPart("markets").then((m) => m.renderCountrySelects(root)).catch((err) => reportError(err, "markets"));
|
|
2121
2258
|
}
|
|
2259
|
+
if ((cfg.account?.clientId || cfg.account?.server) && (within(root, `[${attr("account-login")}],[${attr("account-bind")}],[${attr("orders")}],[${attr("show")}^="logged-"]`).length || /[?&]code=/.test(window.location.search) || hasAccountSession())) {
|
|
2260
|
+
loadPart("account").then((m) => m.mountAccount(root)).catch((err) => reportError(err, "account"));
|
|
2261
|
+
}
|
|
2262
|
+
if (within(root, sel("search")).length) {
|
|
2263
|
+
loadPart("search").then((m) => m.mountSearch(root)).catch((err) => reportError(err, "search"));
|
|
2264
|
+
}
|
|
2122
2265
|
qsa(root, sel("account-link")).forEach((a) => {
|
|
2123
2266
|
a.setAttribute("href", `https://${cfg.domain}/account`);
|
|
2124
2267
|
});
|
|
2268
|
+
settleMarket();
|
|
2125
2269
|
Cart.render();
|
|
2126
2270
|
log(`mounted ${count} scope(s)`, root);
|
|
2127
2271
|
return count;
|
|
@@ -2170,9 +2314,12 @@
|
|
|
2170
2314
|
e.preventDefault();
|
|
2171
2315
|
fn().catch((err) => reportError(err, "cart line"));
|
|
2172
2316
|
};
|
|
2173
|
-
if (t.closest(sel("line-remove")))
|
|
2174
|
-
|
|
2175
|
-
|
|
2317
|
+
if (t.closest(sel("line-remove"))) {
|
|
2318
|
+
line.classList.add("is-busy");
|
|
2319
|
+
return run(() => Cart.removeLine(id));
|
|
2320
|
+
}
|
|
2321
|
+
if (current && t.closest(sel("line-plus"))) return run(() => Cart.changeQuantity(id, 1));
|
|
2322
|
+
if (current && t.closest(sel("line-minus"))) return run(() => Cart.changeQuantity(id, -1));
|
|
2176
2323
|
}
|
|
2177
2324
|
const removeCode = t.closest(sel("discount-remove"));
|
|
2178
2325
|
if (removeCode) {
|
|
@@ -2192,10 +2339,15 @@
|
|
|
2192
2339
|
if (!t?.closest) return;
|
|
2193
2340
|
const lineEl = t.closest(dataKeySel());
|
|
2194
2341
|
const id = lineEl?.getAttribute(`data-${cfg.prefix}-key`);
|
|
2342
|
+
const busyLine = (p, what) => {
|
|
2343
|
+
lineEl.classList.add("is-busy");
|
|
2344
|
+
lineEl.setAttribute("aria-busy", "true");
|
|
2345
|
+
p.catch((err) => reportError(err, what));
|
|
2346
|
+
};
|
|
2195
2347
|
if (id && t.matches(`input${sel("line", "quantity")}`)) {
|
|
2196
|
-
Cart.updateLine(id, parseInt(t.value, 10) || 0)
|
|
2348
|
+
busyLine(Cart.updateLine(id, parseInt(t.value, 10) || 0), "cart line");
|
|
2197
2349
|
} else if (id && t.matches(`select${sel("line-plan")}`)) {
|
|
2198
|
-
Cart.updateLine(id, { sellingPlanId: t.value || null })
|
|
2350
|
+
busyLine(Cart.updateLine(id, { sellingPlanId: t.value || null }), "cart line plan");
|
|
2199
2351
|
} else if (t.matches(`select${sel("country-select")}`)) {
|
|
2200
2352
|
if (t.value) setCountry(t.value).catch((err) => reportError(err, "country"));
|
|
2201
2353
|
} else if (t.matches(sel("cart-note"))) {
|
|
@@ -2210,25 +2362,57 @@
|
|
|
2210
2362
|
const code = input?.value.trim();
|
|
2211
2363
|
if (!code) return;
|
|
2212
2364
|
busy(form, async () => {
|
|
2365
|
+
const mark = (bad) => {
|
|
2366
|
+
form.classList.toggle("is-invalid", bad);
|
|
2367
|
+
if (input) {
|
|
2368
|
+
if (bad) input.setAttribute("aria-invalid", "true");
|
|
2369
|
+
else input.removeAttribute("aria-invalid");
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2213
2372
|
try {
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
form.classList.toggle("is-invalid", !ok);
|
|
2219
|
-
emit(ok ? "discount-applied" : "discount-invalid", { code }, form);
|
|
2220
|
-
if (ok && input) input.value = "";
|
|
2373
|
+
await Cart.applyDiscount(code);
|
|
2374
|
+
mark(false);
|
|
2375
|
+
emit("discount-applied", { code }, form);
|
|
2376
|
+
if (input) input.value = "";
|
|
2221
2377
|
} catch (err) {
|
|
2222
|
-
|
|
2223
|
-
|
|
2378
|
+
mark(true);
|
|
2379
|
+
if (err?.code === "INVALID_DISCOUNT") emit("discount-invalid", { code }, form);
|
|
2380
|
+
else reportError(err, "discount");
|
|
2224
2381
|
}
|
|
2225
2382
|
});
|
|
2226
2383
|
}, true);
|
|
2227
2384
|
document.addEventListener("input", (e) => {
|
|
2228
|
-
e.target.closest?.(sel("discount-form"))
|
|
2385
|
+
const f = e.target.closest?.(sel("discount-form"));
|
|
2386
|
+
if (f) {
|
|
2387
|
+
f.classList.remove("is-invalid");
|
|
2388
|
+
e.target.removeAttribute?.("aria-invalid");
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2391
|
+
window.addEventListener("storage", (e) => {
|
|
2392
|
+
if (cfg.domain && e.key?.startsWith(`${cfg.prefix}:`)) loadPart("extras").then((m) => m.onStorage(e)).catch(() => {
|
|
2393
|
+
});
|
|
2394
|
+
});
|
|
2395
|
+
document.addEventListener(`${cfg.prefix}:variant-changed`, (e) => {
|
|
2396
|
+
const root = e.target;
|
|
2397
|
+
if (cfg.variantUrl || root.hasAttribute?.(attr("variant-url"))) loadPart("extras").then((m) => m.writeVariantUrl(root, e.detail)).catch(() => {
|
|
2398
|
+
});
|
|
2229
2399
|
});
|
|
2230
2400
|
if (cfg.astro !== false) {
|
|
2231
|
-
document.addEventListener("astro:page-load", () =>
|
|
2401
|
+
document.addEventListener("astro:page-load", () => (initPromise || Promise.resolve()).then(() => {
|
|
2402
|
+
let target = pageMarket();
|
|
2403
|
+
if (!target && onPageMarket) {
|
|
2404
|
+
try {
|
|
2405
|
+
target = JSON.parse(localStorage.getItem(`${cfg.prefix}:market:${cfg.domain}`) || "null");
|
|
2406
|
+
} catch {
|
|
2407
|
+
target = null;
|
|
2408
|
+
}
|
|
2409
|
+
target = target?.country ? target : baseMarket;
|
|
2410
|
+
}
|
|
2411
|
+
onPageMarket = Boolean(pageMarket());
|
|
2412
|
+
const differs = target?.country && (String(target.country).toUpperCase() !== String(cfg.country || "").toUpperCase() || String(target.language || "").toUpperCase() !== String(cfg.language || "").toUpperCase());
|
|
2413
|
+
if (differs) setCountry(target.country, { language: target.language ?? null, locale: target.locale, remember: false }).then(() => mount(document), () => mount(document));
|
|
2414
|
+
else mount(document);
|
|
2415
|
+
}));
|
|
2232
2416
|
document.addEventListener("astro:before-swap", () => unmount());
|
|
2233
2417
|
}
|
|
2234
2418
|
window.addEventListener("online", () => {
|
|
@@ -2258,30 +2442,8 @@
|
|
|
2258
2442
|
function dataKeySel() {
|
|
2259
2443
|
return `[data-${cfg.prefix}-key]`;
|
|
2260
2444
|
}
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
if (!group) return null;
|
|
2264
|
-
const name = get(group, "group");
|
|
2265
|
-
const extra = toAttributes(parseJSONAttr(group, "line-attributes"));
|
|
2266
|
-
const groupAttrs = name ? [{ key: "_group", value: name }, ...extra] : extra;
|
|
2267
|
-
const lines = qsa(group, sel("product")).map((el) => scopes.get(el)).filter((s) => s && s.variant?.availableForSale && s.quantity > 0).map((s) => {
|
|
2268
|
-
const l = s.getLine();
|
|
2269
|
-
l.attributes = [...l.attributes || [], ...groupAttrs];
|
|
2270
|
-
return l;
|
|
2271
|
-
});
|
|
2272
|
-
if (!lines.length) {
|
|
2273
|
-
warn("group-add: nothing selected");
|
|
2274
|
-
return null;
|
|
2275
|
-
}
|
|
2276
|
-
try {
|
|
2277
|
-
const cart = await Cart.add(lines);
|
|
2278
|
-
emit("group-added", { group: name, lines }, group);
|
|
2279
|
-
return cart;
|
|
2280
|
-
} catch (err) {
|
|
2281
|
-
reportError(err, "group add");
|
|
2282
|
-
return null;
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2445
|
+
var addGroup = (btn) => loadPart("extras").then((m) => m.addGroup(btn));
|
|
2446
|
+
var analyticsQueue = null;
|
|
2285
2447
|
function init(overrides = {}) {
|
|
2286
2448
|
if (initPromise && !Object.keys(overrides).length) return initPromise;
|
|
2287
2449
|
configure({ ...window.TETHER_CONFIG || {}, ...overrides });
|
|
@@ -2292,6 +2454,12 @@
|
|
|
2292
2454
|
}
|
|
2293
2455
|
bindControls();
|
|
2294
2456
|
if (cfg.observe) startObserver();
|
|
2457
|
+
if (cfg.analytics && !analyticsQueue) {
|
|
2458
|
+
analyticsQueue = [];
|
|
2459
|
+
["product-ready", "list-ready", "cart-updated", "cart-toggled", "checkout", "search"].forEach((n) => document.addEventListener(`${cfg.prefix}:${n}`, (e) => analyticsQueue.push(e)));
|
|
2460
|
+
document.addEventListener("astro:page-load", () => analyticsQueue.push(new CustomEvent(`${cfg.prefix}:page`)));
|
|
2461
|
+
loadPart("analytics").then((m) => m.startAnalytics(analyticsQueue)).catch((err) => reportError(err, "analytics"));
|
|
2462
|
+
}
|
|
2295
2463
|
initPromise = applyMarket(overrides).then(() => Cart.init()).then(async (cart) => {
|
|
2296
2464
|
const want = cfg.country ? String(cfg.country).toUpperCase() : null;
|
|
2297
2465
|
if (cart && want && cart.buyerIdentity?.countryCode && cart.buyerIdentity.countryCode !== want) {
|
|
@@ -2306,8 +2474,24 @@
|
|
|
2306
2474
|
});
|
|
2307
2475
|
return initPromise;
|
|
2308
2476
|
}
|
|
2477
|
+
function pageMarket(doc = document) {
|
|
2478
|
+
const get2 = (n) => doc.querySelector(`meta[name="${cfg.prefix}-page-${n}"]`)?.getAttribute("content") || void 0;
|
|
2479
|
+
const country = get2("country");
|
|
2480
|
+
return country ? { country: country.toUpperCase(), language: get2("language"), locale: get2("locale") } : null;
|
|
2481
|
+
}
|
|
2482
|
+
var baseMarket = null;
|
|
2483
|
+
var onPageMarket = false;
|
|
2309
2484
|
async function applyMarket(overrides) {
|
|
2310
2485
|
const m = cfg.markets || {};
|
|
2486
|
+
baseMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };
|
|
2487
|
+
const page = pageMarket();
|
|
2488
|
+
onPageMarket = Boolean(page);
|
|
2489
|
+
if (page) {
|
|
2490
|
+
cfg.country = page.country;
|
|
2491
|
+
cfg.language = page.language ? page.language.toUpperCase() : void 0;
|
|
2492
|
+
if (page.locale) cfg.locale = page.locale;
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2311
2495
|
if (m.detect === false) return;
|
|
2312
2496
|
let market = null;
|
|
2313
2497
|
if (m.detect) {
|
|
@@ -2330,6 +2514,14 @@
|
|
|
2330
2514
|
if (market.locale && !overrides.locale) cfg.locale = market.locale;
|
|
2331
2515
|
log("market", market);
|
|
2332
2516
|
}
|
|
2517
|
+
function hasAccountSession() {
|
|
2518
|
+
try {
|
|
2519
|
+
return Boolean(sessionStorage[`${cfg.prefix}:account:${cfg.domain}`] || localStorage[`${cfg.prefix}:account:${cfg.domain}`] || localStorage[`${cfg.prefix}:account-hint:${cfg.domain}`]);
|
|
2520
|
+
} catch {
|
|
2521
|
+
return false;
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
var accountApi = new Proxy({}, { get: (_, n) => (...a) => loadPart("account").then((m) => m[n](...a)) });
|
|
2333
2525
|
function setCountry(code, opts) {
|
|
2334
2526
|
return loadPart("markets").then((m) => m.setCountry(code, opts));
|
|
2335
2527
|
}
|
|
@@ -2370,6 +2562,7 @@
|
|
|
2370
2562
|
getRecommendations,
|
|
2371
2563
|
getScope,
|
|
2372
2564
|
addGroup,
|
|
2565
|
+
account: accountApi,
|
|
2373
2566
|
primeProduct,
|
|
2374
2567
|
normalizeProductKey,
|
|
2375
2568
|
clearProductCache,
|