@useinsider/guido 3.27.0-beta.ee85b86 → 3.27.1-beta.4ca8aae

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 (37) hide show
  1. package/README.md +5 -10
  2. package/dist/components/Guido.vue.js +7 -7
  3. package/dist/components/Guido.vue2.js +83 -86
  4. package/dist/composables/useHtmlCompiler.js +8 -9
  5. package/dist/composables/useModuleRepair.js +32 -63
  6. package/dist/composables/useStripo.js +43 -44
  7. package/dist/config/compiler/htmlCompilerRules.js +6 -16
  8. package/dist/config/migrator/unsubscribeMigrator.js +112 -100
  9. package/dist/extensions/Blocks/Recommendation/controls/image/index.js +58 -40
  10. package/dist/extensions/Blocks/Unsubscribe/block.js +104 -96
  11. package/dist/extensions/Blocks/Unsubscribe/utils/constants.js +4 -3
  12. package/dist/extensions/Blocks/Unsubscribe/utils/swapMergeTagInHref.js +5 -0
  13. package/dist/guido.css +1 -1
  14. package/dist/package.json.js +3 -3
  15. package/dist/src/@types/generic.d.ts +0 -7
  16. package/dist/src/composables/useModuleRepair.d.ts +0 -6
  17. package/dist/src/extensions/Blocks/Recommendation/controls/image/index.d.ts +2 -0
  18. package/dist/src/extensions/Blocks/Unsubscribe/utils/constants.d.ts +1 -0
  19. package/dist/src/extensions/Blocks/Unsubscribe/utils/swapMergeTagInHref.d.ts +1 -0
  20. package/dist/src/utils/dynamicContentConverter.d.ts +18 -20
  21. package/dist/src/utils/genericUtil.d.ts +8 -4
  22. package/dist/static/stripo-runtime-manifest.json.js +2 -2
  23. package/dist/utils/dynamicContentConverter.js +27 -41
  24. package/dist/utils/genericUtil.js +39 -39
  25. package/dist/utils/templatePreparation.js +9 -9
  26. package/package.json +2 -2
  27. package/dist/components/organisms/dynamic-content/AmbiguousDynamicContentModal.vue.js +0 -24
  28. package/dist/components/organisms/dynamic-content/AmbiguousDynamicContentModal.vue2.js +0 -59
  29. package/dist/composables/useAmbiguousDynamicContent.js +0 -72
  30. package/dist/enums/dynamic-content.js +0 -4
  31. package/dist/src/components/organisms/dynamic-content/AmbiguousDynamicContentModal.vue.d.ts +0 -2
  32. package/dist/src/composables/useAmbiguousDynamicContent.d.ts +0 -7
  33. package/dist/src/enums/dynamic-content.d.ts +0 -3
  34. package/dist/src/stores/module-dynamic-content.d.ts +0 -30
  35. package/dist/src/utils/dynamicContentPendingMarker.d.ts +0 -2
  36. package/dist/stores/module-dynamic-content.js +0 -20
  37. package/dist/utils/dynamicContentPendingMarker.js +0 -6
@@ -1,52 +1,51 @@
1
- import { useActionsApi as j } from "./useActionsApi.js";
2
- import { useAmbiguousDynamicContent as z } from "./useAmbiguousDynamicContent.js";
3
- import { useBlocksConfig as $ } from "./useBlocksConfig.js";
4
- import { useConfig as G } from "./useConfig.js";
1
+ import { useActionsApi as N } from "./useActionsApi.js";
2
+ import { useBlocksConfig as j } from "./useBlocksConfig.js";
3
+ import { useConfig as z } from "./useConfig.js";
5
4
  import { useCustomInterfaceAppearance as v } from "./useCustomInterfaceAppearance.js";
6
- import { useFullStoryBridge as X } from "./useFullStoryBridge.js";
7
- import { useModuleRepair as J } from "./useModuleRepair.js";
8
- import { useStripoEventHandler as K } from "./useStripoEventHandler.js";
9
- import { useStripoNotifications as Q } from "./useStripoNotifications.js";
10
- import { useToaster as W } from "./useToaster.js";
11
- import { localePatch as Y } from "../config/i18n/index.js";
5
+ import { useFullStoryBridge as $ } from "./useFullStoryBridge.js";
6
+ import { useModuleRepair as G } from "./useModuleRepair.js";
7
+ import { useStripoEventHandler as X } from "./useStripoEventHandler.js";
8
+ import { useStripoNotifications as J } from "./useStripoNotifications.js";
9
+ import { useToaster as K } from "./useToaster.js";
10
+ import { localePatch as Q } from "../config/i18n/index.js";
12
11
  import { en as B } from "../config/i18n/en/index.js";
13
- import { ToasterTypeOptions as Z } from "../enums/toaster.js";
14
- import { useStripoApi as ee } from "../services/stripoApi.js";
15
- import te from "../static/styles/customEditorStyle.css.js";
12
+ import { ToasterTypeOptions as W } from "../enums/toaster.js";
13
+ import { useStripoApi as Y } from "../services/stripoApi.js";
14
+ import Z from "../static/styles/customEditorStyle.css.js";
16
15
  import { useEditorStore as T } from "../stores/editor.js";
17
- import { buildMergeTagEntries as oe, flattenDynamicContentList as re } from "../utils/genericUtil.js";
18
- import { getStripoRuntimeBaseUrl as ie } from "../utils/stripoRuntime.js";
16
+ import { buildMergeTagEntries as ee, flattenDynamicContentList as te } from "../utils/genericUtil.js";
17
+ import { getStripoRuntimeBaseUrl as oe } from "../utils/stripoRuntime.js";
19
18
  let d = null, F = !1;
20
- const ve = (t) => {
21
- const { features: u, template: m, isFeatureEnabled: p } = G(), { handleError: f, showToaster: A } = W(), { getToken: P, getCustomFonts: R, getSyncModulesStatus: D } = ee(), { handleEvent: U } = K(), { handleModuleAdd: V } = J(), { restorePending: I } = z(), { getStripoBlocksConfig: L } = $(), { getStripoNotifications: q } = Q(), g = () => d || (d = new Promise((n, s) => {
19
+ const ke = (t) => {
20
+ const { features: u, template: m, isFeatureEnabled: p } = z(), { handleError: f, showToaster: A } = K(), { getToken: P, getCustomFonts: R, getSyncModulesStatus: U } = Y(), { handleEvent: V } = X(), { handleModuleAdd: D } = G(), { getStripoBlocksConfig: I } = j(), { getStripoNotifications: L } = J(), g = () => d || (d = new Promise((n, a) => {
22
21
  if (document.getElementById("UiEditorScript")) {
23
22
  n();
24
23
  return;
25
24
  }
26
- const i = ie();
25
+ const i = oe();
27
26
  if (!i) {
28
- s(
27
+ a(
29
28
  new Error("guido.stripo.version is not set in package.json. Please configure the Stripo version.")
30
29
  );
31
30
  return;
32
31
  }
33
- const a = `${i}UIEditor.js`, e = document.createElement("script");
34
- e.id = "UiEditorScript", e.type = "module", e.src = a, e.onload = () => {
32
+ const s = `${i}UIEditor.js`, e = document.createElement("script");
33
+ e.id = "UiEditorScript", e.type = "module", e.src = s, e.onload = () => {
35
34
  F = !0, n();
36
35
  }, e.onerror = () => {
37
- e.remove(), s(new Error(`Failed to load Stripo UIEditor script from S3: ${a}`));
36
+ e.remove(), a(new Error(`Failed to load Stripo UIEditor script from S3: ${s}`));
38
37
  }, document.body.appendChild(e);
39
38
  }), d.catch(() => {
40
39
  d = null;
41
- }), d), x = async (n, s, i, a = [], e = !1, c = !1) => {
40
+ }), d), q = async (n, a, i, s = [], e = !1, c = !1) => {
42
41
  var w, k, b, _;
43
- const o = T(), { html: S, css: y } = n, { baseBlocks: E, extensions: O } = await L();
42
+ const o = T(), { html: S, css: y } = n, { baseBlocks: E, extensions: x } = await I();
44
43
  t.mark("blocks_config_ready");
45
- const h = ((w = u.value) == null ? void 0 : w.displayConditions) ?? !0, H = ((k = u.value) == null ? void 0 : k.modulesDisabled) ?? !1, C = c || (((b = m.value) == null ? void 0 : b.forceRecreate) ?? !1), M = "https://email-static.useinsider.com https://edge.fullstory.com https://rs.fullstory.com";
44
+ const h = ((w = u.value) == null ? void 0 : w.displayConditions) ?? !0, O = ((k = u.value) == null ? void 0 : k.modulesDisabled) ?? !1, C = c || (((b = m.value) == null ? void 0 : b.forceRecreate) ?? !1), H = "https://email-static.useinsider.com https://edge.fullstory.com https://rs.fullstory.com";
46
45
  t.setTemplate({ html: S, css: y, forceRecreate: C }), v().importCss(), t.mark("init_editor"), await window.UIEditor.initEditor(
47
46
  document.querySelector("#guido-editor"),
48
47
  {
49
- metadata: s,
48
+ metadata: a,
50
49
  html: S,
51
50
  css: y,
52
51
  forceRecreate: C,
@@ -59,11 +58,11 @@ const ve = (t) => {
59
58
  customAppearanceMergetags: !p("liquidSyntax"),
60
59
  customAppearanceMergetagsBorderColor: "#f1f3fe",
61
60
  customAppearanceMergetagsBackgroundColor: "#f1f3fe",
62
- customViewStyles: te,
61
+ customViewStyles: Z,
63
62
  conditionsEnabled: h,
64
63
  customConditionsEnabled: h,
65
64
  enableXSSSecurity: !0,
66
- modulesDisabled: H,
65
+ modulesDisabled: O,
67
66
  syncModulesEnabled: e,
68
67
  messageSettingsEnabled: !0,
69
68
  displayGmailAnnotations: !0,
@@ -74,18 +73,18 @@ const ve = (t) => {
74
73
  copyPasteEnabled: !1,
75
74
  selectElementAfterDrop: !0,
76
75
  sameFontSizeForOutlook: !0,
77
- allowedScriptSourceDomains: M,
76
+ allowedScriptSourceDomains: H,
78
77
  ...E ? { baseBlocks: E } : {},
79
78
  editorFonts: {
80
79
  showDefaultStandardFonts: !0,
81
80
  showDefaultNotStandardFonts: !0,
82
- customFonts: a
81
+ customFonts: s
83
82
  },
84
83
  mergeTags: [
85
84
  {
86
- entries: oe(
85
+ entries: ee(
87
86
  i.preselectedDynamicContentList,
88
- re((_ = m.value) == null ? void 0 : _.dynamicContentList),
87
+ te((_ = m.value) == null ? void 0 : _.dynamicContentList),
89
88
  p("liquidSyntax")
90
89
  )
91
90
  }
@@ -101,8 +100,8 @@ const ve = (t) => {
101
100
  },
102
101
  onTemplateLoaded() {
103
102
  try {
104
- const { activateCustomViewStyles: r, updateTimerInClonedTemplate: l } = j(), { injectFullStory: N } = X();
105
- v().importCss(), r(), N(), I(), l(), i.onReady(), o.isStripoInitialized = !0, o.loadingStatus = !1, setTimeout(() => {
103
+ const { activateCustomViewStyles: r, updateTimerInClonedTemplate: l } = N(), { injectFullStory: M } = $();
104
+ v().importCss(), r(), M(), l(), i.onReady(), o.isStripoInitialized = !0, o.loadingStatus = !1, setTimeout(() => {
106
105
  o.hasChanges = !1;
107
106
  }, 1e3), t.markReady();
108
107
  } catch (r) {
@@ -121,9 +120,9 @@ const ve = (t) => {
121
120
  onDataChanged() {
122
121
  o.hasChanges = !0, o.changeSeq += 1;
123
122
  },
124
- onEvent: U,
125
- onModuleAdd: V,
126
- notifications: q(),
123
+ onEvent: V,
124
+ onModuleAdd: D,
125
+ notifications: L(),
127
126
  // Stripo deselects the block on any click outside the canvas, so every surface
128
127
  // that belongs to the block's own flow has to be listed here (SD-153273).
129
128
  ignoreClickOutsideSelectors: [
@@ -133,23 +132,23 @@ const ve = (t) => {
133
132
  ".in-modal-v2-wrapper",
134
133
  ".in-toasts-wrapper"
135
134
  ],
136
- extensions: O,
137
- localePatch: Y
135
+ extensions: x,
136
+ localePatch: Q
138
137
  }
139
138
  );
140
139
  };
141
- return { initPlugin: async (n, s, i, a = !1) => {
140
+ return { initPlugin: async (n, a, i, s = !1) => {
142
141
  const e = T();
143
142
  try {
144
143
  t.mark("plugin_start"), t.setScriptPreloaded(F), await g(), t.mark("script_ready");
145
144
  const [c, o] = await Promise.all([
146
145
  R(),
147
- D()
146
+ U()
148
147
  ]);
149
- t.mark("fonts_sync_ready"), e.syncModulesEnabled = o, await x(n, s, i, c, o, a);
148
+ t.mark("fonts_sync_ready"), e.syncModulesEnabled = o, await q(n, a, i, c, o, s);
150
149
  } catch {
151
150
  e.loadingStatus = !1, A({
152
- type: Z.Error,
151
+ type: W.Error,
153
152
  message: B["An error has occurred. Please report the error code to support."],
154
153
  actionButton: {
155
154
  text: B["Reload page"],
@@ -163,5 +162,5 @@ const ve = (t) => {
163
162
  } };
164
163
  };
165
164
  export {
166
- ve as useStripo
165
+ ke as useStripo
167
166
  };
@@ -1,7 +1,6 @@
1
- import { PENDING_MARKER_PATTERN as c } from "../../utils/dynamicContentPendingMarker.js";
2
- import { stripFontTags as l } from "../../utils/stripFontTags.js";
3
- import { trimHrefWhitespace as d } from "../../utils/trimHrefWhitespace.js";
4
- const y = [
1
+ import { stripFontTags as c } from "../../utils/stripFontTags.js";
2
+ import { trimHrefWhitespace as l } from "../../utils/trimHrefWhitespace.js";
3
+ const f = [
5
4
  {
6
5
  id: "fix-url-encoding-start",
7
6
  description: "Replace {%22 with %7B%22 to fix URL encoding",
@@ -60,14 +59,14 @@ const y = [
60
59
  id: "strip-font-tags",
61
60
  description: 'Unwrap browser-translate-injected <font dir="auto"> tags, keeping inner content; other <font> elements are left intact.',
62
61
  type: "custom",
63
- processor: (e) => l(e),
62
+ processor: (e) => c(e),
64
63
  priority: 15
65
64
  },
66
65
  {
67
66
  id: "trim-href-whitespace",
68
67
  description: "Trim leading/trailing whitespace and &nbsp; from every href value",
69
68
  type: "custom",
70
- processor: (e) => d(e),
69
+ processor: (e) => l(e),
71
70
  priority: 16
72
71
  },
73
72
  {
@@ -184,15 +183,6 @@ const y = [
184
183
  flags: "g",
185
184
  priority: 45
186
185
  },
187
- {
188
- id: "remove-dc-pending-marker",
189
- description: "Strip the editor-only pending dynamic-content choice marker from compiled output",
190
- type: "regex",
191
- pattern: c,
192
- replacement: "",
193
- flags: "g",
194
- priority: 46
195
- },
196
186
  {
197
187
  id: "remove-script-tags",
198
188
  // SD-145051: parity with HB HtmlCleaner.removeUnsafeHtmlTags — strip <script> from
@@ -218,5 +208,5 @@ const y = [
218
208
  }
219
209
  ];
220
210
  export {
221
- y as defaultHtmlCompilerRules
211
+ f as defaultHtmlCompilerRules
222
212
  };
@@ -1,146 +1,158 @@
1
- var x = Object.defineProperty;
2
- var B = (c, e, r) => e in c ? x(c, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : c[e] = r;
3
- var A = (c, e, r) => B(c, typeof e != "symbol" ? e + "" : e, r);
4
- import { MERGE_TAGS as f, PAGE_TYPES as p } from "../../enums/unsubscribe.js";
5
- import { UNSUBSCRIBE_BLOCK_SELECTOR as h, DATA_ATTRIBUTES as k } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
6
- import { useUnsubscribeStore as C } from "../../stores/unsubscribe.js";
7
- class $ {
1
+ var _ = Object.defineProperty;
2
+ var $ = (f, e, t) => e in f ? _(f, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : f[e] = t;
3
+ var k = (f, e, t) => $(f, typeof e != "symbol" ? e + "" : e, t);
4
+ import { MERGE_TAGS as c, PAGE_TYPES as T } from "../../enums/unsubscribe.js";
5
+ import { UNSUBSCRIBE_BLOCK_SELECTOR as y, DATA_ATTRIBUTES as d, UNSUBSCRIBE_MERGE_TAG_REGEX as h } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
6
+ import { swapMergeTagInHref as B } from "../../extensions/Blocks/Unsubscribe/utils/swapMergeTagInHref.js";
7
+ import { parsePageList as P } from "../../extensions/Blocks/Unsubscribe/utils/utils.js";
8
+ import { useUnsubscribeStore as M } from "../../stores/unsubscribe.js";
9
+ import { escapeHtml as A } from "../../utils/htmlEscape.js";
10
+ const S = "unsubscribe", N = "You can ", R = " from our emails, if you need to.", C = "{-{-UNSUBSCRIBE-LINK-}-}";
11
+ class H {
8
12
  constructor() {
9
- A(this, "parser");
13
+ k(this, "parser");
10
14
  this.parser = new DOMParser();
11
15
  }
12
16
  async migrate(e) {
13
17
  try {
14
- const r = this.parser.parseFromString(e, "text/html"), t = r.querySelectorAll("td.unsubscribe-block");
15
- if (t.length) {
16
- const s = C();
17
- await s.fetchTemplates(), this.migrateLegacyBlocks(t, s);
18
+ const t = this.parser.parseFromString(e, "text/html"), r = t.querySelectorAll("td.unsubscribe-block");
19
+ if (r.length) {
20
+ const i = M();
21
+ await i.fetchTemplates(), this.migrateLegacyBlocks(r, i, this.collectBlockTags(t, i));
18
22
  }
19
- const n = this.repairUnsubscribeLinks(r);
20
- return !t.length && !n ? e : r.documentElement.outerHTML;
21
- } catch (r) {
22
- return console.error("UnsubscribeMigrator failed:", r), e;
23
+ const s = this.repairUnsubscribeLinks(t);
24
+ return !r.length && !s ? e : t.documentElement.outerHTML;
25
+ } catch (t) {
26
+ return console.error("UnsubscribeMigrator failed:", t), e;
23
27
  }
24
28
  }
25
- migrateLegacyBlocks(e, r) {
26
- e.forEach((t) => {
27
- const n = t.getAttribute("id"), s = this.extractPaddingClass(t), l = t.getAttribute("bgcolor") || "", a = this.extractUnsubscribeContent(t), o = this.getCollectionTypeFromMergeTag(a.linkHref), b = r.getSelectedUnsubscribePagesByCollection(o);
29
+ migrateLegacyBlocks(e, t, r) {
30
+ e.forEach((s) => {
31
+ const i = s.getAttribute("id"), n = this.extractPaddingClass(s), o = s.getAttribute("bgcolor") || "", a = this.extractUnsubscribeContent(s, r), b = this.getCollectionTypeFromMergeTag(a.linkHref), u = t.getSelectedUnsubscribePagesByCollection(b);
28
32
  console.debug("[UnsubscribeMigrator]", {
29
33
  mergeTag: a.linkHref,
30
- collectionType: o,
31
- pageIds: b,
32
- paddingClass: s,
33
- bgColor: l
34
+ collectionType: b,
35
+ pageIds: u,
36
+ paddingClass: n,
37
+ bgColor: o
34
38
  });
35
- const u = this.buildUnsubscribeBlock(
39
+ const l = this.buildUnsubscribeBlock(
36
40
  a,
37
- o,
38
41
  b,
39
- s,
40
- l
41
- ), i = this.parser.parseFromString(
42
- `<table id="tempDoc"><tbody><tr>${u}</tr></tbody></table>`,
42
+ u,
43
+ n,
44
+ o
45
+ ), g = this.parser.parseFromString(
46
+ `<table id="tempDoc"><tbody><tr>${l}</tr></tbody></table>`,
43
47
  "text/html"
44
- ).querySelector(h);
45
- i && t.parentNode && (i.setAttribute("id", n || ""), t.parentNode.replaceChild(i, t));
48
+ ).querySelector(y);
49
+ g && s.parentNode && (g.setAttribute("id", i || ""), s.parentNode.replaceChild(g, s));
46
50
  });
47
51
  }
52
+ // The compiler resolves a typed tag only through a block that lists pages, so a page-less block carries no tag.
53
+ collectBlockTags(e, t) {
54
+ const r = /* @__PURE__ */ new Set();
55
+ return e.querySelectorAll(y).forEach((s) => {
56
+ var a, b, u;
57
+ if (!P(s.getAttribute(d.PAGE_LIST) || "").length)
58
+ return;
59
+ const i = Number(s.getAttribute(d.PAGE_TYPE)), n = c[i];
60
+ if (n) {
61
+ r.add(n);
62
+ return;
63
+ }
64
+ const o = (u = (b = (a = this.findLinkCandidates(s).primary) == null ? void 0 : a.getAttribute("href")) == null ? void 0 : b.match(h)) == null ? void 0 : u[0];
65
+ o && r.add(c[this.getCollectionTypeFromMergeTag(o)]);
66
+ }), e.querySelectorAll("td.unsubscribe-block").forEach((s) => {
67
+ const i = this.getCollectionTypeFromMergeTag(this.getLegacyLinkTag(s));
68
+ t.getSelectedUnsubscribePagesByCollection(i).length && r.add(c[i]);
69
+ }), r;
70
+ }
71
+ // Mirrors extractUnsubscribeContent: without a <p> the block migrates on the default tag.
72
+ getLegacyLinkTag(e) {
73
+ var r, s;
74
+ const { primary: t } = this.findLinkCandidates(e);
75
+ return !t || !(t.closest("p") || e.querySelector("p")) ? c.default : ((s = (r = t.getAttribute("href")) == null ? void 0 : r.match(h)) == null ? void 0 : s[0]) ?? c.default;
76
+ }
48
77
  // Re-derives the typed merge tag from data-unsubscribe-page-type for any v2
49
78
  // block whose anchor href is missing or the legacy default. Returns true when
50
79
  // at least one href was rewritten. Typed tags and custom URLs are left intact.
51
80
  repairUnsubscribeLinks(e) {
52
- const r = e.querySelectorAll(`${h}[${k.PAGE_TYPE}]`);
53
- let t = !1;
54
- return r.forEach((n) => {
55
- const s = n.querySelector('a[data-unsubscribe-link="true"], a.unsubscribe-link');
56
- if (!s)
81
+ const t = e.querySelectorAll(`${y}[${d.PAGE_TYPE}]`);
82
+ let r = !1;
83
+ return t.forEach((s) => {
84
+ const i = s.querySelector('a[data-unsubscribe-link="true"], a.unsubscribe-link');
85
+ if (!i)
57
86
  return;
58
- const l = s.getAttribute("href") || "";
59
- if (l && l !== f.default)
87
+ const n = i.getAttribute("href") || "";
88
+ if (n && n !== c.default)
60
89
  return;
61
- const a = Number(n.getAttribute(k.PAGE_TYPE)), o = f[a];
62
- o && (s.setAttribute("href", o), t = !0);
63
- }), t;
90
+ const o = Number(s.getAttribute(d.PAGE_TYPE)), a = c[o];
91
+ a && (i.setAttribute("href", a), r = !0);
92
+ }), r;
64
93
  }
65
94
  extractPaddingClass(e) {
66
- const t = (e.getAttribute("class") || "").match(/\bes-p\d+\b/);
67
- return t ? t[0] : "";
95
+ const r = (e.getAttribute("class") || "").match(/\bes-p\d+\b/);
96
+ return r ? r[0] : "";
97
+ }
98
+ hasVisibleContent(e) {
99
+ var t;
100
+ return !!((t = e.textContent) != null && t.trim() || e.querySelector("img"));
101
+ }
102
+ findLinkCandidates(e) {
103
+ const t = Array.from(/* @__PURE__ */ new Set([
104
+ ...Array.from(e.querySelectorAll("a.unsubscribe-link")),
105
+ ...Array.from(e.querySelectorAll('a[href*="unsubscribe"]'))
106
+ ])), r = (n) => h.test(n.getAttribute("href") || ""), s = (n) => this.hasVisibleContent(n), i = t.find((n) => s(n) && r(n)) ?? t.find((n) => s(n) && n.classList.contains("unsubscribe-link")) ?? t.find(r) ?? t.find(s) ?? t[0];
107
+ return { candidates: t, primary: i };
68
108
  }
69
- extractUnsubscribeContent(e) {
70
- var d;
71
- const r = e.querySelector("a.unsubscribe-link") || e.querySelector('a[href*="unsubscribe"]'), t = (r == null ? void 0 : r.closest("p")) || e.querySelector("p");
72
- if (!t)
73
- return {
74
- textBefore: "You can ",
75
- linkText: "unsubscribe",
76
- linkHref: "{{ins-unsubscribe-link}}",
77
- linkStyle: "",
78
- textAfter: " from our emails, if you need to.",
79
- isBold: !1,
80
- isItalic: !1,
81
- align: "center",
82
- paragraphStyle: ""
83
- };
84
- const n = e.getAttribute("align") || t.getAttribute("align") || "center", s = t.getAttribute("style") || "";
85
- if (!r)
86
- return {
87
- textBefore: "You can ",
88
- linkText: "unsubscribe",
89
- linkHref: "{{ins-unsubscribe-link}}",
90
- linkStyle: "",
91
- textAfter: " from our emails, if you need to.",
92
- isBold: !1,
93
- isItalic: !1,
94
- align: n,
95
- paragraphStyle: s
96
- };
97
- const l = ((d = r.textContent) == null ? void 0 : d.trim()) || "unsubscribe", a = r.getAttribute("href") || "{{ins-unsubscribe-link}}", o = r.getAttribute("style") || "", b = t.textContent || "", u = b.indexOf(l), g = u >= 0 ? b.substring(0, u).trim() : "You can ", i = u >= 0 ? b.substring(u + l.length).trim() : " from our emails, if you need to.", m = !!t.querySelector("b, strong"), y = !!t.querySelector("i, em");
98
- return {
99
- textBefore: g,
100
- linkText: l,
101
- linkHref: this.normalizeUnsubscribeHref(a),
102
- linkStyle: o,
103
- textAfter: i,
104
- isBold: m,
105
- isItalic: y,
106
- align: n,
107
- paragraphStyle: s
109
+ extractUnsubscribeContent(e, t) {
110
+ const r = e.cloneNode(!0), { candidates: s, primary: i } = this.findLinkCandidates(r), n = (i == null ? void 0 : i.closest("p")) ?? null, o = n || r.querySelector("p"), a = {
111
+ footerHtml: "",
112
+ linkText: S,
113
+ linkHref: c.default,
114
+ linkStyle: "",
115
+ align: "center",
116
+ paragraphStyle: ""
108
117
  };
118
+ if (!o || (a.align = e.getAttribute("align") || o.getAttribute("align") || "center", n || (a.paragraphStyle = o.getAttribute("style") || ""), !i))
119
+ return a;
120
+ a.linkText = this.hasVisibleContent(i) ? i.innerHTML.trim() : S, a.linkHref = this.normalizeUnsubscribeHref(i.getAttribute("href") || c.default), a.linkStyle = i.getAttribute("style") || "";
121
+ const b = this.getCollectionTypeFromMergeTag(a.linkHref), u = c[b];
122
+ return r.querySelectorAll("a[href]").forEach((l) => {
123
+ var m;
124
+ const p = l.getAttribute("href") || "", g = (m = p.match(h)) == null ? void 0 : m[0];
125
+ g && l.setAttribute("href", B(p, t.has(g) ? g : u));
126
+ }), s.filter((l) => l !== i && !this.hasVisibleContent(l)).forEach((l) => l.replaceWith(...Array.from(l.childNodes))), i.replaceWith(r.ownerDocument.createTextNode(C)), a.footerHtml = r.innerHTML.trim(), a;
109
127
  }
110
- buildUnsubscribeBlock(e, r, t, n, s) {
111
- const l = e.linkStyle ? ` style="${e.linkStyle}"` : "", u = `<a ${`href="${Object.values(f).includes(e.linkHref) ? f[r] : e.linkHref}" class="unsubscribe-link" target="_blank" data-unsubscribe-link="true"`}${l}>${e.linkText}</a>`, g = [];
112
- e.textBefore && g.push(e.textBefore), g.push(u), e.textAfter && g.push(e.textAfter);
113
- let i = g.join(" ");
114
- e.isBold && e.isItalic ? i = `<strong path="1,0"><em path="1,0,0">${i}</em></strong>` : e.isBold ? i = `<strong path="1,0">${i}</strong>` : e.isItalic && (i = `<em path="1,0">${i}</em>`);
115
- const m = e.align ? ` align="${e.align}"` : "", y = s ? ` bgcolor="${s}"` : "", d = e.paragraphStyle ? ` style="${e.paragraphStyle}"` : "", T = t.length > 0 ? ` ${k.PAGE_LIST}="${t.join(",")}"` : "", E = ` ${k.PAGE_TYPE}="${r}"`, S = "esd-block-text unsubscribe-block-v2 esd-block-unsubscribe esd-unsubscribe-block esd-extension-block";
128
+ buildUnsubscribeBlock(e, t, r, s, i) {
129
+ const n = e.linkStyle ? ` style="${A(e.linkStyle)}"` : "", a = Object.values(c).includes(e.linkHref) ? c[t] : e.linkHref, u = `<a ${`href="${A(a)}" class="unsubscribe-link" target="_blank" data-unsubscribe-link="true"`}${n}>${e.linkText}</a>`, l = e.paragraphStyle ? ` style="${A(e.paragraphStyle)}"` : "", p = e.footerHtml ? e.footerHtml.replace(C, () => u) : `<p path="1"${l}>${N}${u}${R}</p>`, g = e.align ? ` align="${e.align}"` : "", m = i ? ` bgcolor="${i}"` : "", L = r.length > 0 ? ` ${d.PAGE_LIST}="${r.join(",")}"` : "", U = ` ${d.PAGE_TYPE}="${t}"`, E = "esd-block-text unsubscribe-block-v2 esd-block-unsubscribe esd-unsubscribe-block esd-extension-block";
116
130
  return `
117
131
  <td
118
132
  data-migration="true"
119
133
  esd-extension-block-id="unsubscribe-block"
120
- class="${n ? `${S} ${n}` : S}"
121
- ${m}${y}${E}${T}
134
+ class="${s ? `${E} ${s}` : E}"
135
+ ${g}${m}${U}${L}
122
136
  >
123
- <p path="1"${d}>
124
- ${i}
125
- </p>
137
+ ${p}
126
138
  </td>
127
139
  `;
128
140
  }
129
141
  getCollectionTypeFromMergeTag(e) {
130
- return e === f[p.SUBSCRIPTION_PREFERENCE_CENTER] ? p.SUBSCRIPTION_PREFERENCE_CENTER : e === f[p.GLOBAL_UNSUBSCRIBE] ? p.GLOBAL_UNSUBSCRIBE : p.GLOBAL_UNSUBSCRIBE;
142
+ return e === c[T.SUBSCRIPTION_PREFERENCE_CENTER] ? T.SUBSCRIPTION_PREFERENCE_CENTER : e === c[T.GLOBAL_UNSUBSCRIBE] ? T.GLOBAL_UNSUBSCRIBE : T.GLOBAL_UNSUBSCRIBE;
131
143
  }
132
144
  normalizeUnsubscribeHref(e) {
133
- if (Object.values(f).includes(e))
145
+ if (Object.values(c).includes(e))
134
146
  return e;
135
- const t = e.match(/{{ins-(?:global-)?(?:preferences-)?unsubscribe-link}}/);
136
- return t ? t[0] : e.includes("unsubscribe") || e.includes("unsub") ? (console.warn(
147
+ const r = e.match(/{{ins-(?:global-)?(?:preferences-)?unsubscribe-link}}/);
148
+ return r ? r[0] : e.includes("unsubscribe") || e.includes("unsub") ? (console.warn(
137
149
  `[UnsubscribeMigrator] Unrecognized unsubscribe href format: "${e}". Defaulting to {{ins-unsubscribe-link}}`
138
150
  ), "{{ins-unsubscribe-link}}") : e;
139
151
  }
140
152
  }
141
- async function M(c) {
142
- return new $().migrate(c);
153
+ async function Y(f) {
154
+ return new H().migrate(f);
143
155
  }
144
156
  export {
145
- M as migrateUnsubscribe
157
+ Y as migrateUnsubscribe
146
158
  };
@@ -1,79 +1,97 @@
1
- import { EditorStatePropertyType as E, PreviewDeviceMode as I, ModificationDescription as h } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
- import { createImageMarginsControl as M, createImageSizeControl as C } from "../../../controlFactories.js";
1
+ import { EditorStatePropertyType as M, PreviewDeviceMode as I, ModificationDescription as g } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
+ import { createImageMarginsControl as C, createImageSizeControl as O } from "../../../controlFactories.js";
3
3
  import { RecommendationBlockId as c } from "../../constants/blockIds.js";
4
- import { RecommendationControlId as g } from "../../constants/controlIds.js";
5
- import { BLOCK_ROOT_SELECTOR as l } from "../../constants/selectors.js";
6
- import { hasGetStyle as a, hasGetAttribute as O } from "../../utils/tagName.js";
7
- const R = { width: "", height: "auto", "max-height": "" }, f = `${l} img.adapt-img`, x = `*${l.slice(1)}`, p = `@media only screen and (max-width: 600px) { ${f} { max-height: none !important; } }`;
8
- function _(t) {
4
+ import { RecommendationControlId as f } from "../../constants/controlIds.js";
5
+ import { BLOCK_ROOT_SELECTOR as l, MOBILE_CONTAINER_SELECTOR as R } from "../../constants/selectors.js";
6
+ import { hasGetStyle as a, hasGetAttribute as T } from "../../utils/tagName.js";
7
+ const _ = { width: "", height: "auto", "max-height": "" }, E = `${l} img.adapt-img`, b = `*${l.slice(1)}`, p = `@media only screen and (max-width: 600px) { ${E} { max-height: none !important; } }`;
8
+ function x(t) {
9
9
  return { width: "auto", height: "auto", "max-height": `${t}px` };
10
10
  }
11
11
  function A(t) {
12
- return t.querySelectorAll(x).some((e) => "getSelector" in e && e.getSelector() === f);
12
+ return t.querySelectorAll(b).some((e) => "getSelector" in e && e.getSelector() === E);
13
13
  }
14
- function T(t, e) {
15
- return a(t) && Object.entries(e).some(([o, n]) => (t.getStyle(o) ?? "") !== n);
14
+ function B(t, e) {
15
+ return a(t) && Object.entries(e).some(([o, i]) => (t.getStyle(o) ?? "") !== i);
16
16
  }
17
17
  function S(t) {
18
18
  const e = t.getDocumentRootCssNode();
19
19
  return e && !A(e) ? e : null;
20
20
  }
21
- function H(t, e) {
22
- const n = e.querySelectorAll("img").some((r) => a(r) && !!r.getStyle("max-height")) ? S(t) : null;
23
- if (!n)
21
+ function U(t, e) {
22
+ const i = e.querySelectorAll("img").some((r) => a(r) && !!r.getStyle("max-height")) ? S(t) : null;
23
+ if (!i)
24
24
  return;
25
- const i = t.getDocumentModifier();
26
- i.modifyCss(n).appendRule(p), i.apply(new h("Add the reco image mobile fit rule"));
25
+ const n = t.getDocumentModifier();
26
+ n.modifyCss(i).appendRule(p), n.apply(new g("Add the reco image mobile fit rule"));
27
27
  }
28
- const B = `[esd-extension-block-id="${c.IMAGE}"]`, L = /^\d+px$/;
28
+ const L = `[esd-extension-block-id="${c.IMAGE}"]`, v = /^\d+px$/;
29
29
  function y(t) {
30
- return O(t) && Number(t.getAttribute("height")) || 0;
30
+ return T(t) && Number(t.getAttribute("height")) || 0;
31
31
  }
32
- function b(t) {
32
+ function w(t) {
33
33
  const e = y(t), o = a(t) ? t.getStyle("max-height") ?? "" : "";
34
- return L.test(o) && e > 0 && o !== `${e}px`;
34
+ return v.test(o) && e > 0 && o !== `${e}px`;
35
35
  }
36
- function U(t, e) {
37
- const o = e.querySelectorAll(B).map((i) => i.querySelector("img")).filter(b);
36
+ function Z(t, e) {
37
+ const o = e.querySelectorAll(L).map((n) => n.querySelector("img")).filter(w);
38
38
  if (!o.length)
39
39
  return;
40
- const n = t.getDocumentModifier();
41
- o.forEach((i) => n.modifyHtml(i).setStyle("max-height", `${y(i)}px`)), n.apply(new h("Sync the reco image height bound").asHidden());
40
+ const i = t.getDocumentModifier();
41
+ o.forEach((n) => i.modifyHtml(n).setStyle("max-height", `${y(n)}px`)), i.apply(new g("Sync the reco image height bound").asHidden());
42
+ }
43
+ function h(t) {
44
+ try {
45
+ return !!t.closest(R);
46
+ } catch {
47
+ return null;
48
+ }
42
49
  }
43
- class D extends C(
44
- g.IMAGE_SIZE,
50
+ class D extends O(
51
+ f.IMAGE_SIZE,
45
52
  c.IMAGE,
46
53
  l
47
54
  ) {
55
+ // Stripo derives the mobile size from rendered targets, and the container hidden in mobile preview renders 0px.
56
+ getTargetNodes(e) {
57
+ const o = super.getTargetNodes(e);
58
+ if (!this.isMobilePreview())
59
+ return o;
60
+ const i = h(e);
61
+ return i === null ? [] : o.filter((n) => h(n) === i);
62
+ }
48
63
  getAdditionalModifications(e) {
49
- if (this.api.getEditorState()[E.previewDeviceMode] === I.MOBILE)
64
+ if (this.isMobilePreview())
50
65
  return;
51
- const o = this.getFitStyles(), n = this.getTargetNodes(e).map((s) => s.querySelector("img")).filter((s) => T(s, o)), i = o["max-height"] ? S(this.api) : null;
52
- if (!n.length && !i)
66
+ const o = this.getFitStyles(), i = this.getTargetNodes(e).map((s) => s.querySelector("img")).filter((s) => B(s, o)), n = o["max-height"] ? S(this.api) : null;
67
+ if (!i.length && !n)
53
68
  return;
54
69
  const r = this.api.getDocumentModifier();
55
- return i && r.modifyCss(i).appendRule(p), n.forEach((s) => {
56
- const m = r.modifyHtml(s);
57
- Object.entries(o).forEach(([u, d]) => {
58
- d ? m.setStyle(u, d) : m.removeStyle(u);
70
+ return n && r.modifyCss(n).appendRule(p), i.forEach((s) => {
71
+ const u = r.modifyHtml(s);
72
+ Object.entries(o).forEach(([d, m]) => {
73
+ m ? u.setStyle(d, m) : u.removeStyle(d);
59
74
  });
60
75
  }), r;
61
76
  }
77
+ isMobilePreview() {
78
+ return this.api.getEditorState()[M.previewDeviceMode] === I.MOBILE;
79
+ }
62
80
  getFitStyles() {
63
81
  const { size: e } = this.api.getValues();
64
- return (e == null ? void 0 : e.type) === "height" && e.value ? _(e.value) : R;
82
+ return (e == null ? void 0 : e.type) === "height" && e.value ? x(e.value) : _;
65
83
  }
66
84
  }
67
- const F = M(
68
- g.IMAGE_MARGINS,
85
+ const N = C(
86
+ f.IMAGE_MARGINS,
69
87
  c.IMAGE,
70
88
  l
71
- ), P = {
89
+ ), j = {
72
90
  size: D,
73
- margins: F
91
+ margins: N
74
92
  };
75
93
  export {
76
- P as ImageControls,
77
- H as ensureMobileFitRule,
78
- U as syncHeightBound
94
+ j as ImageControls,
95
+ U as ensureMobileFitRule,
96
+ Z as syncHeightBound
79
97
  };