@useinsider/guido 3.14.0 → 3.15.0-beta.0b5dc39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/composables/useEmailTemplateApplier.js +46 -32
  2. package/dist/composables/useHtmlValidator.js +105 -100
  3. package/dist/composables/useStripo.js +30 -29
  4. package/dist/config/compiler/itemsCompilerRules.js +25 -4
  5. package/dist/config/compiler/utils/itemsCompilerUtils.js +28 -0
  6. package/dist/config/i18n/en/labels.json.js +7 -3
  7. package/dist/config/migrator/index.js +9 -8
  8. package/dist/config/migrator/itemsBlockMigrator.js +47 -45
  9. package/dist/config/migrator/productNameLinkMigrator.js +27 -0
  10. package/dist/config/migrator/recommendation/compositionMapper.js +26 -23
  11. package/dist/config/migrator/recommendation/htmlBuilder.js +156 -155
  12. package/dist/extensions/Blocks/Items/block.js +30 -30
  13. package/dist/extensions/Blocks/Items/controls/name/trimming.js +16 -16
  14. package/dist/extensions/Blocks/Items/controls/price/hideDiscount.js +26 -22
  15. package/dist/extensions/Blocks/Items/controls/settingsControl.js +36 -35
  16. package/dist/extensions/Blocks/Items/template.js +228 -263
  17. package/dist/extensions/Blocks/Items/utils/nameNode.js +4 -0
  18. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +18 -15
  19. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +363 -322
  20. package/dist/extensions/Blocks/Recommendation/controls/main/layoutOrientation.js +8 -8
  21. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +271 -231
  22. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +16 -16
  23. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +16 -16
  24. package/dist/extensions/Blocks/Recommendation/templates/utils.js +120 -120
  25. package/dist/extensions/Blocks/Recommendation/utils/captureStyleTemplates.js +55 -54
  26. package/dist/extensions/Blocks/Recommendation/utils/compositionKeys.js +89 -0
  27. package/dist/extensions/Blocks/common-control.js +42 -35
  28. package/dist/src/composables/useHtmlValidator.d.ts +3 -2
  29. package/dist/src/config/compiler/utils/itemsCompilerUtils.d.ts +12 -0
  30. package/dist/src/config/migrator/productNameLinkMigrator.d.ts +1 -0
  31. package/dist/src/config/migrator/recommendation/types.d.ts +1 -0
  32. package/dist/src/extensions/Blocks/Items/block.d.ts +1 -1
  33. package/dist/src/extensions/Blocks/Items/utils/nameNode.d.ts +9 -0
  34. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  35. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +6 -0
  36. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +27 -5
  37. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +4 -2
  38. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +12 -11
  39. package/dist/src/extensions/Blocks/Recommendation/utils/compositionKeys.d.ts +47 -0
  40. package/dist/src/extensions/Blocks/common-control.d.ts +3 -1
  41. package/dist/src/stores/dynamic-content.d.ts +8 -0
  42. package/dist/src/utils/registerUsedDynamicContent.d.ts +8 -0
  43. package/dist/src/utils/unwrapProductNameLink.d.ts +15 -0
  44. package/dist/stores/dynamic-content.js +12 -2
  45. package/dist/utils/pairProductVariables.js +75 -72
  46. package/dist/utils/registerUsedDynamicContent.js +23 -0
  47. package/dist/utils/unwrapProductNameLink.js +37 -0
  48. package/package.json +1 -1
@@ -29,6 +29,8 @@ export declare class RecommendationCardCompositionControl extends CommonControl
29
29
  private _initializeComposition;
30
30
  private _registerValueChangeListeners;
31
31
  private _readCompositionFromNode;
32
+ /** Upgrades legacy bare-name custom keys to the canonical form. (SD-147101) */
33
+ private _normalizeComposition;
32
34
  private _readCustomAttributesFromNode;
33
35
  private _readVisibilityFromRows;
34
36
  private _getDefaultVisibilities;
@@ -61,7 +63,15 @@ export declare class RecommendationCardCompositionControl extends CommonControl
61
63
  private _createPriceGroupItemHtml;
62
64
  private _createCustomItemHtml;
63
65
  /**
64
- * Builds select options from the store's filterList.
66
+ * Dropdown label of an attribute. A partner can own a default and a custom
67
+ * attribute under the same name; the type is spelled out only then, so partners
68
+ * without a collision keep clean labels. (SD-147101)
69
+ */
70
+ private _toOptionLabel;
71
+ /**
72
+ * Builds select options from the store's filterList. Option values are
73
+ * `product-attr` values, so a default attribute and a same-named custom one
74
+ * never collapse onto a single option. (SD-147101)
65
75
  * Falls back to a single option for the currently selected attribute.
66
76
  */
67
77
  private _getSelectOptions;
@@ -144,6 +154,12 @@ export declare class RecommendationCardCompositionControl extends CommonControl
144
154
  */
145
155
  private _onCustomAttributeChanged;
146
156
  private _onReorder;
157
+ /**
158
+ * Outer HTML of an existing attribute row, rewritten to the canonical key when
159
+ * the row still carries a legacy bare-name one. Copying rows this way lets the
160
+ * DOM converge on the next composition change without writing on open.
161
+ */
162
+ private _getExistingRowHtml;
147
163
  private _injectCustomAttributeHtml;
148
164
  private _injectGridAttributeRow;
149
165
  private _injectListAttributeRow;
@@ -204,13 +220,19 @@ export declare class RecommendationCardCompositionControl extends CommonControl
204
220
  */
205
221
  private _subscribeToStoreChanges;
206
222
  /**
207
- * Returns filters eligible for the custom attribute dropdown,
208
- * excluding default attributes already covered by built-in toggle items.
223
+ * Returns filters eligible for the custom attribute dropdown, excluding default
224
+ * attributes already covered by built-in toggle items.
225
+ *
226
+ * Deduped by canonical value: a partner can own two backend rows of the same type
227
+ * and name (e.g. two `defaultAttribute` "discount"), which are indistinguishable
228
+ * once reduced to a `product-attr`. Left in, they render as identical options and
229
+ * skew the collision count that decides the (Default)/(Custom) suffix. (SD-147101)
209
230
  */
210
231
  private _getAddableFilters;
211
232
  /**
212
- * Looks up the display name for an attribute from the store's filterList.
213
- * Falls back to Title Case conversion of the snake_case attribute name.
233
+ * Looks up the display name for a `product-attr` value from the store's filterList.
234
+ * Matches on the canonical value, so a default and a same-named custom attribute
235
+ * resolve to their own entry. Falls back to Title Case of the bare name.
214
236
  */
215
237
  private _getDisplayNameForAttribute;
216
238
  /**
@@ -9,7 +9,7 @@
9
9
  * - In-place content updates (style-preserving)
10
10
  */
11
11
  import type { CurrencyConfig } from '../../types/nodeConfig';
12
- import type { RecommendationProduct } from '@@/Types/recommendation';
12
+ import type { FiltersResponse, RecommendationProduct } from '@@/Types/recommendation';
13
13
  import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
14
14
  import { ModificationDescription } from '@stripoinc/ui-editor-extensions';
15
15
  import { type Orientation } from '../../templates';
@@ -54,9 +54,11 @@ export declare function getCurrentLayout(currentNode: ImmutableHtmlNode | null |
54
54
  /**
55
55
  * Gets the current card composition from block data attribute or use default
56
56
  * @param currentNode - The current template node
57
+ * @param filterList - Partner filters. Omitting it makes a row-less legacy key resolve
58
+ * differently here than in `_readCompositionFromNode`. (SD-147101)
57
59
  * @returns Array of card element types in order
58
60
  */
59
- export declare function getCardComposition(currentNode: ImmutableHtmlNode | null | undefined): string[];
61
+ export declare function getCardComposition(currentNode: ImmutableHtmlNode | null | undefined, filterList?: FiltersResponse): string[];
60
62
  export interface RegenerateProductRowsOptions {
61
63
  currentNode: ImmutableHtmlNode | null | undefined;
62
64
  documentModifier: DocumentModifier;
@@ -43,22 +43,23 @@ export declare function resolveInlinePriceOrder(composition: string[]): {
43
43
  */
44
44
  export declare function toDisplayableAttributeValue(value: unknown): string | undefined;
45
45
  /**
46
- * Checks whether an attribute is a default (top-level) product attribute
47
- * vs a custom product attribute (nested under `product_attributes`).
48
- * Returns false when filterList is empty or the attribute is not found.
46
+ * Name-only type lookup **legacy fallback only**; the canonical path uses
47
+ * `isDefaultProductAttr` on the `product-attr` value. A bare name is the canonical
48
+ * form of a default attribute, so a duplicated name resolves to the default rather
49
+ * than to whichever entry the filter list happens to enumerate first. (SD-147101)
49
50
  */
50
51
  export declare function isDefaultAttribute(attrName: string, filterList: FiltersResponse): boolean;
51
52
  /**
52
- * Resolves the `product-attr` HTML attribute value based on attribute type from the filter list.
53
- * - Default attributes attribute name directly (e.g., "brand") → compiles to `{{1_0_brand}}`
54
- * - Product attributes → "product_attribute.<name>" (e.g., "product_attribute.rating_star")
53
+ * Name-only `product-attr` resolution **legacy fallback only**, see
54
+ * `isDefaultAttribute`. Prefer `filterToProductAttrValue`, which reads the type off
55
+ * the filter entry itself.
55
56
  */
56
57
  export declare function resolveProductAttrValue(attrName: string, filterList: FiltersResponse): string;
57
58
  /**
58
- * Reverse of `resolveProductAttrValue`: maps a cell's `product-attr` value back to its
59
- * `customAttr:<name>` composition key. The resolver only ever emits `<name>` (default
60
- * attribute) or `product_attribute.<name>` (custom product attribute), so stripping the
61
- * prefix recovers the attribute name without needing the store's filter list.
59
+ * Maps a cell's `product-attr` value to its composition key. The value is the
60
+ * canonical identity, so it is carried into the key verbatim — stripping the
61
+ * `product_attribute.` prefix here would collapse a default and a same-named
62
+ * custom attribute onto one key.
62
63
  */
63
64
  export declare function toCustomCompositionKey(productAttrValue: string): string;
64
65
  /**
@@ -184,7 +185,7 @@ export interface RenderOptions {
184
185
  * `RenderOptions`. Single source for both the stacked per-cell options and the two inline
185
186
  * (price + old-price) option sets, so style capture → re-bake stays consistent across layouts.
186
187
  */
187
- export declare function buildCellOptions(attrType: string, renderOptions: RenderOptions): CellRenderOptions;
188
+ export declare function buildCellOptions(attrType: string, renderOptions: RenderOptions, composition?: string[]): CellRenderOptions;
188
189
  /**
189
190
  * Options for prepareProductRows unified function
190
191
  */
@@ -0,0 +1,47 @@
1
+ import type { FiltersResponse } from '@@/Types/recommendation';
2
+ import type { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
3
+ /**
4
+ * Canonical identity of a card-composition custom attribute is its `product-attr`
5
+ * value — `brand` for a default attribute, `product_attribute.brand` for a custom
6
+ * one. A partner can own both under the same name, and only this form tells them
7
+ * apart. (SD-147101)
8
+ */
9
+ type FilterEntry = FiltersResponse[string];
10
+ export declare function isDefaultProductAttr(productAttrValue: string): boolean;
11
+ export declare function bareAttributeName(productAttrValue: string): string;
12
+ export declare function compositionKeyToProductAttr(compositionKey: string): string;
13
+ export declare function productAttrToCompositionKey(productAttrValue: string): string;
14
+ /**
15
+ * Canonical identity of a filter entry. Reads the type off the entry itself, so
16
+ * two same-named filters never collapse onto one value.
17
+ */
18
+ export declare function filterToProductAttrValue(filter: FilterEntry): string;
19
+ /**
20
+ * Filter entry behind a bare attribute name. A bare name is the canonical form of a
21
+ * DEFAULT attribute, so the default wins a name collision; the custom entry is only
22
+ * reached when it is the sole match (a genuinely legacy key). (SD-147101)
23
+ */
24
+ export declare function findFilterByBareName(bareName: string, filterList: FiltersResponse): FilterEntry | undefined;
25
+ export declare function normalizeCompositionKeys(composition: string[], blockNode?: ImmutableHtmlNode | null, filterList?: FiltersResponse): string[];
26
+ /**
27
+ * `data-custom-attributes` upgraded to canonical values. Index-preserving: the
28
+ * Nth entry must keep mapping to the Nth `customAttr:*` composition key.
29
+ */
30
+ export declare function normalizeCustomAttrValues(values: string[], blockNode?: ImmutableHtmlNode | null, filterList?: FiltersResponse): string[];
31
+ /**
32
+ * Composition key of an attribute row. Custom rows are keyed by their `product-attr`
33
+ * value, which stays correct on rows still tagged with a legacy bare name.
34
+ */
35
+ export declare function resolveRowCompositionKey(row: ImmutableHtmlNode): string | null;
36
+ /**
37
+ * Finds an attribute row by composition key, tolerating rows still tagged with the
38
+ * legacy bare-name key. Custom candidates are validated against their `product-attr`,
39
+ * so a legacy custom `brand` row is never mistaken for the default `brand`.
40
+ */
41
+ export declare function findAttributeRow(container: ImmutableHtmlNode, compositionKey: string): ImmutableHtmlNode | null;
42
+ /**
43
+ * Rewrites a copied row's legacy `data-attribute-type` to the canonical key so the
44
+ * DOM converges on the user's next composition change (no write on mere open).
45
+ */
46
+ export declare function rewriteLegacyRowKeyHtml(rowHtml: string, compositionKey: string): string;
47
+ export {};
@@ -34,6 +34,8 @@ interface SelectProps {
34
34
  placeholder: string;
35
35
  options: TextValueObject[];
36
36
  className?: string;
37
+ /** Adds the picker's built-in search box. Worth it once the list gets long. */
38
+ searchable?: boolean;
37
39
  }
38
40
  interface CounterProps {
39
41
  name: string;
@@ -105,7 +107,7 @@ export declare abstract class CommonControl extends Control {
105
107
  text: string;
106
108
  value: string;
107
109
  }): string;
108
- _GuSelect({ name, placeholder, options, className }: SelectProps): string;
110
+ _GuSelect({ name, placeholder, options, className, searchable, }: SelectProps): string;
109
111
  _GuTextInput({ name, placeholder, className, disabled }: TextInputProps): string;
110
112
  _GuCounter({ name, maxValue, minValue, step }: CounterProps): string;
111
113
  _GuRadioButtonItem({ text, value, icon }: {
@@ -36,4 +36,12 @@ export declare const useDynamicContentStore: import("pinia").StoreDefinition<"gu
36
36
  }[];
37
37
  }, {
38
38
  setSelectedDynamicContent(dynamicContent: DynamicContent): void;
39
+ /**
40
+ * Append-only bulk registration for dynamic content used by an
41
+ * AI-applied template. Unlike `setSelectedDynamicContent` it doesn't
42
+ * touch `selectedDynamicContent`; the getter removes duplicates, so
43
+ * re-registering the same entries is harmless. Items are shallow-copied so Pinia
44
+ * reactivity can't write a later mutation back into the host's catalog.
45
+ */
46
+ registerDynamicContent(items: DynamicContent[]): void;
39
47
  }>;
@@ -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 s = a("guidoDynamicContent", {
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
- s as useDynamicContentStore
33
+ c as useDynamicContentStore
24
34
  };
@@ -1,85 +1,88 @@
1
- import { productPairs as N } from "../extensions/Blocks/Items/enums/productEnums.js";
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
- function V(L) {
5
- const H = L.replaceAll("{%", "<!--{%").replaceAll("%}", "%}-->"), g = new DOMParser().parseFromString(H, "text/html"), m = N.PAIRS_FOR_EXTENSION;
6
- Object.entries(m).forEach(([r, f]) => {
7
- g.querySelectorAll(".ins-product-td").forEach((c) => {
8
- const A = c.getAttribute("data-number") || "1", y = c.getAttribute("data-type") || "CART_ITEMS";
9
- c.querySelectorAll(`[product-attr="${r}"]`).forEach((t) => {
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") || y, u = t.getAttribute("data-number") || A, d = f[T];
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, i = null;
16
- if (t.tagName === "IMG" ? (e = t, i = e.closest("a")) : (e = t.querySelector("img"), i = t.querySelector("a") || t.closest("a")), !e)
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 a = d.DEFAULT, s = d.ATTR;
19
- if (a && e.src) {
21
+ const i = d.DEFAULT, a = d.ATTR;
22
+ if (i && e.src) {
20
23
  const o = e.src;
21
- a.some((h) => {
22
- const l = h.split("/").pop() || "", $ = o.split("/").pop() || "";
23
- return o.includes(h) || o.includes(l) || $ === l;
24
- }) && (e.src = `{{${s}_${u}}}`);
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 = m.name) == null ? void 0 : F[T]) == null ? void 0 : I.ATTR;
27
- if (n && e.setAttribute("alt", `{{${n}_${u}}}`), i) {
28
- const o = (w = m.itemLink) == null ? void 0 : w[T];
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 E = o.HREF, h = o.DEFAULT_HREF || "#!", l = i.href;
31
- (l === "#" || l === "" || l.endsWith("#!") || l.endsWith(h) || l === `${window.location.href}${h}` || !l || l === window.location.href) && (i.href = `{{${E}_${u}}}`);
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, i = e.ATTR, a = e.DEFAULT_HREF || "#!", s = e.HREF;
38
- t.textContent && (t.innerHTML = C(t, `{{${i}_${u}}}`));
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 && s) {
41
- const o = n.href, E = `${window.location.href}${a}`;
42
- (o === E || o.endsWith(a)) && (n.href = `{{${s}_${u}}}`);
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, i = t.getAttribute("data-formated"), a = t.getAttribute("data-single_price"), s = i === "true", n = a === "true", o = t.getAttribute("data-curency") || "before";
49
- let E;
50
- n ? E = s ? e.SINGLE_PRICE_FORMATTED : e.SINGLE_PRICE : E = s ? e.PRICE_FORMATTED : e.PRICE;
51
- const h = e.CURRENCY;
52
- let l = `{{${E}_${u}}}`;
53
- const $ = (t.getAttribute("data-currency_symbol") || "").trim(), q = $ !== "" && $ !== O.productPriceCurrencySymbolControlValue;
54
- let b = "";
55
- q ? b = $ : h && (b = `{{${h}_${u}}}`), b && (l = o === "after" ? `${l} ${b}` : `${b} ${l}`), t.innerHTML = C(t, l);
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, i = e.ATTR, a = e.DEFAULT;
60
- t.textContent && t.textContent.trim() === a && (t.innerHTML = C(t, `{{${i}_${u}}}`));
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, i = e.HREF, a = e.DEFAULT_HREF || "#!", s = t.tagName === "A" ? t : t.querySelector("a");
65
- if (s) {
66
- const n = s.href || "", o = `${window.location.href}${a}`;
67
- (n === "" || n === "#" || n === o || n.endsWith(a) || n.endsWith("#!") || n === window.location.href) && (s.href = `{{${i}_${u}}}`);
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, i = e.HREF, a = e.DEFAULT_HREF || "#!", s = t;
73
- if (s.href) {
74
- const n = s.href, o = `${window.location.href}${a}`;
75
- (n === o || n.endsWith(a)) && (s.href = `{{${i}_${u}}}`);
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}_${u}}}`));
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 = g.querySelectorAll(".ins-product-td"), M = [];
94
+ ` : "", D = b.querySelectorAll(".ins-product-td"), M = [];
92
95
  D.forEach((r) => {
93
- const f = r.getAttribute("data-type") || "CART_ITEMS", p = r.getAttribute("data-number") || "1", c = r.getAttribute("data-nodup"), A = r.outerHTML;
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: A,
97
- type: f,
98
- number: p,
99
- nodup: c || void 0
99
+ outerHtml: l,
100
+ type: u,
101
+ number: p
100
102
  });
101
103
  });
102
- let _ = P + g.documentElement.outerHTML;
103
- M.reverse().forEach(({ outerHtml: r, type: f, number: p }) => {
104
- let c = "";
105
- switch (f) {
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
- c = "ins_apr_total_product_kind";
109
+ l = "ins_apr_total_product_kind";
108
110
  break;
109
111
  case "BROWSED_ITEMS":
110
- c = "browsed_item_total_product_kind";
112
+ l = "browsed_item_total_product_kind";
111
113
  break;
112
114
  case "PURCHASED_ITEMS":
113
- c = "purchased_item_total_product_kind";
115
+ l = "purchased_item_total_product_kind";
114
116
  break;
115
117
  }
116
- if (c) {
117
- const y = parseInt(p) - 1, T = `${`{% if ${c} > ${y} %}`}${r}{% endif %}`;
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 = g.querySelectorAll('[product-attr="originalPrice"][data-type="CART_ITEMS"]'), R = [];
123
+ const x = b.querySelectorAll('[product-attr="originalPrice"][data-type="CART_ITEMS"]'), S = [];
122
124
  return x.forEach((r) => {
123
- const f = r.getAttribute("data-number"), p = r.getAttribute("data-type");
124
- if (!f || p !== "CART_ITEMS")
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 c = r.closest(".product-original-price-class");
127
- if (c) {
128
- const A = c.outerHTML;
129
- R.some((S) => S.tdOuterHtml === A) || R.push({ tdOuterHtml: A, number: f });
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
- }), R.reverse().forEach(({ tdOuterHtml: r, number: f }) => {
132
- const p = `{% if ins_apr_price_${f} != ins_apr_originalprice_${f} %}`;
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 A = `${p}${r}{% endif %}`;
135
- _ = _.replace(r, A);
137
+ const h = `${p}${r}{% endif %}`;
138
+ _ = _.replace(r, h);
136
139
  }
137
140
  }), _.replaceAll("<!--{%", "{%").replaceAll("%}-->", "%}").replaceAll("&lt;!--{%", "{%").replaceAll("%}--&gt;", "%}");
138
141
  }
139
142
  export {
140
- V as pairProductVariables
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
+ };
@@ -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.14.0",
3
+ "version": "3.15.0-beta.0b5dc39",
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",