@useinsider/guido 3.14.0 → 3.15.0-beta.36d3655
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/dist/@types/config/schemas.js +108 -92
- package/dist/components/Guido.vue.js +1 -1
- package/dist/components/Guido.vue2.js +91 -85
- package/dist/composables/useActionsApi.js +30 -22
- package/dist/composables/useEmailTemplateApplier.js +48 -32
- package/dist/composables/useHtmlValidator.js +111 -106
- package/dist/composables/useTimerClone.js +21 -19
- package/dist/config/compiler/itemsCompilerRules.js +25 -4
- package/dist/config/compiler/utils/itemsCompilerUtils.js +28 -0
- package/dist/config/migrator/index.js +12 -10
- package/dist/config/migrator/itemsBlockMigrator.js +47 -45
- package/dist/config/migrator/productNameLinkMigrator.js +27 -0
- package/dist/config/migrator/trackIdMigrator.js +11 -0
- package/dist/extensions/Blocks/Items/block.js +30 -30
- package/dist/extensions/Blocks/Items/controls/name/trimming.js +16 -16
- package/dist/extensions/Blocks/Items/controls/price/hideDiscount.js +26 -22
- package/dist/extensions/Blocks/Items/controls/settingsControl.js +36 -35
- package/dist/extensions/Blocks/Items/template.js +228 -263
- package/dist/extensions/Blocks/Items/utils/nameNode.js +4 -0
- package/dist/guido.css +1 -1
- package/dist/library.js +8 -5
- package/dist/node_modules/valibot/dist/index.js +230 -216
- package/dist/src/@types/config/schemas.d.ts +20 -0
- package/dist/src/composables/useActionsApi.d.ts +1 -1
- package/dist/src/composables/useConfig.d.ts +4 -0
- package/dist/src/config/compiler/utils/itemsCompilerUtils.d.ts +12 -0
- package/dist/src/config/migrator/index.d.ts +6 -1
- package/dist/src/config/migrator/productNameLinkMigrator.d.ts +1 -0
- package/dist/src/config/migrator/trackIdMigrator.d.ts +6 -0
- package/dist/src/extensions/Blocks/Items/block.d.ts +1 -1
- package/dist/src/extensions/Blocks/Items/utils/nameNode.d.ts +9 -0
- package/dist/src/library.d.ts +1 -0
- package/dist/src/stores/config.d.ts +36 -0
- package/dist/src/stores/dynamic-content.d.ts +8 -0
- package/dist/src/stores/editor.d.ts +25 -0
- package/dist/src/utils/linkTrackingIds.d.ts +25 -0
- package/dist/src/utils/registerUsedDynamicContent.d.ts +8 -0
- package/dist/src/utils/unwrapProductNameLink.d.ts +15 -0
- package/dist/stores/dynamic-content.js +12 -2
- package/dist/stores/editor.js +4 -1
- package/dist/utils/linkTrackingIds.js +46 -0
- package/dist/utils/pairProductVariables.js +75 -72
- package/dist/utils/registerUsedDynamicContent.js +23 -0
- package/dist/utils/templatePreparation.js +72 -61
- package/dist/utils/unwrapProductNameLink.js +37 -0
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent link tracking ids for the click heatmap (SD-141414).
|
|
3
|
+
* Pure string transforms — no DOM parse/serialize round-trip, safe for both
|
|
4
|
+
* Stripo raw HTML and hand-authored HTML-editor documents. Shared with email-fe.
|
|
5
|
+
*/
|
|
6
|
+
export declare const TRACK_ID_ATTRIBUTE = "data-ins-track-id";
|
|
7
|
+
export declare const TRACK_SEQ_ATTRIBUTE = "data-ins-track-seq";
|
|
8
|
+
export interface TrackingIdResult {
|
|
9
|
+
html: string;
|
|
10
|
+
changed: boolean;
|
|
11
|
+
nextSeq: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Reads the persisted id high-water mark: <body> attr, then <html> attr,
|
|
15
|
+
* then the leading comment fallback. Returns 0 when absent.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readTrackSeq(html: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Ensures every <a> opening tag carries a unique positive-integer id and the
|
|
20
|
+
* document records the id high-water mark. Ids are minted above
|
|
21
|
+
* max(persisted seq, max id in document, floor) so deleted ids are never
|
|
22
|
+
* reused — even after a version restore or paste-over (floor = session floor).
|
|
23
|
+
* Idempotent: a document already normalized returns changed=false.
|
|
24
|
+
*/
|
|
25
|
+
export declare function assignTrackingIds(html: string, floor?: number): TrackingIdResult;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DynamicContent } from '@@/Types/generic';
|
|
2
|
+
export declare const extractUsedDynamicContent: (html: string, flattenedCatalog: DynamicContent[] | undefined | null) => DynamicContent[];
|
|
3
|
+
/**
|
|
4
|
+
* Dev-diagnostic sibling of `extractUsedDynamicContent`: the used token names
|
|
5
|
+
* with NO catalog match (so intentionally not registered). Surfaced via a
|
|
6
|
+
* dev-gated `console.debug` to spot a neo↔guido vocabulary mismatch. Never throws.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getUnmatchedDynamicContentTokens: (html: string, flattenedCatalog: DynamicContent[] | undefined | null) => string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unwraps the `<a>` around an Items-block product name so the name compiles as plain text.
|
|
3
|
+
*
|
|
4
|
+
* Outlook's Word engine ignores `color: inherit` on an `<a>` and paints its own blue link
|
|
5
|
+
* color (SD-147616). New blocks no longer emit the anchor, but templates saved before the
|
|
6
|
+
* fix still carry it in their stored HTML — this heals them at compile time.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Removes the product-name link from every `.ins-product-td` in the document, in place.
|
|
10
|
+
*
|
|
11
|
+
* Scoped like `pairProductVariables` so partner or legacy recommendation markup outside a
|
|
12
|
+
* product cell is never touched. Idempotent.
|
|
13
|
+
* @param doc - Parsed template document, mutated in place
|
|
14
|
+
*/
|
|
15
|
+
export declare function unwrapProductNameLinks(doc: Document): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { removeDuplicatesFromArray as n } from "../utils/arrayUtil.js";
|
|
2
2
|
import { defineStore as a } from "pinia";
|
|
3
|
-
const
|
|
3
|
+
const c = a("guidoDynamicContent", {
|
|
4
4
|
state: () => ({
|
|
5
5
|
selectedDynamicContent: { text: "", value: "" },
|
|
6
6
|
selectedDynamicContentList: []
|
|
@@ -8,6 +8,16 @@ const s = a("guidoDynamicContent", {
|
|
|
8
8
|
actions: {
|
|
9
9
|
setSelectedDynamicContent(t) {
|
|
10
10
|
this.selectedDynamicContent = t, this.selectedDynamicContentList.push(t);
|
|
11
|
+
},
|
|
12
|
+
/**
|
|
13
|
+
* Append-only bulk registration for dynamic content used by an
|
|
14
|
+
* AI-applied template. Unlike `setSelectedDynamicContent` it doesn't
|
|
15
|
+
* touch `selectedDynamicContent`; the getter removes duplicates, so
|
|
16
|
+
* re-registering the same entries is harmless. Items are shallow-copied so Pinia
|
|
17
|
+
* reactivity can't write a later mutation back into the host's catalog.
|
|
18
|
+
*/
|
|
19
|
+
registerDynamicContent(t) {
|
|
20
|
+
this.selectedDynamicContentList.push(...t.map((e) => ({ ...e })));
|
|
11
21
|
}
|
|
12
22
|
},
|
|
13
23
|
getters: {
|
|
@@ -20,5 +30,5 @@ const s = a("guidoDynamicContent", {
|
|
|
20
30
|
}
|
|
21
31
|
});
|
|
22
32
|
export {
|
|
23
|
-
|
|
33
|
+
c as useDynamicContentStore
|
|
24
34
|
};
|
package/dist/stores/editor.js
CHANGED
|
@@ -10,7 +10,10 @@ const o = e("guidoEditor", {
|
|
|
10
10
|
hasChanges: !1,
|
|
11
11
|
isStripoInitialized: !1,
|
|
12
12
|
templateId: "",
|
|
13
|
-
syncModulesEnabled: !1
|
|
13
|
+
syncModulesEnabled: !1,
|
|
14
|
+
// In-session id high-water mark; floors tracking-id minting so ids
|
|
15
|
+
// deleted by a version restore / paste-over are never reused.
|
|
16
|
+
trackIdSessionFloor: 0
|
|
14
17
|
}),
|
|
15
18
|
getters: {
|
|
16
19
|
// Middle Slot
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const $ = "data-ins-track-id", l = "data-ins-track-seq", E = `(?:[^>"']|"[^"]*"|'[^']*')*`, S = new RegExp(`<a\\b${E}>`, "gi"), p = new RegExp(`<body\\b${E}>`, "i"), b = new RegExp(`<html\\b${E}>`, "i"), I = new RegExp(`\\b${$}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i"), N = new RegExp(`\\b${l}\\s*=\\s*(?:"(\\d+)"|'(\\d+)'|(\\d+))`, "i"), f = "ins-track-seq", x = new RegExp(`<!--\\s*${f}:(\\d+)\\s*-->`, "i"), w = /^[1-9][0-9]{0,14}$/, q = 999999999999999;
|
|
2
|
+
function C(e) {
|
|
3
|
+
const n = Number(e);
|
|
4
|
+
return Number.isSafeInteger(n) && n > 0 ? n : 0;
|
|
5
|
+
}
|
|
6
|
+
function k(e) {
|
|
7
|
+
var a;
|
|
8
|
+
const n = (r) => {
|
|
9
|
+
var c;
|
|
10
|
+
const o = (c = r.exec(e)) == null ? void 0 : c[0];
|
|
11
|
+
return o ? N.exec(o) : null;
|
|
12
|
+
}, t = n(p) ?? n(b), s = (t == null ? void 0 : t[1]) ?? (t == null ? void 0 : t[2]) ?? (t == null ? void 0 : t[3]) ?? ((a = x.exec(e)) == null ? void 0 : a[1]) ?? "";
|
|
13
|
+
return C(s);
|
|
14
|
+
}
|
|
15
|
+
function D(e, n) {
|
|
16
|
+
const t = `${l}="${n}"`, s = p.exec(e) ?? b.exec(e);
|
|
17
|
+
if (s) {
|
|
18
|
+
const [r] = s, o = N.exec(r), c = o ? r.replace(o[0], t) : r.replace(/\/?>$/, (T) => ` ${t}${T}`);
|
|
19
|
+
return e.slice(0, s.index) + c + e.slice(s.index + r.length);
|
|
20
|
+
}
|
|
21
|
+
const a = `<!--${f}:${n}-->`;
|
|
22
|
+
return x.test(e) ? e.replace(x, a) : `${a}${e}`;
|
|
23
|
+
}
|
|
24
|
+
function M(e, n, t) {
|
|
25
|
+
const s = `${$}="${t}"`;
|
|
26
|
+
return n ? e.replace(n[0], s) : e.replace(/^<a\b/i, (a) => `${a} ${s}`);
|
|
27
|
+
}
|
|
28
|
+
function P(e, n = 0) {
|
|
29
|
+
const t = /* @__PURE__ */ new Set();
|
|
30
|
+
let s = 0;
|
|
31
|
+
const a = [...e.matchAll(S)].map((i) => {
|
|
32
|
+
const [g] = i, u = I.exec(g), _ = u ? u[1] ?? u[2] ?? u[3] ?? "" : "";
|
|
33
|
+
let d = null;
|
|
34
|
+
return w.test(_) && !t.has(Number(_)) && (d = Number(_), t.add(d), s = Math.max(s, d)), { index: i.index, tag: g, attrMatch: u, id: d };
|
|
35
|
+
}), r = k(e), o = Number.isSafeInteger(n) && n > 0 && n < q ? n : 0;
|
|
36
|
+
let c = Math.max(r, s, o), T = "", A = 0, R = !1;
|
|
37
|
+
return a.forEach((i) => {
|
|
38
|
+
T += e.slice(A, i.index), i.id === null ? (c += 1, T += M(i.tag, i.attrMatch, c), R = !0) : T += i.tag, A = i.index + i.tag.length;
|
|
39
|
+
}), T += e.slice(A), c > 0 && r !== c && (T = D(T, c), R = !0), { html: T, changed: R, nextSeq: c };
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
$ as TRACK_ID_ATTRIBUTE,
|
|
43
|
+
l as TRACK_SEQ_ATTRIBUTE,
|
|
44
|
+
P as assignTrackingIds,
|
|
45
|
+
k as readTrackSeq
|
|
46
|
+
};
|
|
@@ -1,85 +1,88 @@
|
|
|
1
|
-
import { productPairs as
|
|
1
|
+
import { productPairs as q } from "../extensions/Blocks/Items/enums/productEnums.js";
|
|
2
2
|
import { DefaultConfigValues as O } from "../extensions/Blocks/Items/enums/settingsEnums.js";
|
|
3
3
|
import { preserveTextStyles as C } from "./preserveTextStyles.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import { unwrapProductNameLinks as U } from "./unwrapProductNameLink.js";
|
|
5
|
+
function Y(L) {
|
|
6
|
+
const H = L.replaceAll("{%", "<!--{%").replaceAll("%}", "%}-->"), b = new DOMParser().parseFromString(H, "text/html");
|
|
7
|
+
U(b);
|
|
8
|
+
const R = q.PAIRS_FOR_EXTENSION;
|
|
9
|
+
Object.entries(R).forEach(([r, u]) => {
|
|
10
|
+
b.querySelectorAll(".ins-product-td").forEach((l) => {
|
|
11
|
+
const h = l.getAttribute("data-number") || "1", $ = l.getAttribute("data-type") || "CART_ITEMS";
|
|
12
|
+
l.querySelectorAll(`[product-attr="${r}"]`).forEach((t) => {
|
|
10
13
|
var F, I, w;
|
|
11
|
-
const T = t.getAttribute("data-type") ||
|
|
14
|
+
const T = t.getAttribute("data-type") || $, f = t.getAttribute("data-number") || h, d = u[T];
|
|
12
15
|
if (d)
|
|
13
16
|
switch (r) {
|
|
14
17
|
case "imageSrc": {
|
|
15
|
-
let e = null,
|
|
16
|
-
if (t.tagName === "IMG" ? (e = t,
|
|
18
|
+
let e = null, c = null;
|
|
19
|
+
if (t.tagName === "IMG" ? (e = t, c = e.closest("a")) : (e = t.querySelector("img"), c = t.querySelector("a") || t.closest("a")), !e)
|
|
17
20
|
break;
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
21
|
+
const i = d.DEFAULT, a = d.ATTR;
|
|
22
|
+
if (i && e.src) {
|
|
20
23
|
const o = e.src;
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
return o.includes(
|
|
24
|
-
}) && (e.src = `{{${
|
|
24
|
+
i.some((E) => {
|
|
25
|
+
const s = E.split("/").pop() || "", g = o.split("/").pop() || "";
|
|
26
|
+
return o.includes(E) || o.includes(s) || g === s;
|
|
27
|
+
}) && (e.src = `{{${a}_${f}}}`);
|
|
25
28
|
}
|
|
26
|
-
const n = (I = (F =
|
|
27
|
-
if (n && e.setAttribute("alt", `{{${n}_${
|
|
28
|
-
const o = (w =
|
|
29
|
+
const n = (I = (F = R.name) == null ? void 0 : F[T]) == null ? void 0 : I.ATTR;
|
|
30
|
+
if (n && e.setAttribute("alt", `{{${n}_${f}}}`), c) {
|
|
31
|
+
const o = (w = R.itemLink) == null ? void 0 : w[T];
|
|
29
32
|
if (o) {
|
|
30
|
-
const
|
|
31
|
-
(
|
|
33
|
+
const A = o.HREF, E = o.DEFAULT_HREF || "#!", s = c.href;
|
|
34
|
+
(s === "#" || s === "" || s.endsWith("#!") || s.endsWith(E) || s === `${window.location.href}${E}` || !s || s === window.location.href) && (c.href = `{{${A}_${f}}}`);
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
break;
|
|
35
38
|
}
|
|
36
39
|
case "name": {
|
|
37
|
-
const e = d,
|
|
38
|
-
t.textContent && (t.innerHTML = C(t, `{{${
|
|
40
|
+
const e = d, c = e.ATTR, i = e.DEFAULT_HREF || "#!", a = e.HREF;
|
|
41
|
+
t.textContent && (t.innerHTML = C(t, `{{${c}_${f}}}`));
|
|
39
42
|
const n = t.closest("a") || (t.tagName === "A" ? t : null);
|
|
40
|
-
if (n &&
|
|
41
|
-
const o = n.href,
|
|
42
|
-
(o ===
|
|
43
|
+
if (n && a) {
|
|
44
|
+
const o = n.href, A = `${window.location.href}${i}`;
|
|
45
|
+
(o === A || o.endsWith(i)) && (n.href = `{{${a}_${f}}}`);
|
|
43
46
|
}
|
|
44
47
|
break;
|
|
45
48
|
}
|
|
46
49
|
case "price":
|
|
47
50
|
case "originalPrice": {
|
|
48
|
-
const e = d,
|
|
49
|
-
let
|
|
50
|
-
n ?
|
|
51
|
-
const
|
|
52
|
-
let
|
|
53
|
-
const
|
|
54
|
-
let
|
|
55
|
-
|
|
51
|
+
const e = d, c = t.getAttribute("data-formated"), i = t.getAttribute("data-single_price"), a = c === "true", n = i === "true", o = t.getAttribute("data-curency") || "before";
|
|
52
|
+
let A;
|
|
53
|
+
n ? A = a ? e.SINGLE_PRICE_FORMATTED : e.SINGLE_PRICE : A = a ? e.PRICE_FORMATTED : e.PRICE;
|
|
54
|
+
const E = e.CURRENCY;
|
|
55
|
+
let s = `{{${A}_${f}}}`;
|
|
56
|
+
const g = (t.getAttribute("data-currency_symbol") || "").trim(), N = g !== "" && g !== O.productPriceCurrencySymbolControlValue;
|
|
57
|
+
let m = "";
|
|
58
|
+
N ? m = g : E && (m = `{{${E}_${f}}}`), m && (s = o === "after" ? `${s} ${m}` : `${m} ${s}`), t.innerHTML = C(t, s);
|
|
56
59
|
break;
|
|
57
60
|
}
|
|
58
61
|
case "quantity": {
|
|
59
|
-
const e = d,
|
|
60
|
-
t.textContent && t.textContent.trim() ===
|
|
62
|
+
const e = d, c = e.ATTR, i = e.DEFAULT;
|
|
63
|
+
t.textContent && t.textContent.trim() === i && (t.innerHTML = C(t, `{{${c}_${f}}}`));
|
|
61
64
|
break;
|
|
62
65
|
}
|
|
63
66
|
case "button": {
|
|
64
|
-
const e = d,
|
|
65
|
-
if (
|
|
66
|
-
const n =
|
|
67
|
-
(n === "" || n === "#" || n === o || n.endsWith(
|
|
67
|
+
const e = d, c = e.HREF, i = e.DEFAULT_HREF || "#!", a = t.tagName === "A" ? t : t.querySelector("a");
|
|
68
|
+
if (a) {
|
|
69
|
+
const n = a.href || "", o = `${window.location.href}${i}`;
|
|
70
|
+
(n === "" || n === "#" || n === o || n.endsWith(i) || n.endsWith("#!") || n === window.location.href) && (a.href = `{{${c}_${f}}}`);
|
|
68
71
|
}
|
|
69
72
|
break;
|
|
70
73
|
}
|
|
71
74
|
case "itemLink": {
|
|
72
|
-
const e = d,
|
|
73
|
-
if (
|
|
74
|
-
const n =
|
|
75
|
-
(n === o || n.endsWith(
|
|
75
|
+
const e = d, c = e.HREF, i = e.DEFAULT_HREF || "#!", a = t;
|
|
76
|
+
if (a.href) {
|
|
77
|
+
const n = a.href, o = `${window.location.href}${i}`;
|
|
78
|
+
(n === o || n.endsWith(i)) && (a.href = `{{${c}_${f}}}`);
|
|
76
79
|
}
|
|
77
80
|
break;
|
|
78
81
|
}
|
|
79
82
|
default: {
|
|
80
83
|
if ("ATTR" in d) {
|
|
81
84
|
const e = d.ATTR;
|
|
82
|
-
t.textContent && (t.innerHTML = C(t, `{{${e}_${
|
|
85
|
+
t.textContent && (t.innerHTML = C(t, `{{${e}_${f}}}`));
|
|
83
86
|
}
|
|
84
87
|
break;
|
|
85
88
|
}
|
|
@@ -88,54 +91,54 @@ function V(L) {
|
|
|
88
91
|
});
|
|
89
92
|
});
|
|
90
93
|
const k = H.match(/<!DOCTYPE[^>]*>/i), P = k ? `${k[0]}
|
|
91
|
-
` : "", D =
|
|
94
|
+
` : "", D = b.querySelectorAll(".ins-product-td"), M = [];
|
|
92
95
|
D.forEach((r) => {
|
|
93
|
-
const
|
|
96
|
+
const u = r.getAttribute("data-type") || "CART_ITEMS", p = r.getAttribute("data-number") || "1", l = r.outerHTML;
|
|
94
97
|
M.push({
|
|
95
98
|
element: r,
|
|
96
|
-
outerHtml:
|
|
97
|
-
type:
|
|
98
|
-
number: p
|
|
99
|
-
nodup: c || void 0
|
|
99
|
+
outerHtml: l,
|
|
100
|
+
type: u,
|
|
101
|
+
number: p
|
|
100
102
|
});
|
|
101
103
|
});
|
|
102
|
-
let _ = P +
|
|
103
|
-
M.reverse().forEach(({ outerHtml: r, type:
|
|
104
|
-
let
|
|
105
|
-
switch (
|
|
104
|
+
let _ = P + b.documentElement.outerHTML;
|
|
105
|
+
M.reverse().forEach(({ outerHtml: r, type: u, number: p }) => {
|
|
106
|
+
let l = "";
|
|
107
|
+
switch (u) {
|
|
106
108
|
case "CART_ITEMS":
|
|
107
|
-
|
|
109
|
+
l = "ins_apr_total_product_kind";
|
|
108
110
|
break;
|
|
109
111
|
case "BROWSED_ITEMS":
|
|
110
|
-
|
|
112
|
+
l = "browsed_item_total_product_kind";
|
|
111
113
|
break;
|
|
112
114
|
case "PURCHASED_ITEMS":
|
|
113
|
-
|
|
115
|
+
l = "purchased_item_total_product_kind";
|
|
114
116
|
break;
|
|
115
117
|
}
|
|
116
|
-
if (
|
|
117
|
-
const
|
|
118
|
+
if (l) {
|
|
119
|
+
const $ = parseInt(p) - 1, T = `${`{% if ${l} > ${$} %}`}${r}{% endif %}`;
|
|
118
120
|
_ = _.replace(r, T);
|
|
119
121
|
}
|
|
120
122
|
});
|
|
121
|
-
const x =
|
|
123
|
+
const x = b.querySelectorAll('[product-attr="originalPrice"][data-type="CART_ITEMS"]'), S = [];
|
|
122
124
|
return x.forEach((r) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
var $;
|
|
126
|
+
const u = r.getAttribute("data-number"), p = r.getAttribute("data-type");
|
|
127
|
+
if (!u || p !== "CART_ITEMS" || (($ = r.closest(".ins-product-td")) == null ? void 0 : $.getAttribute("data-nodup")) === "false")
|
|
125
128
|
return;
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
const
|
|
129
|
-
|
|
129
|
+
const h = r.closest(".product-original-price-class");
|
|
130
|
+
if (h) {
|
|
131
|
+
const y = h.outerHTML;
|
|
132
|
+
S.some((T) => T.tdOuterHtml === y) || S.push({ tdOuterHtml: y, number: u });
|
|
130
133
|
}
|
|
131
|
-
}),
|
|
132
|
-
const p = `{% if ins_apr_price_${
|
|
134
|
+
}), S.reverse().forEach(({ tdOuterHtml: r, number: u }) => {
|
|
135
|
+
const p = `{% if ins_apr_price_${u} != ins_apr_originalprice_${u} %}`;
|
|
133
136
|
if (!_.includes(p) && !r.includes("{% if")) {
|
|
134
|
-
const
|
|
135
|
-
_ = _.replace(r,
|
|
137
|
+
const h = `${p}${r}{% endif %}`;
|
|
138
|
+
_ = _.replace(r, h);
|
|
136
139
|
}
|
|
137
140
|
}), _.replaceAll("<!--{%", "{%").replaceAll("%}-->", "%}").replaceAll("<!--{%", "{%").replaceAll("%}-->", "%}");
|
|
138
141
|
}
|
|
139
142
|
export {
|
|
140
|
-
|
|
143
|
+
Y as pairProductVariables
|
|
141
144
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DATA_ATTRIBUTE_REGEX as T } from "../enums/html-validator.js";
|
|
2
|
+
const d = /{{([a-zA-Z0-9_.\s]*)}}/gm, h = (n, e) => {
|
|
3
|
+
if (!e || e.length === 0)
|
|
4
|
+
return { matched: [], unmatchedTokens: [] };
|
|
5
|
+
const s = n.replace(T, "$1").match(d);
|
|
6
|
+
if (!s)
|
|
7
|
+
return { matched: [], unmatchedTokens: [] };
|
|
8
|
+
const u = new Map(
|
|
9
|
+
e.map((c) => [c.value.trim().toLowerCase(), c])
|
|
10
|
+
), a = /* @__PURE__ */ new Set(), o = [], m = [];
|
|
11
|
+
return s.forEach((c) => {
|
|
12
|
+
const t = c.slice(2, -2).trim().toLowerCase();
|
|
13
|
+
if (t === "" || a.has(t))
|
|
14
|
+
return;
|
|
15
|
+
a.add(t);
|
|
16
|
+
const r = u.get(t);
|
|
17
|
+
r ? o.push(r) : m.push(t);
|
|
18
|
+
}), { matched: o, unmatchedTokens: m };
|
|
19
|
+
}, E = (n, e) => h(n, e).matched, k = (n, e) => h(n, e).unmatchedTokens;
|
|
20
|
+
export {
|
|
21
|
+
E as extractUsedDynamicContent,
|
|
22
|
+
k as getUnmatchedDynamicContentTokens
|
|
23
|
+
};
|
|
@@ -1,62 +1,65 @@
|
|
|
1
|
-
import { useActionsApi as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { useActionsApi as k } from "../composables/useActionsApi.js";
|
|
2
|
+
import { useConfig as U } from "../composables/useConfig.js";
|
|
3
|
+
import { useHtmlCompiler as x } from "../composables/useHtmlCompiler.js";
|
|
4
|
+
import { DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as s } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
|
|
5
|
+
import { useRecommendationExtensionStore as C } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
6
|
+
import { mapLegacyStrategy as L } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
|
|
7
|
+
import { DATA_ATTRIBUTES as A } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
|
|
8
|
+
import { parsePageList as R } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
|
|
9
|
+
import { useDynamicContentStore as N } from "../stores/dynamic-content.js";
|
|
10
|
+
import { useEditorStore as z } from "../stores/editor.js";
|
|
11
|
+
import { useUnsubscribeStore as _ } from "../stores/unsubscribe.js";
|
|
12
|
+
import { assignTrackingIds as v } from "./linkTrackingIds.js";
|
|
13
|
+
function O(m) {
|
|
14
|
+
const n = new DOMParser().parseFromString(m, "text/html").querySelectorAll(`[${A.PAGE_LIST}]`), t = [];
|
|
15
|
+
return n.forEach((i) => {
|
|
16
|
+
const o = i.getAttribute(A.PAGE_LIST);
|
|
17
|
+
o && t.push(...R(o));
|
|
15
18
|
}), [...new Set(t)];
|
|
16
19
|
}
|
|
17
|
-
async function
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
+
async function M(m) {
|
|
21
|
+
const a = new DOMParser().parseFromString(m, "text/html").querySelectorAll(".recommendation-block-v2");
|
|
22
|
+
if (a.length === 0)
|
|
20
23
|
return;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
const
|
|
25
|
-
if (!Number.isFinite(
|
|
24
|
+
const n = C();
|
|
25
|
+
a.forEach((t) => {
|
|
26
|
+
var r, l, g, f, c;
|
|
27
|
+
const i = t.getAttribute("recommendation-id"), o = i ? Number(i) : NaN;
|
|
28
|
+
if (!Number.isFinite(o))
|
|
26
29
|
return;
|
|
27
|
-
const
|
|
28
|
-
if (!
|
|
30
|
+
const p = t.getAttribute("esd-ext-config");
|
|
31
|
+
if (!p)
|
|
29
32
|
return;
|
|
30
33
|
let e;
|
|
31
34
|
try {
|
|
32
|
-
e = JSON.parse(
|
|
35
|
+
e = JSON.parse(p);
|
|
33
36
|
} catch {
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
39
|
if (!e || typeof e != "object" || Array.isArray(e))
|
|
37
40
|
return;
|
|
38
|
-
const
|
|
41
|
+
const h = {
|
|
39
42
|
// Normalize legacy strategy keys (e.g. `mostViewed` → `mostPopular`) so
|
|
40
43
|
// already-saved blocks that never re-run the migrator don't emit a
|
|
41
44
|
// strategy-less campaign URL. `||` also catches '' / undefined (SD-144882).
|
|
42
|
-
strategy:
|
|
43
|
-
language: e.language ??
|
|
44
|
-
size: e.size ??
|
|
45
|
+
strategy: L(e.strategy) || s.strategy,
|
|
46
|
+
language: e.language ?? s.language,
|
|
47
|
+
size: e.size ?? s.size,
|
|
45
48
|
// Spread the default arrays so each block gets a fresh reference
|
|
46
49
|
// instead of sharing the singleton in DEFAULT_NODE_CONFIG.
|
|
47
|
-
productIds: e.productIds ?? [...
|
|
48
|
-
filters: e.filters ?? [...
|
|
49
|
-
shuffleProducts: e.shuffleProducts ??
|
|
50
|
-
currencyCode: ((
|
|
51
|
-
currencyAlignment: ((
|
|
52
|
-
currencyDecimalCount: ((g = e.currency) == null ? void 0 : g.decimalCount) ??
|
|
53
|
-
currencyDecimalSeparator: ((f = e.currency) == null ? void 0 : f.decimalSeparator) ??
|
|
54
|
-
currencyThousandSeparator: ((
|
|
50
|
+
productIds: e.productIds ?? [...s.productIds],
|
|
51
|
+
filters: e.filters ?? [...s.filters],
|
|
52
|
+
shuffleProducts: e.shuffleProducts ?? s.shuffleProducts,
|
|
53
|
+
currencyCode: ((r = e.currency) == null ? void 0 : r.code) ?? d.code,
|
|
54
|
+
currencyAlignment: ((l = e.currency) == null ? void 0 : l.alignment) ?? d.alignment,
|
|
55
|
+
currencyDecimalCount: ((g = e.currency) == null ? void 0 : g.decimalCount) ?? d.decimalCount,
|
|
56
|
+
currencyDecimalSeparator: ((f = e.currency) == null ? void 0 : f.decimalSeparator) ?? d.decimalSeparator,
|
|
57
|
+
currencyThousandSeparator: ((c = e.currency) == null ? void 0 : c.thousandSeparator) ?? d.thousandSeparator
|
|
55
58
|
};
|
|
56
|
-
|
|
59
|
+
n.seedBlockUrlConfig(o, h);
|
|
57
60
|
});
|
|
58
61
|
try {
|
|
59
|
-
await
|
|
62
|
+
await n.fetchRecommendationCreateData();
|
|
60
63
|
} catch (t) {
|
|
61
64
|
console.warn(
|
|
62
65
|
"Recommendation reference data pre-load failed; validator will skip the availability check.",
|
|
@@ -64,43 +67,51 @@ async function R(s) {
|
|
|
64
67
|
);
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
|
-
const
|
|
68
|
-
const
|
|
70
|
+
const Z = () => {
|
|
71
|
+
const m = N(), u = z(), a = _(), { getCompiledEmail: n, getTemplateData: t, updateHtmlAndCss: i } = k(), { compileHtml: o, compileAmpHtml: p } = x(), { isFeatureEnabled: e } = U();
|
|
69
72
|
return {
|
|
70
73
|
prepareTemplateDetails: async () => {
|
|
71
|
-
|
|
74
|
+
let { html: r, css: l, syncModulesIds: g = [] } = await t();
|
|
75
|
+
if (e("linkTrackingIds"))
|
|
76
|
+
try {
|
|
77
|
+
const { html: y, changed: H, nextSeq: I } = v(r, u.trackIdSessionFloor);
|
|
78
|
+
u.trackIdSessionFloor = I, H && (r = y, await i(y, l));
|
|
79
|
+
} catch (y) {
|
|
80
|
+
console.error("Tracking-id step failed, saving anyway:", y);
|
|
81
|
+
}
|
|
82
|
+
const { html: f, ampHtml: c = "", ampErrors: T = [] } = await n({
|
|
72
83
|
minimize: !0,
|
|
73
84
|
resetDataSavedFlag: !1
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const { compiledHtml:
|
|
85
|
+
});
|
|
86
|
+
a.selectedUnsubscribePages.length && await a.fetchTemplates(), await M(r);
|
|
87
|
+
const { compiledHtml: w, stats: S, appliedRules: D } = o(f), E = c && p(c).compiledHtml, P = m.getSelectedDynamicContentList, F = C(), b = O(r);
|
|
77
88
|
return console.debug("HTML Compilation Stats:", {
|
|
78
|
-
originalSize:
|
|
79
|
-
compiledSize:
|
|
80
|
-
reduction: `${
|
|
81
|
-
appliedRules:
|
|
82
|
-
executionTime: `${
|
|
89
|
+
originalSize: S.originalSize,
|
|
90
|
+
compiledSize: S.compiledSize,
|
|
91
|
+
reduction: `${S.reductionPercentage.toFixed(2)}%`,
|
|
92
|
+
appliedRules: D,
|
|
93
|
+
executionTime: `${S.executionTime.toFixed(2)}ms`
|
|
83
94
|
}), {
|
|
84
|
-
dynamicContentList:
|
|
85
|
-
compiledHtml:
|
|
86
|
-
rawHtml:
|
|
87
|
-
css:
|
|
88
|
-
ampHtml:
|
|
89
|
-
ampErrors:
|
|
95
|
+
dynamicContentList: P,
|
|
96
|
+
compiledHtml: w,
|
|
97
|
+
rawHtml: r,
|
|
98
|
+
css: l,
|
|
99
|
+
ampHtml: E,
|
|
100
|
+
ampErrors: T,
|
|
90
101
|
modules: g.map(Number),
|
|
91
102
|
recommendation: {
|
|
92
|
-
campaignUrls:
|
|
103
|
+
campaignUrls: F.recommendationCampaignUrls,
|
|
93
104
|
configs: {}
|
|
94
105
|
},
|
|
95
106
|
unsubscribe: {
|
|
96
|
-
status:
|
|
97
|
-
config:
|
|
107
|
+
status: b.length > 0,
|
|
108
|
+
config: b
|
|
98
109
|
}
|
|
99
110
|
};
|
|
100
111
|
}
|
|
101
112
|
};
|
|
102
113
|
};
|
|
103
114
|
export {
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
O as getHtmlReferencedUnsubscribePages,
|
|
116
|
+
Z as useTemplatePreparation
|
|
106
117
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const f = "text-decoration", A = /(?:[^;"'()]|"[^"]*"|'[^']*'|\([^)]*\))+/g, c = (e) => (e.match(A) || []).map((r) => r.trim()).filter(Boolean), l = (e) => e.slice(0, e.indexOf(":")).trim().toLowerCase(), m = (e) => e.slice(e.indexOf(":") + 1).trim().toLowerCase(), u = (e, r) => {
|
|
2
|
+
const s = e.getAttribute("style");
|
|
3
|
+
if (!s)
|
|
4
|
+
return;
|
|
5
|
+
const t = c(r.getAttribute("style") || ""), i = new Set(t.map(l)), o = c(s).filter((n) => {
|
|
6
|
+
const a = l(n);
|
|
7
|
+
return !a || i.has(a) ? !1 : !a.startsWith(f) && m(n) !== "inherit";
|
|
8
|
+
});
|
|
9
|
+
o.length && r.setAttribute("style", [...t, ...o].join("; "));
|
|
10
|
+
}, y = ["product-attr", "data-type", "data-number", "data-slot-2"], p = (e, r) => {
|
|
11
|
+
y.forEach((s) => {
|
|
12
|
+
const t = e.getAttribute(s);
|
|
13
|
+
t !== null && !r.hasAttribute(s) && r.setAttribute(s, t);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
function E(e) {
|
|
17
|
+
e.querySelectorAll(".ins-product-td").forEach((r) => {
|
|
18
|
+
const s = /* @__PURE__ */ new Set();
|
|
19
|
+
r.querySelectorAll('[product-attr="name"]').forEach((t) => {
|
|
20
|
+
const i = t.closest("a");
|
|
21
|
+
i && r.contains(i) && s.add(i);
|
|
22
|
+
}), s.forEach((t) => {
|
|
23
|
+
if (t.querySelector("img") || Array.from(t.querySelectorAll("[product-attr]")).some((a) => a.getAttribute("product-attr") !== "name"))
|
|
24
|
+
return;
|
|
25
|
+
const o = t.closest("p");
|
|
26
|
+
if (o && r.contains(o)) {
|
|
27
|
+
p(t, o), u(t, o), t.replaceWith(...Array.from(t.childNodes));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const n = e.createElement("span");
|
|
31
|
+
p(t, n), u(t, n), n.append(...Array.from(t.childNodes)), t.replaceWith(n);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
E as unwrapProductNameLinks
|
|
37
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useinsider/guido",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0-beta.36d3655",
|
|
4
4
|
"description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
|
|
5
5
|
"main": "./dist/guido.umd.cjs",
|
|
6
6
|
"module": "./dist/library.js",
|