@useinsider/guido 3.10.0-beta.5013269 → 3.10.0-beta.511e745
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/composables/useCortexBlueprintBridge.js +7 -7
- package/dist/composables/useCustomInterfaceAppearance.js +16 -18
- package/dist/composables/useEmailTemplateApplier.js +9 -9
- package/dist/composables/useExport.js +18 -18
- package/dist/composables/useHtmlValidator.js +90 -90
- package/dist/composables/useStripo.js +53 -51
- package/dist/composables/useStripoNotifications.js +26 -0
- package/dist/composables/useToaster.js +11 -11
- package/dist/composables/useVersionHistoryApi.js +2 -2
- package/dist/composables/validators/useLiquidValidator.js +1 -1
- package/dist/composables/validators/useUnsubscribeBlockValidator.js +10 -10
- package/dist/config/compiler/recommendationCompilerRules.js +55 -38
- package/dist/config/compiler/utils/recommendationCompilerUtils.js +142 -100
- package/dist/enums/toaster.js +2 -2
- package/dist/extensions/Blocks/Checkbox/control.js +1 -1
- package/dist/extensions/Blocks/RadioButton/control.js +1 -1
- package/dist/extensions/Blocks/Recommendation/useRecommendationBlockWarning.js +7 -7
- package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMap.js +2 -0
- package/dist/services/blankTemplate.js +1 -1
- package/dist/services/templateLibraryApi.js +8 -7
- package/dist/src/composables/useStripoNotifications.d.ts +10 -0
- package/dist/src/enums/toaster.d.ts +2 -2
- package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.d.ts +1 -0
- package/dist/src/stores/toaster.d.ts +2 -3
- package/dist/stores/toaster.js +10 -10
- package/dist/utils/templatePreparation.js +20 -16
- package/package.json +2 -2
- package/dist/static/styles/components/notification.css.js +0 -74
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
import { useActionsApi as
|
|
2
|
-
import { useBlocksConfig as
|
|
3
|
-
import { useConfig as
|
|
4
|
-
import { useCustomInterfaceAppearance as
|
|
5
|
-
import { useFullStoryBridge as
|
|
6
|
-
import { useStripoEventHandler as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { useActionsApi as U } from "./useActionsApi.js";
|
|
2
|
+
import { useBlocksConfig as R } from "./useBlocksConfig.js";
|
|
3
|
+
import { useConfig as x } from "./useConfig.js";
|
|
4
|
+
import { useCustomInterfaceAppearance as O } from "./useCustomInterfaceAppearance.js";
|
|
5
|
+
import { useFullStoryBridge as q } from "./useFullStoryBridge.js";
|
|
6
|
+
import { useStripoEventHandler as H } from "./useStripoEventHandler.js";
|
|
7
|
+
import { useStripoNotifications as N } from "./useStripoNotifications.js";
|
|
8
|
+
import { useToaster as z } from "./useToaster.js";
|
|
9
|
+
import { localePatch as L } from "../config/i18n/index.js";
|
|
9
10
|
import { en as h } from "../config/i18n/en/index.js";
|
|
10
|
-
import { ToasterTypeOptions as
|
|
11
|
-
import { useStripoApi as
|
|
12
|
-
import
|
|
13
|
-
import { useEditorStore as
|
|
14
|
-
import { dynamicContentToMergeTags as
|
|
15
|
-
import
|
|
16
|
-
const
|
|
17
|
-
const { features: c, template: w, isFeatureEnabled: u } =
|
|
11
|
+
import { ToasterTypeOptions as $ } from "../enums/toaster.js";
|
|
12
|
+
import { useStripoApi as G } from "../services/stripoApi.js";
|
|
13
|
+
import J from "../static/styles/customEditorStyle.css.js";
|
|
14
|
+
import { useEditorStore as E } from "../stores/editor.js";
|
|
15
|
+
import { dynamicContentToMergeTags as X } from "../utils/genericUtil.js";
|
|
16
|
+
import j from "../package.json.js";
|
|
17
|
+
const fo = (C, l) => {
|
|
18
|
+
const { features: c, template: w, isFeatureEnabled: u } = x(), { handleError: m, showToaster: b } = z(), { getToken: T, getCustomFonts: k, getSyncModulesStatus: B } = G(), { handleEvent: F } = H(), { getStripoBlocksConfig: v } = R(), { getStripoNotifications: V } = N(), _ = async (s, i = [], r = !1) => {
|
|
18
19
|
var g, S, y;
|
|
19
|
-
const
|
|
20
|
+
const o = E(), { html: p, css: a } = s, { baseBlocks: t, extensions: d } = await v(), f = ((g = c.value) == null ? void 0 : g.displayConditions) ?? !0, M = ((S = c.value) == null ? void 0 : S.modulesDisabled) ?? !1, P = ((y = w.value) == null ? void 0 : y.forceRecreate) ?? !1;
|
|
20
21
|
await window.UIEditor.initEditor(
|
|
21
22
|
document.querySelector("#guido-editor"),
|
|
22
23
|
{
|
|
23
|
-
metadata:
|
|
24
|
+
metadata: C,
|
|
24
25
|
html: p,
|
|
25
26
|
css: a,
|
|
26
|
-
forceRecreate:
|
|
27
|
+
forceRecreate: P,
|
|
27
28
|
locale: "en",
|
|
28
29
|
undoButtonSelector: "#guido__undo-button",
|
|
29
30
|
redoButtonSelector: "#guido__redo-button",
|
|
@@ -33,12 +34,12 @@ const ce = (E, l) => {
|
|
|
33
34
|
customAppearanceMergetags: !u("liquidSyntax"),
|
|
34
35
|
customAppearanceMergetagsBorderColor: "#f1f3fe",
|
|
35
36
|
customAppearanceMergetagsBackgroundColor: "#f1f3fe",
|
|
36
|
-
customViewStyles:
|
|
37
|
+
customViewStyles: J,
|
|
37
38
|
conditionsEnabled: f,
|
|
38
39
|
customConditionsEnabled: f,
|
|
39
40
|
enableXSSSecurity: !0,
|
|
40
|
-
modulesDisabled:
|
|
41
|
-
syncModulesEnabled:
|
|
41
|
+
modulesDisabled: M,
|
|
42
|
+
syncModulesEnabled: r,
|
|
42
43
|
messageSettingsEnabled: !0,
|
|
43
44
|
displayGmailAnnotations: !0,
|
|
44
45
|
displayHiddenPreheader: !1,
|
|
@@ -51,82 +52,83 @@ const ce = (E, l) => {
|
|
|
51
52
|
editorFonts: {
|
|
52
53
|
showDefaultStandardFonts: !0,
|
|
53
54
|
showDefaultNotStandardFonts: !0,
|
|
54
|
-
customFonts:
|
|
55
|
+
customFonts: i
|
|
55
56
|
},
|
|
56
57
|
mergeTags: [
|
|
57
58
|
{
|
|
58
|
-
entries:
|
|
59
|
+
entries: X(
|
|
59
60
|
l.preselectedDynamicContentList,
|
|
60
61
|
u("liquidSyntax")
|
|
61
62
|
)
|
|
62
63
|
}
|
|
63
64
|
],
|
|
64
|
-
async onTokenRefreshRequest(
|
|
65
|
+
async onTokenRefreshRequest(e) {
|
|
65
66
|
try {
|
|
66
67
|
const n = await T();
|
|
67
|
-
|
|
68
|
+
e(n);
|
|
68
69
|
} catch (n) {
|
|
69
70
|
m(n, "Failed to refresh token");
|
|
70
71
|
}
|
|
71
72
|
},
|
|
72
73
|
onTemplateLoaded() {
|
|
73
74
|
try {
|
|
74
|
-
const { importCss:
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
const { importCss: e } = O(), { activateCustomViewStyles: n, updateTimerInClonedTemplate: D } = U(), { injectFullStory: I } = q();
|
|
76
|
+
e(), n(), I(), D(), l.onReady(), o.isStripoInitialized = !0, o.loadingStatus = !1, setTimeout(() => {
|
|
77
|
+
o.hasChanges = !1;
|
|
77
78
|
}, 1e3);
|
|
78
|
-
} catch (
|
|
79
|
-
m(
|
|
79
|
+
} catch (e) {
|
|
80
|
+
m(e, "Failed to load custom interface appearance");
|
|
80
81
|
}
|
|
81
82
|
},
|
|
82
|
-
onCodeEditorVisibilityChanged(
|
|
83
|
-
|
|
83
|
+
onCodeEditorVisibilityChanged(e) {
|
|
84
|
+
o.isCodeEditorOpen = e;
|
|
84
85
|
},
|
|
85
|
-
onEditorVisualModeChanged(
|
|
86
|
-
|
|
86
|
+
onEditorVisualModeChanged(e) {
|
|
87
|
+
o.editorVisualMode = e.toLowerCase();
|
|
87
88
|
},
|
|
88
|
-
onVersionHistoryVisibilityChanged(
|
|
89
|
-
|
|
89
|
+
onVersionHistoryVisibilityChanged(e) {
|
|
90
|
+
o.isVersionHistoryOpen = e;
|
|
90
91
|
},
|
|
91
92
|
onDataChanged() {
|
|
92
|
-
|
|
93
|
+
o.hasChanges = !0;
|
|
93
94
|
},
|
|
94
95
|
onEvent: F,
|
|
96
|
+
notifications: V(),
|
|
95
97
|
ignoreClickOutsideSelectors: [
|
|
96
98
|
"#guido-dynamic-content-modal",
|
|
97
99
|
".in-on-board-wrapper",
|
|
98
100
|
".in-drawer__container"
|
|
99
101
|
],
|
|
100
102
|
extensions: d,
|
|
101
|
-
localePatch:
|
|
103
|
+
localePatch: L
|
|
102
104
|
}
|
|
103
105
|
);
|
|
104
|
-
},
|
|
106
|
+
}, A = (s) => new Promise((i, r) => {
|
|
105
107
|
var d;
|
|
106
108
|
if (document.getElementById("UiEditorScript")) {
|
|
107
|
-
Promise.resolve(s()).then(
|
|
109
|
+
Promise.resolve(s()).then(i, r);
|
|
108
110
|
return;
|
|
109
111
|
}
|
|
110
|
-
const
|
|
112
|
+
const o = j.guido, a = `https://email-static.useinsider.com/guido/${(d = o == null ? void 0 : o.stripo) == null ? void 0 : d.version}/UIEditor.js`, t = document.createElement("script");
|
|
111
113
|
t.id = "UiEditorScript", t.type = "module", t.src = a, t.onload = () => {
|
|
112
|
-
Promise.resolve(s()).then(
|
|
114
|
+
Promise.resolve(s()).then(i, r);
|
|
113
115
|
}, t.onerror = () => {
|
|
114
|
-
|
|
116
|
+
r(new Error(`Failed to load Stripo UIEditor script from S3: ${a}`));
|
|
115
117
|
}, document.body.appendChild(t);
|
|
116
118
|
});
|
|
117
119
|
return { initPlugin: async (s) => {
|
|
118
|
-
const
|
|
120
|
+
const i = E();
|
|
119
121
|
try {
|
|
120
|
-
await
|
|
121
|
-
const [
|
|
122
|
+
await A(async () => {
|
|
123
|
+
const [r, o] = await Promise.all([
|
|
122
124
|
k(),
|
|
123
125
|
B()
|
|
124
126
|
]);
|
|
125
|
-
|
|
127
|
+
i.syncModulesEnabled = o, await _(s, r, o);
|
|
126
128
|
});
|
|
127
129
|
} catch {
|
|
128
|
-
|
|
129
|
-
type:
|
|
130
|
+
i.loadingStatus = !1, b({
|
|
131
|
+
type: $.Error,
|
|
130
132
|
message: h["An error has occurred. Please report the error code to support."],
|
|
131
133
|
actionButton: {
|
|
132
134
|
text: h["Reload page"],
|
|
@@ -137,5 +139,5 @@ const ce = (E, l) => {
|
|
|
137
139
|
} };
|
|
138
140
|
};
|
|
139
141
|
export {
|
|
140
|
-
|
|
142
|
+
fo as useStripo
|
|
141
143
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useToaster as d } from "./useToaster.js";
|
|
2
|
+
import { ToasterTypeOptions as c } from "../enums/toaster.js";
|
|
3
|
+
const g = () => {
|
|
4
|
+
const { showToaster: s, hideToaster: u } = d();
|
|
5
|
+
let r = null;
|
|
6
|
+
const n = (o, t, i, e, f = !0) => {
|
|
7
|
+
r = i;
|
|
8
|
+
const l = e != null && e.action ? { text: e.action.label, onClick: e.action.func } : void 0;
|
|
9
|
+
s({ type: o, message: t, actionButton: l, autoHide: (e == null ? void 0 : e.autoClose) ?? f });
|
|
10
|
+
};
|
|
11
|
+
return { getStripoNotifications: () => ({
|
|
12
|
+
info: (o, t, i) => n(c.Info, o, t, i),
|
|
13
|
+
success: (o, t, i) => n(c.Success, o, t, i),
|
|
14
|
+
warn: (o, t, i) => n(c.Warning, o, t, i),
|
|
15
|
+
error: (o, t, i) => n(c.Error, o, t, i),
|
|
16
|
+
// A loader marks an in-progress operation; Stripo dismisses it via hide(id)
|
|
17
|
+
// when finished, so it must not auto-hide.
|
|
18
|
+
loader: (o, t, i) => n(c.Info, o, t, i, !1),
|
|
19
|
+
hide: (o) => {
|
|
20
|
+
r === o && (u(), r = null);
|
|
21
|
+
}
|
|
22
|
+
}) };
|
|
23
|
+
};
|
|
24
|
+
export {
|
|
25
|
+
g as useStripoNotifications
|
|
26
|
+
};
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { ToasterTypeOptions as T } from "../enums/toaster.js";
|
|
2
2
|
import { useToasterStore as c } from "../stores/toaster.js";
|
|
3
|
-
import { watch as u, nextTick as
|
|
3
|
+
import { watch as u, nextTick as f } from "vue";
|
|
4
4
|
const p = () => {
|
|
5
|
-
const
|
|
6
|
-
let
|
|
5
|
+
const o = c();
|
|
6
|
+
let t = null;
|
|
7
7
|
const s = () => {
|
|
8
|
-
|
|
8
|
+
t && (clearTimeout(t), t = null);
|
|
9
9
|
};
|
|
10
10
|
function r(e = { message: "" }) {
|
|
11
|
-
s(),
|
|
12
|
-
|
|
11
|
+
s(), o.hideToaster(), f(() => {
|
|
12
|
+
o.showToaster(e);
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
function i() {
|
|
16
|
-
s(),
|
|
16
|
+
s(), o.hideToaster();
|
|
17
17
|
}
|
|
18
18
|
function n(e, a) {
|
|
19
19
|
const m = e instanceof Error ? e.message : String(e);
|
|
20
|
-
r({ type: T.
|
|
20
|
+
r({ type: T.Error, message: `${a}: ${m}` });
|
|
21
21
|
}
|
|
22
22
|
return u(
|
|
23
|
-
() =>
|
|
23
|
+
() => o.shouldAutoHide,
|
|
24
24
|
(e) => {
|
|
25
|
-
s(), e && (
|
|
26
|
-
|
|
25
|
+
s(), e && (t = setTimeout(() => {
|
|
26
|
+
o.hideToaster(), s();
|
|
27
27
|
}, 6e3));
|
|
28
28
|
}
|
|
29
29
|
), { showToaster: r, hideToaster: i, handleError: n };
|
|
@@ -24,7 +24,7 @@ const S = () => {
|
|
|
24
24
|
window.StripoEditorApi.versionHistoryApi.previewVersion(e, (o) => {
|
|
25
25
|
const r = o instanceof Error ? o.message : String(o);
|
|
26
26
|
s({
|
|
27
|
-
type: t.
|
|
27
|
+
type: t.Error,
|
|
28
28
|
message: `Error compiling email: ${r}`
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -41,7 +41,7 @@ const S = () => {
|
|
|
41
41
|
(o) => {
|
|
42
42
|
const r = o instanceof Error ? o.message : String(o);
|
|
43
43
|
s({
|
|
44
|
-
type: t.
|
|
44
|
+
type: t.Error,
|
|
45
45
|
message: `Error compiling email: ${r}`
|
|
46
46
|
});
|
|
47
47
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ToasterTypeOptions as i } from "../../enums/toaster.js";
|
|
2
2
|
import { PAGE_TYPES as u } from "../../enums/unsubscribe.js";
|
|
3
3
|
import { UNSUBSCRIBE_BLOCK_SELECTOR as l, DATA_ATTRIBUTES as a } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
|
|
4
|
-
import { useToaster as
|
|
5
|
-
import { useTranslations as
|
|
4
|
+
import { useToaster as E } from "../useToaster.js";
|
|
5
|
+
import { useTranslations as m } from "../useTranslations.js";
|
|
6
6
|
const T = [
|
|
7
7
|
{
|
|
8
8
|
pageType: u.GLOBAL_UNSUBSCRIBE,
|
|
@@ -13,23 +13,23 @@ const T = [
|
|
|
13
13
|
messageKey: "unsubscription-preference.duplicate-pref-center"
|
|
14
14
|
}
|
|
15
15
|
], g = "unsubscription-preference.duplicate-both", b = "unsubscription-preference.select-page-to-continue", P = () => {
|
|
16
|
-
const { showToaster: c } =
|
|
16
|
+
const { showToaster: c } = E(), p = m();
|
|
17
17
|
return { validateUnsubscribeBlockUniqueness: (n) => {
|
|
18
|
-
const o = new DOMParser().parseFromString(n, "text/html"),
|
|
18
|
+
const o = new DOMParser().parseFromString(n, "text/html"), r = /* @__PURE__ */ new Map();
|
|
19
19
|
o.querySelectorAll(l).forEach((e) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
20
|
+
const s = Number(e.getAttribute(a.PAGE_TYPE));
|
|
21
|
+
r.set(s, (r.get(s) ?? 0) + 1);
|
|
22
22
|
});
|
|
23
|
-
const t = T.filter((e) => (
|
|
23
|
+
const t = T.filter((e) => (r.get(e.pageType) ?? 0) > 1).map((e) => e.messageKey);
|
|
24
24
|
return t.length ? (c({
|
|
25
|
-
type: i.
|
|
25
|
+
type: i.Error,
|
|
26
26
|
message: p(t.length > 1 ? g : t[0])
|
|
27
27
|
}), !1) : !0;
|
|
28
28
|
}, validateUnsubscribeBlockHasTemplate: (n) => {
|
|
29
29
|
const o = new DOMParser().parseFromString(n, "text/html");
|
|
30
30
|
return Array.from(o.querySelectorAll(l)).some((t) => {
|
|
31
|
-
const e = t.getAttribute(a.PAGE_TYPE),
|
|
32
|
-
return !e || !
|
|
31
|
+
const e = t.getAttribute(a.PAGE_TYPE), s = t.getAttribute(a.PAGE_LIST);
|
|
32
|
+
return !e || !s;
|
|
33
33
|
}) ? (c({
|
|
34
34
|
type: i.Warning,
|
|
35
35
|
message: p(b)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { useRecommendation as N } from "../../composables/useRecommendation.js";
|
|
2
|
-
import { DUMMY_IMAGE_MAPPINGS as
|
|
2
|
+
import { DUMMY_IMAGE_MAPPINGS as h, REGEX as d, VerticalOrientation as $, CSS as n, ATTRIBUTES as p, CONDITIONS as A, HTML as g } from "../../enums/recommendation.js";
|
|
3
3
|
import { prepareRecommendationBlocks as O } from "./utils/recommendationCompilerUtils.js";
|
|
4
|
-
const
|
|
4
|
+
const H = [
|
|
5
5
|
{
|
|
6
6
|
id: "replace-images-with-variable-names",
|
|
7
7
|
description: "Replacing dummy images with variable names in recommendation module",
|
|
8
8
|
type: "custom",
|
|
9
|
-
processor: (
|
|
10
|
-
let e =
|
|
11
|
-
return Object.entries(
|
|
12
|
-
Object.entries(r).forEach(([
|
|
13
|
-
e = e.replaceAll(i, `{{${
|
|
9
|
+
processor: (t) => {
|
|
10
|
+
let e = t;
|
|
11
|
+
return Object.entries(h).forEach(([, r]) => {
|
|
12
|
+
Object.entries(r).forEach(([o, i]) => {
|
|
13
|
+
e = e.replaceAll(i, `{{${o}}}`);
|
|
14
14
|
});
|
|
15
15
|
}), e;
|
|
16
16
|
},
|
|
@@ -41,10 +41,10 @@ const U = [
|
|
|
41
41
|
id: "add-recommendation-unresponsive-css",
|
|
42
42
|
description: "Adding recommendation unresponsive css",
|
|
43
43
|
type: "custom",
|
|
44
|
-
processor: (
|
|
44
|
+
processor: (t) => {
|
|
45
45
|
const { calculateCardWidth: e, getRecommendationCampaignData: r } = N();
|
|
46
|
-
let
|
|
47
|
-
const i =
|
|
46
|
+
let o = t;
|
|
47
|
+
const i = o.match(d.ID);
|
|
48
48
|
if (i) {
|
|
49
49
|
const s = [];
|
|
50
50
|
if (i.forEach((c) => {
|
|
@@ -52,10 +52,10 @@ const U = [
|
|
|
52
52
|
u.textTrimming && u.orientation === $ && s.push(e(u));
|
|
53
53
|
}), s.length) {
|
|
54
54
|
const c = `width:${Math.min(...s)}px!important;`;
|
|
55
|
-
|
|
55
|
+
o = o.replace(n.REGULAR_NAME_HEIGHT, `${n.TRIMMED_NAME_HEIGHT} ${c} ${n.ELLIPSIS}`).replace(n.REGULAR_NAME_CONTAINER_HEIGHT, n.TRIMMED_NAME_CONTAINER_CSS).replace(n.RESPONSIVE_NAME_SIZE, `${n.RESPONSIVE_NAME_HEIGHT} ${c} ${n.ELLIPSIS}`).replace(n.RESPONSIVE_NAME_CONTAINER_HEIGHT, n.TRIMMED_RESPONSIVE_NAME_CONTAINER_CSS);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return o;
|
|
59
59
|
},
|
|
60
60
|
priority: 52
|
|
61
61
|
},
|
|
@@ -63,16 +63,16 @@ const U = [
|
|
|
63
63
|
id: "prepare-recommendations",
|
|
64
64
|
description: "Replacing product data with template variables in recommendation blocks",
|
|
65
65
|
type: "custom",
|
|
66
|
-
processor: (
|
|
66
|
+
processor: (t) => O(t),
|
|
67
67
|
priority: 48
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
id: "add-discount-conditions",
|
|
71
71
|
description: "Adding discount conditions to the recommendation block",
|
|
72
72
|
type: "custom",
|
|
73
|
-
processor: (
|
|
74
|
-
let e =
|
|
75
|
-
const r = e.match(d.ATTRIBUTE_PARAGRAPH), { getRecommendationCampaignData:
|
|
73
|
+
processor: (t) => {
|
|
74
|
+
let e = t;
|
|
75
|
+
const r = e.match(d.ATTRIBUTE_PARAGRAPH), { getRecommendationCampaignData: o } = N();
|
|
76
76
|
return r !== null && r.forEach((i) => {
|
|
77
77
|
const s = i.match(d.CUSTOM_FIELD);
|
|
78
78
|
if (!s)
|
|
@@ -80,17 +80,17 @@ const U = [
|
|
|
80
80
|
const [c] = s, m = c.match(d.CUSTOM_FIELD_INDEXES_PART), E = c.match(d.CUSTOM_FIELD_NAME_PART), u = i.match(d.ATTRIBUTE_PARAGRAPH_START_TAG);
|
|
81
81
|
if (!m || !E || !u)
|
|
82
82
|
return;
|
|
83
|
-
const [
|
|
83
|
+
const [T] = m, [I] = E, [_] = u, b = I.substring(1, I.length - 2), S = _.match(d.COMPOSITION) !== null;
|
|
84
84
|
let a = c;
|
|
85
85
|
if (S) {
|
|
86
|
-
const
|
|
86
|
+
const y = T.substring(2, T.length - 3), l = o(y);
|
|
87
87
|
b === p.OMNIBUS_PRICE && (l.priceBeforeTextValue && (a = `${l.priceBeforeTextValue}${a}`), l.priceAfterTextValue && (a = `${a}${l.priceAfterTextValue}`)), b === p.OMNIBUS_DISCOUNT && (l.discountBeforeTextValue && (a = `${l.discountBeforeTextValue}${a}`), l.discountAfterTextValue && (a = `${a}${l.discountAfterTextValue}`));
|
|
88
88
|
}
|
|
89
|
-
const
|
|
90
|
-
let
|
|
91
|
-
b in
|
|
92
|
-
const
|
|
93
|
-
e = e.replace(i,
|
|
89
|
+
const R = T.substring(2);
|
|
90
|
+
let f = "";
|
|
91
|
+
b in A.IF && (f = A.IF[b].replaceAll(`{${p.DISCOUNT}}`, `${R}${p.DISCOUNT}`).replaceAll(`{${p.OMNIBUS_DISCOUNT}}`, `${R}${p.OMNIBUS_DISCOUNT}`).replaceAll(`{${p.OMNIBUS_PRICE}}`, `${R}${p.OMNIBUS_PRICE}`));
|
|
92
|
+
const M = `${_}${a}${g.PARAGRAPH_END_TAG}`, C = `${f}${S ? M : i}${A.ELSE}${_}${g.PARAGRAPH_END_TAG}${A.END_IF}`;
|
|
93
|
+
e = e.replace(i, C);
|
|
94
94
|
}), e;
|
|
95
95
|
},
|
|
96
96
|
priority: 53
|
|
@@ -110,11 +110,11 @@ const U = [
|
|
|
110
110
|
type: "custom",
|
|
111
111
|
// Scoped to REC_START/REC_END markers so it cannot strip `product-attr`
|
|
112
112
|
// from Items block elements, which the items compiler rule depends on.
|
|
113
|
-
processor: (
|
|
113
|
+
processor: (t) => {
|
|
114
114
|
const e = /\s+(?:esd-extension-block-id|data-attribute-type|data-visibility|data-text-before|data-text-after|data-same-price|product-attr|composition)="[^"]*"/g;
|
|
115
|
-
return
|
|
115
|
+
return t.replace(
|
|
116
116
|
/<!--REC_START-->([\s\S]*?)<!--REC_END-->/g,
|
|
117
|
-
(r,
|
|
117
|
+
(r, o) => `<!--REC_START-->${o.replace(e, "")}<!--REC_END-->`
|
|
118
118
|
);
|
|
119
119
|
},
|
|
120
120
|
priority: 55
|
|
@@ -123,8 +123,8 @@ const U = [
|
|
|
123
123
|
id: "strip-unused-recommendation-classes",
|
|
124
124
|
description: "Remove CSS classes not referenced by any style rule from recommendation elements",
|
|
125
125
|
type: "custom",
|
|
126
|
-
processor: (
|
|
127
|
-
let e =
|
|
126
|
+
processor: (t) => {
|
|
127
|
+
let e = t.replace(
|
|
128
128
|
/ class="(?:product-card-segment|attribute-cell|recommendation-attribute-row|product-image|product-name|product-price|product-old-price|product-omnibus-price|product-omnibus-discount|product-button|recommendation-product-row|product-card-wrapper)"/g,
|
|
129
129
|
""
|
|
130
130
|
);
|
|
@@ -136,8 +136,8 @@ const U = [
|
|
|
136
136
|
id: "remove-empty-mobile-layout-artifacts",
|
|
137
137
|
description: "Remove empty mobile container rows and unused mobile layout CSS",
|
|
138
138
|
type: "custom",
|
|
139
|
-
processor: (
|
|
140
|
-
let e =
|
|
139
|
+
processor: (t) => {
|
|
140
|
+
let e = t;
|
|
141
141
|
return e = e.replace(
|
|
142
142
|
/<tr[^>]*class="ins-recommendation-mobile-row"[^>]*><\/tr>/g,
|
|
143
143
|
""
|
|
@@ -146,8 +146,8 @@ const U = [
|
|
|
146
146
|
""
|
|
147
147
|
), e = e.replace(
|
|
148
148
|
/@media[^{]*max-width\s*:\s*480px[^{]*\{((?:[^{}]*\{[^{}]*\})*[^{}]*)\}/g,
|
|
149
|
-
(r,
|
|
150
|
-
const i =
|
|
149
|
+
(r, o) => {
|
|
150
|
+
const i = o.match(/[^{}]+\{[^{}]*\}/g) || [], s = /ins-recommendation|product-image-cell|button-cell|product-info-cell/;
|
|
151
151
|
return i.every((m) => s.test(m)) ? "" : r;
|
|
152
152
|
}
|
|
153
153
|
)), e;
|
|
@@ -163,11 +163,11 @@ const U = [
|
|
|
163
163
|
// (SD-142395: split out of the removed deduplicate-inline-styles rule.)
|
|
164
164
|
description: "Force !important on es-button link color to defeat Gmail link-color override",
|
|
165
165
|
type: "custom",
|
|
166
|
-
processor: (
|
|
166
|
+
processor: (t) => t.replace(
|
|
167
167
|
/<a\b[^>]*\bes-button\b[^>]*>/g,
|
|
168
168
|
(e) => e.replace(
|
|
169
169
|
/([;"]color:)([^;"]+)/g,
|
|
170
|
-
(r,
|
|
170
|
+
(r, o, i) => i.includes("!important") ? r : `${o}${i} !important`
|
|
171
171
|
)
|
|
172
172
|
),
|
|
173
173
|
priority: 58
|
|
@@ -183,13 +183,30 @@ const U = [
|
|
|
183
183
|
// rather than classed, so they survive Gmail clipping.
|
|
184
184
|
description: "Collapse whitespace inside recommendation blocks and strip boundary markers",
|
|
185
185
|
type: "custom",
|
|
186
|
-
processor: (
|
|
187
|
-
const e = (r) => r.replace(/>\s+</g, "><").replace(/<[^>]+>/g, (
|
|
188
|
-
return
|
|
186
|
+
processor: (t) => {
|
|
187
|
+
const e = (r) => r.replace(/>\s+</g, "><").replace(/<[^>]+>/g, (o) => o.replace(/\s+/g, " "));
|
|
188
|
+
return t.replace(/<!--REC_START-->([\s\S]*?)<!--REC_END-->/g, (r, o) => e(o)).replaceAll("<!--REC_START-->", "").replaceAll("<!--REC_END-->", "");
|
|
189
189
|
},
|
|
190
190
|
priority: 99
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: "wrap-mobile-row-mso-conditional",
|
|
194
|
+
// SD-145666: OLD Outlook (Word engine) ignores @media and honors display:none
|
|
195
|
+
// on <tr>/<table> unreliably, so it renders BOTH the desktop AND the populated
|
|
196
|
+
// mobile recommendation container — products duplicate (1,2,1,2). prepare-
|
|
197
|
+
// recommendations (P48) tags each POPULATED mobile <tr> with safe placeholder
|
|
198
|
+
// comments; swap them here — AFTER minify (P99) has collapsed whitespace and
|
|
199
|
+
// stripped the REC markers — into a downlevel-revealed conditional comment so
|
|
200
|
+
// Outlook skips the row while every other client still renders it. Injecting the
|
|
201
|
+
// delicate `<!-- -->` ghost token LAST keeps it out of every whitespace/comment-
|
|
202
|
+
// mangling pass. email-service preserves this exact pattern (proven around
|
|
203
|
+
// es-button spans in delivered emails).
|
|
204
|
+
description: "Wrap populated recommendation mobile row in [if !mso] conditional comment for Outlook",
|
|
205
|
+
type: "custom",
|
|
206
|
+
processor: (t) => t.replaceAll("<!--MSO_MOBILE_HIDE_START-->", "<!--[if !mso]><!-- -->").replaceAll("<!--MSO_MOBILE_HIDE_END-->", "<!--<![endif]-->"),
|
|
207
|
+
priority: 100
|
|
191
208
|
}
|
|
192
209
|
];
|
|
193
210
|
export {
|
|
194
|
-
|
|
211
|
+
H as recommendationCompilerRules
|
|
195
212
|
};
|