@templatical/quality 0.7.3 → 0.8.1
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/index.d.ts +206 -61
- package/dist/index.js +691 -258
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,41 +1,54 @@
|
|
|
1
|
-
import { HEADING_LEVEL_FONT_SIZE as e, isButton as t,
|
|
2
|
-
import { Parser as
|
|
1
|
+
import { HEADING_LEVEL_FONT_SIZE as e, isButton as t, isHtml as n, isImage as r, isMenu as i, isParagraph as a, isSection as o, isSocialIcons as s, isTable as ee, isTitle as c, isVideo as te } from "@templatical/types";
|
|
2
|
+
import { Parser as l } from "htmlparser2";
|
|
3
3
|
//#region \0rolldown/runtime.js
|
|
4
|
-
var
|
|
4
|
+
var u = Object.defineProperty, d = (e, t) => {
|
|
5
5
|
let n = {};
|
|
6
|
-
for (var r in e)
|
|
6
|
+
for (var r in e) u(n, r, {
|
|
7
7
|
get: e[r],
|
|
8
8
|
enumerable: !0
|
|
9
9
|
});
|
|
10
|
-
return t ||
|
|
11
|
-
},
|
|
10
|
+
return t || u(n, Symbol.toStringTag, { value: "Module" }), n;
|
|
11
|
+
}, f = {
|
|
12
12
|
altMaxLength: 125,
|
|
13
13
|
minFontSize: 14,
|
|
14
14
|
allCapsMinLength: 20,
|
|
15
15
|
minTouchTargetPx: 44
|
|
16
|
-
}
|
|
16
|
+
}, p = [
|
|
17
|
+
"localhost",
|
|
18
|
+
"127.0.0.1",
|
|
19
|
+
"0.0.0.0",
|
|
20
|
+
"*.local",
|
|
21
|
+
"*.staging.*",
|
|
22
|
+
"*.dev.*"
|
|
23
|
+
];
|
|
17
24
|
//#endregion
|
|
18
25
|
//#region src/contrast.ts
|
|
19
|
-
function
|
|
26
|
+
function m(e, t) {
|
|
20
27
|
let n = h(e), r = h(t);
|
|
21
28
|
if (!n || !r) return NaN;
|
|
22
29
|
let i = _(n), a = _(r), o = Math.max(i, a), s = Math.min(i, a);
|
|
23
30
|
return (o + .05) / (s + .05);
|
|
24
31
|
}
|
|
25
|
-
var
|
|
32
|
+
var ne = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, re = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i, ie = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
|
|
26
33
|
function h(e) {
|
|
27
34
|
if (typeof e != "string") return null;
|
|
28
|
-
let t = e.trim(), n =
|
|
29
|
-
if (n) return {
|
|
35
|
+
let t = e.trim(), n = ie.exec(t);
|
|
36
|
+
if (n) return n[4].toLowerCase() === "ff" ? {
|
|
30
37
|
r: parseInt(n[1], 16),
|
|
31
38
|
g: parseInt(n[2], 16),
|
|
32
39
|
b: parseInt(n[3], 16)
|
|
40
|
+
} : null;
|
|
41
|
+
let r = re.exec(t);
|
|
42
|
+
if (r) return {
|
|
43
|
+
r: parseInt(r[1], 16),
|
|
44
|
+
g: parseInt(r[2], 16),
|
|
45
|
+
b: parseInt(r[3], 16)
|
|
33
46
|
};
|
|
34
|
-
let
|
|
35
|
-
return
|
|
36
|
-
r: parseInt(
|
|
37
|
-
g: parseInt(
|
|
38
|
-
b: parseInt(
|
|
47
|
+
let i = ne.exec(t);
|
|
48
|
+
return i ? {
|
|
49
|
+
r: parseInt(i[1] + i[1], 16),
|
|
50
|
+
g: parseInt(i[2] + i[2], 16),
|
|
51
|
+
b: parseInt(i[3] + i[3], 16)
|
|
39
52
|
} : null;
|
|
40
53
|
}
|
|
41
54
|
function g(e) {
|
|
@@ -50,18 +63,20 @@ function v(e) {
|
|
|
50
63
|
}
|
|
51
64
|
//#endregion
|
|
52
65
|
//#region src/walk.ts
|
|
53
|
-
var
|
|
54
|
-
function
|
|
55
|
-
let n = g(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() :
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
var ae = "#ffffff";
|
|
67
|
+
function y(e, t) {
|
|
68
|
+
let n = g(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() : ae, r = (e, n) => {
|
|
69
|
+
let i = e.styles?.backgroundColor, a = g(i) ? i.toLowerCase() : n.resolvedBackgroundColor;
|
|
70
|
+
t(e, a === n.resolvedBackgroundColor ? n : {
|
|
71
|
+
...n,
|
|
72
|
+
resolvedBackgroundColor: a
|
|
73
|
+
}), o(e) && e.children.forEach((t, i) => {
|
|
59
74
|
t.forEach((t) => r(t, {
|
|
60
75
|
parent: e,
|
|
61
76
|
section: e,
|
|
62
77
|
columnIndex: i,
|
|
63
78
|
depth: n.depth + 1,
|
|
64
|
-
resolvedBackgroundColor:
|
|
79
|
+
resolvedBackgroundColor: a
|
|
65
80
|
}));
|
|
66
81
|
});
|
|
67
82
|
};
|
|
@@ -74,122 +89,190 @@ function b(e, t) {
|
|
|
74
89
|
});
|
|
75
90
|
}
|
|
76
91
|
//#endregion
|
|
92
|
+
//#region src/run-rules.ts
|
|
93
|
+
function b(e, t, n, r) {
|
|
94
|
+
let i = [];
|
|
95
|
+
function a(e, t, i) {
|
|
96
|
+
return {
|
|
97
|
+
blockId: i.blockId,
|
|
98
|
+
ruleId: e,
|
|
99
|
+
severity: t,
|
|
100
|
+
message: r(n.locale, e, i.params),
|
|
101
|
+
fix: i.fix
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
y(e, (e, r) => {
|
|
105
|
+
for (let o of t) {
|
|
106
|
+
let t = n.severity(o.meta.id);
|
|
107
|
+
if (t === "off" || !o.block) continue;
|
|
108
|
+
let s = o.block(e, r, n);
|
|
109
|
+
s !== null && i.push(a(o.meta.id, t, s));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
for (let r of t) {
|
|
113
|
+
let t = n.severity(r.meta.id);
|
|
114
|
+
if (t === "off" || !r.template) continue;
|
|
115
|
+
let o = r.template(e, n);
|
|
116
|
+
for (let e of o) i.push(a(r.meta.id, t, e));
|
|
117
|
+
}
|
|
118
|
+
return i;
|
|
119
|
+
}
|
|
120
|
+
function x(e) {
|
|
121
|
+
let t = e.overrides ?? {}, n = {
|
|
122
|
+
...f,
|
|
123
|
+
...e.thresholds ?? {}
|
|
124
|
+
}, r = { nonProductionHosts: e.nonProductionHosts ?? p }, i = e.locale ?? "en", a = e.rules;
|
|
125
|
+
return {
|
|
126
|
+
locale: i,
|
|
127
|
+
rules: t,
|
|
128
|
+
thresholds: n,
|
|
129
|
+
links: r,
|
|
130
|
+
severity: (e) => {
|
|
131
|
+
let n = t[e];
|
|
132
|
+
return n === void 0 ? a.find((t) => t.meta.id === e)?.meta.severity ?? "warning" : n;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function oe(e, t, n) {
|
|
137
|
+
return x({
|
|
138
|
+
locale: e,
|
|
139
|
+
rules: n,
|
|
140
|
+
overrides: t.rules,
|
|
141
|
+
thresholds: t.thresholds,
|
|
142
|
+
nonProductionHosts: void 0
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function se(e, t, n) {
|
|
146
|
+
return x({
|
|
147
|
+
locale: e,
|
|
148
|
+
rules: n,
|
|
149
|
+
overrides: t.rules,
|
|
150
|
+
thresholds: void 0,
|
|
151
|
+
nonProductionHosts: void 0
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function ce(e, t, n) {
|
|
155
|
+
return x({
|
|
156
|
+
locale: e,
|
|
157
|
+
rules: n,
|
|
158
|
+
overrides: t.rules,
|
|
159
|
+
thresholds: void 0,
|
|
160
|
+
nonProductionHosts: t.nonProductionHosts
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
77
164
|
//#region src/accessibility/messages/de.ts
|
|
78
|
-
var
|
|
79
|
-
"img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.",
|
|
80
|
-
"img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.",
|
|
81
|
-
"img-alt-too-long": "Alt-Text ist {length} Zeichen lang; bleibe unter {max}.",
|
|
82
|
-
"img-decorative-needs-empty-alt": "Dekoratives Bild hat Alt-Text. Entferne den Alt-Text oder hebe die Markierung als dekorativ auf.",
|
|
83
|
-
"img-linked-no-context": "Verlinktes Bild beschreibt nur das Motiv, nicht das Linkziel. Nenne das Ziel (z. B. „Frühlingssale ansehen“).",
|
|
84
|
-
"heading-empty": "Überschrift ist leer. Füge Text hinzu oder entferne den Block.",
|
|
85
|
-
"heading-skip-level": "Überschrift springt von H{from} auf H{to}. Eine Ebene pro Schritt.",
|
|
86
|
-
"heading-multiple-h1": "E-Mail enthält mehr als eine H1. Verwende H1 nur einmal für die Hauptüberschrift.",
|
|
87
|
-
"link-empty": "Ein Link in diesem Block hat keinen Text und kein beschriebenes Bild.",
|
|
88
|
-
"link-vague-text": "Link-Text „{text}“ ist unspezifisch. Beschreibe stattdessen das Ziel.",
|
|
89
|
-
"link-href-empty": "Ein Link in diesem Block hat ein leeres oder „#“-href.",
|
|
90
|
-
"link-target-blank-no-rel": "Link öffnet in neuem Tab, aber rel=\"noopener\" fehlt – ergänze es, damit das Ziel nicht auf window.opener zugreifen kann.",
|
|
91
|
-
"text-all-caps": "Längere Texte in Großbuchstaben sind schwerer lesbar. Verwende Groß- und Kleinschreibung.",
|
|
92
|
-
"text-low-contrast": "Überschriftskontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
93
|
-
"text-too-small": "Text ist {size}px; mindestens {min}px verwenden.",
|
|
94
|
-
"button-vague-label": "Button-Beschriftung „{text}“ ist unspezifisch. Beschreibe die Aktion.",
|
|
95
|
-
"button-touch-target": "Button ist etwa {height}px hoch; mindestens {min}px verwenden, um Fehltipper auf Mobilgeräten zu vermeiden.",
|
|
96
|
-
"button-low-contrast": "Buttontextkontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
97
|
-
"missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an."
|
|
98
|
-
},
|
|
99
|
-
"img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.",
|
|
100
|
-
"img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.",
|
|
101
|
-
"img-alt-too-long": "Alt text is {length} characters; aim for under {max}.",
|
|
102
|
-
"img-decorative-needs-empty-alt": "Decorative image has alt text. Either clear the alt text or unmark the image as decorative.",
|
|
103
|
-
"img-linked-no-context": "Linked image alt describes the image but not the link destination. Include where the link goes (e.g. 'Buy spring sale').",
|
|
104
|
-
"heading-empty": "Heading is empty. Add text or remove the block.",
|
|
105
|
-
"heading-skip-level": "Heading jumps from H{from} to H{to}. Step one level at a time.",
|
|
106
|
-
"heading-multiple-h1": "Email has more than one H1. Use H1 once for the main heading.",
|
|
107
|
-
"link-empty": "A link in this block has no text and no described image.",
|
|
108
|
-
"link-vague-text": "Link text \"{text}\" is vague. Describe the destination instead.",
|
|
109
|
-
"link-href-empty": "A link in this block has an empty or '#' href.",
|
|
110
|
-
"link-target-blank-no-rel": "Link opens in a new tab but is missing rel=\"noopener\" — add it to prevent the destination from accessing window.opener.",
|
|
111
|
-
"text-all-caps": "Long all-caps text is harder to read for everyone. Use sentence case.",
|
|
112
|
-
"text-low-contrast": "Heading contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
113
|
-
"text-too-small": "Text is {size}px; aim for at least {min}px.",
|
|
114
|
-
"button-vague-label": "Button label \"{text}\" is vague. Describe the action.",
|
|
115
|
-
"button-touch-target": "Button is roughly {height}px tall; aim for at least {min}px to avoid mis-taps on mobile.",
|
|
116
|
-
"button-low-contrast": "Button text contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
117
|
-
"missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block."
|
|
118
|
-
},
|
|
119
|
-
"./de.ts":
|
|
120
|
-
"./en.ts":
|
|
121
|
-
}),
|
|
122
|
-
for (let e in
|
|
165
|
+
var le = /* @__PURE__ */ d({ default: () => ue }), ue = {
|
|
166
|
+
"a11y.img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.",
|
|
167
|
+
"a11y.img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.",
|
|
168
|
+
"a11y.img-alt-too-long": "Alt-Text ist {length} Zeichen lang; bleibe unter {max}.",
|
|
169
|
+
"a11y.img-decorative-needs-empty-alt": "Dekoratives Bild hat Alt-Text. Entferne den Alt-Text oder hebe die Markierung als dekorativ auf.",
|
|
170
|
+
"a11y.img-linked-no-context": "Verlinktes Bild beschreibt nur das Motiv, nicht das Linkziel. Nenne das Ziel (z. B. „Frühlingssale ansehen“).",
|
|
171
|
+
"a11y.heading-empty": "Überschrift ist leer. Füge Text hinzu oder entferne den Block.",
|
|
172
|
+
"a11y.heading-skip-level": "Überschrift springt von H{from} auf H{to}. Eine Ebene pro Schritt.",
|
|
173
|
+
"a11y.heading-multiple-h1": "E-Mail enthält mehr als eine H1. Verwende H1 nur einmal für die Hauptüberschrift.",
|
|
174
|
+
"a11y.link-empty": "Ein Link in diesem Block hat keinen Text und kein beschriebenes Bild.",
|
|
175
|
+
"a11y.link-vague-text": "Link-Text „{text}“ ist unspezifisch. Beschreibe stattdessen das Ziel.",
|
|
176
|
+
"a11y.link-href-empty": "Ein Link in diesem Block hat ein leeres oder „#“-href.",
|
|
177
|
+
"a11y.link-target-blank-no-rel": "Link öffnet in neuem Tab, aber rel=\"noopener\" fehlt – ergänze es, damit das Ziel nicht auf window.opener zugreifen kann.",
|
|
178
|
+
"a11y.text-all-caps": "Längere Texte in Großbuchstaben sind schwerer lesbar. Verwende Groß- und Kleinschreibung.",
|
|
179
|
+
"a11y.text-low-contrast": "Überschriftskontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
180
|
+
"a11y.text-too-small": "Text ist {size}px; mindestens {min}px verwenden.",
|
|
181
|
+
"a11y.button-vague-label": "Button-Beschriftung „{text}“ ist unspezifisch. Beschreibe die Aktion.",
|
|
182
|
+
"a11y.button-touch-target": "Button ist etwa {height}px hoch; mindestens {min}px verwenden, um Fehltipper auf Mobilgeräten zu vermeiden.",
|
|
183
|
+
"a11y.button-low-contrast": "Buttontextkontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
184
|
+
"a11y.missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an."
|
|
185
|
+
}, de = /* @__PURE__ */ d({ default: () => S }), S = {
|
|
186
|
+
"a11y.img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.",
|
|
187
|
+
"a11y.img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.",
|
|
188
|
+
"a11y.img-alt-too-long": "Alt text is {length} characters; aim for under {max}.",
|
|
189
|
+
"a11y.img-decorative-needs-empty-alt": "Decorative image has alt text. Either clear the alt text or unmark the image as decorative.",
|
|
190
|
+
"a11y.img-linked-no-context": "Linked image alt describes the image but not the link destination. Include where the link goes (e.g. 'Buy spring sale').",
|
|
191
|
+
"a11y.heading-empty": "Heading is empty. Add text or remove the block.",
|
|
192
|
+
"a11y.heading-skip-level": "Heading jumps from H{from} to H{to}. Step one level at a time.",
|
|
193
|
+
"a11y.heading-multiple-h1": "Email has more than one H1. Use H1 once for the main heading.",
|
|
194
|
+
"a11y.link-empty": "A link in this block has no text and no described image.",
|
|
195
|
+
"a11y.link-vague-text": "Link text \"{text}\" is vague. Describe the destination instead.",
|
|
196
|
+
"a11y.link-href-empty": "A link in this block has an empty or '#' href.",
|
|
197
|
+
"a11y.link-target-blank-no-rel": "Link opens in a new tab but is missing rel=\"noopener\" — add it to prevent the destination from accessing window.opener.",
|
|
198
|
+
"a11y.text-all-caps": "Long all-caps text is harder to read for everyone. Use sentence case.",
|
|
199
|
+
"a11y.text-low-contrast": "Heading contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
200
|
+
"a11y.text-too-small": "Text is {size}px; aim for at least {min}px.",
|
|
201
|
+
"a11y.button-vague-label": "Button label \"{text}\" is vague. Describe the action.",
|
|
202
|
+
"a11y.button-touch-target": "Button is roughly {height}px tall; aim for at least {min}px to avoid mis-taps on mobile.",
|
|
203
|
+
"a11y.button-low-contrast": "Button text contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
204
|
+
"a11y.missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block."
|
|
205
|
+
}, C = /* @__PURE__ */ Object.assign({
|
|
206
|
+
"./de.ts": le,
|
|
207
|
+
"./en.ts": de
|
|
208
|
+
}), w = {};
|
|
209
|
+
for (let e in C) {
|
|
123
210
|
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
124
211
|
if (!t) continue;
|
|
125
212
|
let n = t[1];
|
|
126
|
-
n !== "index" && (
|
|
213
|
+
n !== "index" && (w[n] = C[e].default);
|
|
127
214
|
}
|
|
128
|
-
var
|
|
129
|
-
function
|
|
130
|
-
return
|
|
215
|
+
var fe = Object.keys(w);
|
|
216
|
+
function T(e) {
|
|
217
|
+
return w[e.split("-")[0]?.toLowerCase() ?? "en"] ?? w.en ?? S;
|
|
131
218
|
}
|
|
132
|
-
function
|
|
133
|
-
let r =
|
|
219
|
+
function E(e, t, n) {
|
|
220
|
+
let r = T(e)[t] ?? S[t];
|
|
134
221
|
return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
|
|
135
222
|
let r = n[t];
|
|
136
223
|
return r === void 0 ? `{${t}}` : String(r);
|
|
137
224
|
}) : r;
|
|
138
225
|
}
|
|
139
|
-
var
|
|
226
|
+
var pe = {
|
|
140
227
|
meta: {
|
|
141
|
-
id: "img-missing-alt",
|
|
228
|
+
id: "a11y.img-missing-alt",
|
|
142
229
|
severity: "error"
|
|
143
230
|
},
|
|
144
231
|
block(e) {
|
|
145
|
-
return !
|
|
232
|
+
return !r(e) || e.decorative === !0 || (e.alt?.trim() ?? "") !== "" || (e.src ?? "").trim() === "" ? null : { blockId: e.id };
|
|
146
233
|
}
|
|
147
|
-
},
|
|
148
|
-
id: "img-alt-is-filename",
|
|
234
|
+
}, me = {
|
|
235
|
+
id: "a11y.img-alt-is-filename",
|
|
149
236
|
severity: "warning"
|
|
150
|
-
},
|
|
237
|
+
}, he = [
|
|
151
238
|
/\.(jpe?g|png|gif|webp|svg)$/i,
|
|
152
239
|
/^IMG[_-]?\d+/i,
|
|
153
240
|
/^Untitled/i,
|
|
154
241
|
/^Screen[\s_-]?Shot/i,
|
|
155
242
|
/^DSC[_-]?\d+/i
|
|
156
|
-
],
|
|
157
|
-
meta:
|
|
243
|
+
], ge = {
|
|
244
|
+
meta: me,
|
|
158
245
|
block(e) {
|
|
159
|
-
if (!
|
|
246
|
+
if (!r(e) || e.decorative === !0) return null;
|
|
160
247
|
let t = e.alt?.trim() ?? "";
|
|
161
|
-
return t === "" || !
|
|
248
|
+
return t === "" || !he.some((e) => e.test(t)) ? null : {
|
|
162
249
|
blockId: e.id,
|
|
163
|
-
params: { alt: t }
|
|
164
|
-
fix: {
|
|
165
|
-
description: "Clear alt text",
|
|
166
|
-
apply: (t) => t.updateBlock(e.id, { alt: "" })
|
|
167
|
-
}
|
|
250
|
+
params: { alt: t }
|
|
168
251
|
};
|
|
169
252
|
}
|
|
170
|
-
},
|
|
253
|
+
}, _e = {
|
|
171
254
|
meta: {
|
|
172
|
-
id: "img-alt-too-long",
|
|
255
|
+
id: "a11y.img-alt-too-long",
|
|
173
256
|
severity: "warning"
|
|
174
257
|
},
|
|
175
|
-
block(e, t,
|
|
176
|
-
if (!
|
|
258
|
+
block(e, t, n) {
|
|
259
|
+
if (!r(e) || e.decorative === !0) return null;
|
|
177
260
|
let i = e.alt ?? "";
|
|
178
|
-
return i.length <=
|
|
261
|
+
return i.length <= n.thresholds.altMaxLength ? null : {
|
|
179
262
|
blockId: e.id,
|
|
180
263
|
params: {
|
|
181
264
|
length: i.length,
|
|
182
|
-
max:
|
|
265
|
+
max: n.thresholds.altMaxLength
|
|
183
266
|
}
|
|
184
267
|
};
|
|
185
268
|
}
|
|
186
|
-
},
|
|
269
|
+
}, ve = {
|
|
187
270
|
meta: {
|
|
188
|
-
id: "img-decorative-needs-empty-alt",
|
|
271
|
+
id: "a11y.img-decorative-needs-empty-alt",
|
|
189
272
|
severity: "info"
|
|
190
273
|
},
|
|
191
274
|
block(e) {
|
|
192
|
-
return !
|
|
275
|
+
return !r(e) || e.decorative !== !0 || (e.alt ?? "").trim() === "" ? null : {
|
|
193
276
|
blockId: e.id,
|
|
194
277
|
fix: {
|
|
195
278
|
description: "Clear alt text",
|
|
@@ -197,7 +280,7 @@ var A = {
|
|
|
197
280
|
}
|
|
198
281
|
};
|
|
199
282
|
}
|
|
200
|
-
},
|
|
283
|
+
}, ye = /* @__PURE__ */ d({ default: () => be }), be = {
|
|
201
284
|
vagueLinkText: [
|
|
202
285
|
"hier klicken",
|
|
203
286
|
"hier",
|
|
@@ -240,7 +323,7 @@ var A = {
|
|
|
240
323
|
"anschauen",
|
|
241
324
|
"jetzt"
|
|
242
325
|
]
|
|
243
|
-
},
|
|
326
|
+
}, xe = /* @__PURE__ */ d({ default: () => Se }), Se = {
|
|
244
327
|
vagueLinkText: [
|
|
245
328
|
"click here",
|
|
246
329
|
"here",
|
|
@@ -281,49 +364,49 @@ var A = {
|
|
|
281
364
|
"redeem",
|
|
282
365
|
"watch"
|
|
283
366
|
]
|
|
284
|
-
},
|
|
285
|
-
"./de.ts":
|
|
286
|
-
"./en.ts":
|
|
287
|
-
}),
|
|
288
|
-
for (let e in
|
|
367
|
+
}, D = /* @__PURE__ */ Object.assign({
|
|
368
|
+
"./de.ts": ye,
|
|
369
|
+
"./en.ts": xe
|
|
370
|
+
}), O = {};
|
|
371
|
+
for (let e in D) {
|
|
289
372
|
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
290
373
|
if (!t) continue;
|
|
291
374
|
let n = t[1];
|
|
292
|
-
n !== "index" && (
|
|
375
|
+
n !== "index" && (O[n] = D[e].default);
|
|
293
376
|
}
|
|
294
|
-
function
|
|
295
|
-
return
|
|
377
|
+
function k(e) {
|
|
378
|
+
return Ce;
|
|
296
379
|
}
|
|
297
|
-
function
|
|
380
|
+
function A(e) {
|
|
298
381
|
let t = /* @__PURE__ */ new Set();
|
|
299
|
-
for (let n of Object.values(
|
|
382
|
+
for (let n of Object.values(O)) for (let r of e(n)) t.add(r);
|
|
300
383
|
return Array.from(t);
|
|
301
384
|
}
|
|
302
|
-
var
|
|
303
|
-
vagueLinkText:
|
|
304
|
-
vagueButtonLabels:
|
|
305
|
-
linkedImageActionHints:
|
|
306
|
-
},
|
|
307
|
-
function
|
|
385
|
+
var Ce = {
|
|
386
|
+
vagueLinkText: A((e) => e.vagueLinkText),
|
|
387
|
+
vagueButtonLabels: A((e) => e.vagueButtonLabels),
|
|
388
|
+
linkedImageActionHints: A((e) => e.linkedImageActionHints)
|
|
389
|
+
}, we = Object.keys(O);
|
|
390
|
+
function j(e) {
|
|
308
391
|
return e.toLowerCase().replace(/\s+/g, " ").replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "").trim();
|
|
309
392
|
}
|
|
310
|
-
var
|
|
393
|
+
var Te = {
|
|
311
394
|
meta: {
|
|
312
|
-
id: "img-linked-no-context",
|
|
395
|
+
id: "a11y.img-linked-no-context",
|
|
313
396
|
severity: "warning"
|
|
314
397
|
},
|
|
315
|
-
block(e, t,
|
|
316
|
-
if (!
|
|
398
|
+
block(e, t, n) {
|
|
399
|
+
if (!r(e) || e.decorative === !0 || !e.linkUrl || e.linkUrl.trim() === "") return null;
|
|
317
400
|
let i = (e.alt ?? "").trim();
|
|
318
401
|
if (i === "") return null;
|
|
319
|
-
let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o =
|
|
402
|
+
let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o = k(n.locale).linkedImageActionHints;
|
|
320
403
|
return a.some((e) => o.includes(e)) ? null : { blockId: e.id };
|
|
321
404
|
}
|
|
322
405
|
};
|
|
323
406
|
//#endregion
|
|
324
407
|
//#region src/html-utils.ts
|
|
325
|
-
function
|
|
326
|
-
let t = [], n = [], r = new
|
|
408
|
+
function M(e) {
|
|
409
|
+
let t = [], n = [], r = new l({
|
|
327
410
|
onopentag(e, t) {
|
|
328
411
|
if (e === "a") {
|
|
329
412
|
let e = {
|
|
@@ -353,38 +436,38 @@ function U(e) {
|
|
|
353
436
|
});
|
|
354
437
|
return r.write(e), r.end(), t;
|
|
355
438
|
}
|
|
356
|
-
function
|
|
357
|
-
let t = "", n = new
|
|
439
|
+
function N(e) {
|
|
440
|
+
let t = "", n = new l({ ontext(e) {
|
|
358
441
|
t += e;
|
|
359
442
|
} });
|
|
360
443
|
return n.write(e), n.end(), t.trim();
|
|
361
444
|
}
|
|
362
|
-
var
|
|
445
|
+
var Ee = {
|
|
363
446
|
meta: {
|
|
364
|
-
id: "heading-empty",
|
|
447
|
+
id: "a11y.heading-empty",
|
|
365
448
|
severity: "error"
|
|
366
449
|
},
|
|
367
450
|
block(e) {
|
|
368
|
-
return !
|
|
451
|
+
return !c(e) || N(e.content ?? "") !== "" ? null : { blockId: e.id };
|
|
369
452
|
}
|
|
370
|
-
},
|
|
371
|
-
id: "heading-skip-level",
|
|
453
|
+
}, De = {
|
|
454
|
+
id: "a11y.heading-skip-level",
|
|
372
455
|
severity: "error"
|
|
373
456
|
};
|
|
374
|
-
function
|
|
457
|
+
function P(e, t) {
|
|
375
458
|
for (let n of e) {
|
|
376
|
-
if (
|
|
459
|
+
if (c(n)) {
|
|
377
460
|
t.push(n);
|
|
378
461
|
continue;
|
|
379
462
|
}
|
|
380
|
-
if (
|
|
463
|
+
if (o(n)) for (let e of n.children) P(e, t);
|
|
381
464
|
}
|
|
382
465
|
}
|
|
383
|
-
var
|
|
384
|
-
meta:
|
|
466
|
+
var Oe = {
|
|
467
|
+
meta: De,
|
|
385
468
|
template(e) {
|
|
386
469
|
let t = [];
|
|
387
|
-
|
|
470
|
+
P(e.blocks, t);
|
|
388
471
|
let n = [], r = 0;
|
|
389
472
|
for (let e of t) r !== 0 && e.level > r + 1 && n.push({
|
|
390
473
|
blockId: e.id,
|
|
@@ -395,54 +478,54 @@ var J = {
|
|
|
395
478
|
}), r = e.level;
|
|
396
479
|
return n;
|
|
397
480
|
}
|
|
398
|
-
},
|
|
399
|
-
id: "heading-multiple-h1",
|
|
481
|
+
}, ke = {
|
|
482
|
+
id: "a11y.heading-multiple-h1",
|
|
400
483
|
severity: "warning"
|
|
401
484
|
};
|
|
402
|
-
function
|
|
485
|
+
function F(e, t) {
|
|
403
486
|
for (let n of e) {
|
|
404
|
-
if (
|
|
487
|
+
if (c(n)) {
|
|
405
488
|
t.push(n);
|
|
406
489
|
continue;
|
|
407
490
|
}
|
|
408
|
-
if (
|
|
491
|
+
if (o(n)) for (let e of n.children) F(e, t);
|
|
409
492
|
}
|
|
410
493
|
}
|
|
411
|
-
var
|
|
412
|
-
meta:
|
|
494
|
+
var Ae = {
|
|
495
|
+
meta: ke,
|
|
413
496
|
template(e) {
|
|
414
497
|
let t = [];
|
|
415
|
-
|
|
498
|
+
F(e.blocks, t);
|
|
416
499
|
let n = t.filter((e) => e.level === 1);
|
|
417
500
|
return n.length <= 1 ? [] : n.slice(1).map((e) => ({ blockId: e.id }));
|
|
418
501
|
}
|
|
419
|
-
},
|
|
420
|
-
id: "link-empty",
|
|
502
|
+
}, je = {
|
|
503
|
+
id: "a11y.link-empty",
|
|
421
504
|
severity: "error"
|
|
422
505
|
};
|
|
423
|
-
function
|
|
424
|
-
return
|
|
506
|
+
function Me(e) {
|
|
507
|
+
return a(e) || c(e) ? e.content : null;
|
|
425
508
|
}
|
|
426
|
-
var
|
|
427
|
-
meta:
|
|
509
|
+
var Ne = {
|
|
510
|
+
meta: je,
|
|
428
511
|
block(e) {
|
|
429
|
-
let t =
|
|
430
|
-
return t === null || !
|
|
512
|
+
let t = Me(e);
|
|
513
|
+
return t === null || !M(t).find((e) => e.text === "" && !e.hasImageWithAlt) ? null : { blockId: e.id };
|
|
431
514
|
}
|
|
432
|
-
},
|
|
433
|
-
id: "link-vague-text",
|
|
515
|
+
}, Pe = {
|
|
516
|
+
id: "a11y.link-vague-text",
|
|
434
517
|
severity: "warning"
|
|
435
518
|
};
|
|
436
|
-
function
|
|
437
|
-
return
|
|
519
|
+
function Fe(e) {
|
|
520
|
+
return a(e) || c(e) ? e.content : null;
|
|
438
521
|
}
|
|
439
|
-
var
|
|
440
|
-
meta:
|
|
522
|
+
var Ie = {
|
|
523
|
+
meta: Pe,
|
|
441
524
|
block(e, t, n) {
|
|
442
|
-
let r =
|
|
525
|
+
let r = Fe(e);
|
|
443
526
|
if (r === null) return null;
|
|
444
|
-
let i =
|
|
445
|
-
let t =
|
|
527
|
+
let i = k(n.locale).vagueLinkText, a = M(r).find((e) => {
|
|
528
|
+
let t = j(e.text);
|
|
446
529
|
return t !== "" && i.includes(t);
|
|
447
530
|
});
|
|
448
531
|
return a ? {
|
|
@@ -450,53 +533,53 @@ var de = {
|
|
|
450
533
|
params: { text: a.text }
|
|
451
534
|
} : null;
|
|
452
535
|
}
|
|
453
|
-
},
|
|
454
|
-
id: "link-href-empty",
|
|
536
|
+
}, Le = {
|
|
537
|
+
id: "a11y.link-href-empty",
|
|
455
538
|
severity: "error"
|
|
456
539
|
};
|
|
457
|
-
function
|
|
458
|
-
return
|
|
540
|
+
function Re(e) {
|
|
541
|
+
return a(e) || c(e) ? e.content : null;
|
|
459
542
|
}
|
|
460
|
-
var
|
|
461
|
-
meta:
|
|
543
|
+
var ze = {
|
|
544
|
+
meta: Le,
|
|
462
545
|
block(e) {
|
|
463
|
-
let t =
|
|
464
|
-
return t === null || !
|
|
546
|
+
let t = Re(e);
|
|
547
|
+
return t === null || !M(t).find((e) => {
|
|
465
548
|
let t = e.href.trim();
|
|
466
549
|
return t === "" || t === "#";
|
|
467
550
|
}) ? null : { blockId: e.id };
|
|
468
551
|
}
|
|
469
|
-
},
|
|
470
|
-
id: "link-target-blank-no-rel",
|
|
552
|
+
}, Be = {
|
|
553
|
+
id: "a11y.link-target-blank-no-rel",
|
|
471
554
|
severity: "warning"
|
|
472
555
|
};
|
|
473
|
-
function
|
|
474
|
-
return
|
|
556
|
+
function Ve(e) {
|
|
557
|
+
return a(e) || c(e) ? e.content : null;
|
|
475
558
|
}
|
|
476
|
-
function
|
|
559
|
+
function He(e) {
|
|
477
560
|
if (e === null) return !1;
|
|
478
561
|
let t = e.toLowerCase().split(/\s+/);
|
|
479
562
|
return t.includes("noopener") || t.includes("noreferrer");
|
|
480
563
|
}
|
|
481
|
-
var
|
|
482
|
-
meta:
|
|
564
|
+
var Ue = {
|
|
565
|
+
meta: Be,
|
|
483
566
|
block(e) {
|
|
484
|
-
let t =
|
|
485
|
-
return t === null || !
|
|
567
|
+
let t = Ve(e);
|
|
568
|
+
return t === null || !M(t).find((e) => e.target === "_blank" && !He(e.rel)) ? null : {
|
|
486
569
|
blockId: e.id,
|
|
487
570
|
fix: {
|
|
488
571
|
description: "Add rel=\"noopener\"",
|
|
489
572
|
apply: (t) => {
|
|
490
|
-
if (!
|
|
491
|
-
let n =
|
|
573
|
+
if (!a(e) && !c(e)) return;
|
|
574
|
+
let n = Ke(e.content ?? "");
|
|
492
575
|
t.updateBlock(e.id, { content: n });
|
|
493
576
|
}
|
|
494
577
|
}
|
|
495
578
|
};
|
|
496
579
|
}
|
|
497
|
-
},
|
|
498
|
-
function
|
|
499
|
-
let t = [], n = new RegExp(
|
|
580
|
+
}, I = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
581
|
+
function We(e) {
|
|
582
|
+
let t = [], n = new RegExp(I.source, I.flags), r;
|
|
500
583
|
for (; (r = n.exec(e)) !== null;) {
|
|
501
584
|
let e = r[2] ?? r[3] ?? r[4] ?? null;
|
|
502
585
|
t.push({
|
|
@@ -508,13 +591,13 @@ function ye(e) {
|
|
|
508
591
|
}
|
|
509
592
|
return t;
|
|
510
593
|
}
|
|
511
|
-
function
|
|
594
|
+
function Ge(e) {
|
|
512
595
|
return e.some((e) => e.name.toLowerCase() === "target" && e.value !== null && e.value.toLowerCase() === "_blank");
|
|
513
596
|
}
|
|
514
|
-
function
|
|
597
|
+
function Ke(e) {
|
|
515
598
|
return e.replace(/<a\b([^>]*)>/gi, (e, t) => {
|
|
516
|
-
let n =
|
|
517
|
-
if (!
|
|
599
|
+
let n = We(t);
|
|
600
|
+
if (!Ge(n)) return e;
|
|
518
601
|
let r = n.find((e) => e.name.toLowerCase() === "rel");
|
|
519
602
|
if (r) {
|
|
520
603
|
let n = (r.value ?? "").toLowerCase().split(/\s+/);
|
|
@@ -525,24 +608,24 @@ function xe(e) {
|
|
|
525
608
|
return `<a${t} rel="noopener">`;
|
|
526
609
|
});
|
|
527
610
|
}
|
|
528
|
-
var
|
|
611
|
+
var qe = {
|
|
529
612
|
meta: {
|
|
530
|
-
id: "text-all-caps",
|
|
613
|
+
id: "a11y.text-all-caps",
|
|
531
614
|
severity: "warning"
|
|
532
615
|
},
|
|
533
616
|
block(e, t, n) {
|
|
534
|
-
if (!
|
|
535
|
-
let r =
|
|
617
|
+
if (!a(e) && !c(e)) return null;
|
|
618
|
+
let r = N(e.content ?? "").replace(/[^\p{L}]/gu, "");
|
|
536
619
|
return r.length < n.thresholds.allCapsMinLength || r !== r.toLocaleUpperCase() ? null : { blockId: e.id };
|
|
537
620
|
}
|
|
538
|
-
},
|
|
621
|
+
}, Je = {
|
|
539
622
|
meta: {
|
|
540
|
-
id: "text-low-contrast",
|
|
623
|
+
id: "a11y.text-low-contrast",
|
|
541
624
|
severity: "error"
|
|
542
625
|
},
|
|
543
626
|
block(t, n) {
|
|
544
|
-
if (!
|
|
545
|
-
let r = e[t.level] >= 24 ? 3 : 4.5, i =
|
|
627
|
+
if (!c(t) || !g(t.color) || !g(n.resolvedBackgroundColor)) return null;
|
|
628
|
+
let r = e[t.level] >= 24 ? 3 : 4.5, i = m(t.color, n.resolvedBackgroundColor);
|
|
546
629
|
return Number.isNaN(i) || i >= r ? null : {
|
|
547
630
|
blockId: t.id,
|
|
548
631
|
params: {
|
|
@@ -551,34 +634,34 @@ var Se = {
|
|
|
551
634
|
}
|
|
552
635
|
};
|
|
553
636
|
}
|
|
554
|
-
},
|
|
555
|
-
id: "text-too-small",
|
|
637
|
+
}, Ye = {
|
|
638
|
+
id: "a11y.text-too-small",
|
|
556
639
|
severity: "warning"
|
|
557
640
|
};
|
|
558
|
-
function
|
|
559
|
-
return
|
|
641
|
+
function Xe(e) {
|
|
642
|
+
return i(e) || ee(e) ? e.fontSize : null;
|
|
560
643
|
}
|
|
561
644
|
//#endregion
|
|
562
645
|
//#region src/accessibility/index.ts
|
|
563
|
-
var
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
ie,
|
|
568
|
-
H,
|
|
569
|
-
G,
|
|
570
|
-
J,
|
|
571
|
-
ae,
|
|
572
|
-
ce,
|
|
573
|
-
de,
|
|
574
|
-
me,
|
|
646
|
+
var L = [
|
|
647
|
+
pe,
|
|
648
|
+
ge,
|
|
649
|
+
_e,
|
|
575
650
|
ve,
|
|
576
|
-
|
|
577
|
-
|
|
651
|
+
Te,
|
|
652
|
+
Ee,
|
|
653
|
+
Oe,
|
|
654
|
+
Ae,
|
|
655
|
+
Ne,
|
|
656
|
+
Ie,
|
|
657
|
+
ze,
|
|
658
|
+
Ue,
|
|
659
|
+
qe,
|
|
660
|
+
Je,
|
|
578
661
|
{
|
|
579
|
-
meta:
|
|
662
|
+
meta: Ye,
|
|
580
663
|
block(e, t, n) {
|
|
581
|
-
let r =
|
|
664
|
+
let r = Xe(e);
|
|
582
665
|
return r === null || r >= n.thresholds.minFontSize ? null : {
|
|
583
666
|
blockId: e.id,
|
|
584
667
|
params: {
|
|
@@ -590,13 +673,13 @@ var $ = [
|
|
|
590
673
|
},
|
|
591
674
|
{
|
|
592
675
|
meta: {
|
|
593
|
-
id: "button-vague-label",
|
|
676
|
+
id: "a11y.button-vague-label",
|
|
594
677
|
severity: "warning"
|
|
595
678
|
},
|
|
596
679
|
block(e, n, r) {
|
|
597
680
|
if (!t(e)) return null;
|
|
598
|
-
let i =
|
|
599
|
-
return i === "" || !
|
|
681
|
+
let i = j(e.text ?? "");
|
|
682
|
+
return i === "" || !k(r.locale).vagueButtonLabels.includes(i) ? null : {
|
|
600
683
|
blockId: e.id,
|
|
601
684
|
params: { text: e.text }
|
|
602
685
|
};
|
|
@@ -604,7 +687,7 @@ var $ = [
|
|
|
604
687
|
},
|
|
605
688
|
{
|
|
606
689
|
meta: {
|
|
607
|
-
id: "button-touch-target",
|
|
690
|
+
id: "a11y.button-touch-target",
|
|
608
691
|
severity: "warning"
|
|
609
692
|
},
|
|
610
693
|
block(e, n, r) {
|
|
@@ -623,12 +706,12 @@ var $ = [
|
|
|
623
706
|
},
|
|
624
707
|
{
|
|
625
708
|
meta: {
|
|
626
|
-
id: "button-low-contrast",
|
|
709
|
+
id: "a11y.button-low-contrast",
|
|
627
710
|
severity: "error"
|
|
628
711
|
},
|
|
629
712
|
block(e) {
|
|
630
713
|
if (!t(e)) return null;
|
|
631
|
-
let n =
|
|
714
|
+
let n = m(e.textColor, e.backgroundColor);
|
|
632
715
|
if (Number.isNaN(n)) return null;
|
|
633
716
|
let r = e.fontSize >= 24 ? 3 : 4.5;
|
|
634
717
|
return n >= r ? null : {
|
|
@@ -642,7 +725,7 @@ var $ = [
|
|
|
642
725
|
},
|
|
643
726
|
{
|
|
644
727
|
meta: {
|
|
645
|
-
id: "missing-preheader",
|
|
728
|
+
id: "a11y.missing-preheader",
|
|
646
729
|
severity: "info"
|
|
647
730
|
},
|
|
648
731
|
template(e) {
|
|
@@ -650,50 +733,400 @@ var $ = [
|
|
|
650
733
|
}
|
|
651
734
|
}
|
|
652
735
|
];
|
|
653
|
-
function
|
|
654
|
-
if (t.disabled === !0) return [];
|
|
655
|
-
let n =
|
|
656
|
-
|
|
736
|
+
function Ze(e, t = {}) {
|
|
737
|
+
if (t.disabled === !0 || t.accessibility === !1) return [];
|
|
738
|
+
let n = t.accessibility ?? {};
|
|
739
|
+
return b(e, L, oe(t.locale, n, L), (e, t, n) => E(e, t, n));
|
|
740
|
+
}
|
|
741
|
+
//#endregion
|
|
742
|
+
//#region src/structure/messages/de.ts
|
|
743
|
+
var Qe = /* @__PURE__ */ d({ default: () => $e }), $e = {
|
|
744
|
+
"structure.duplicate-block-id": "Block-ID erscheint {count}-mal im Baum. Jeder Block muss eine eindeutige ID haben.",
|
|
745
|
+
"structure.section-column-mismatch": "Sektion verwendet Layout „{layout}\" (erwartet {expected} Spalten), hat aber {actual}. Deutet auf beschädigten Zustand hin.",
|
|
746
|
+
"structure.nested-section": "Sektion ist in einer anderen Sektion verschachtelt. Sektionen können nicht verschachtelt werden – der Renderer wird sich falsch verhalten.",
|
|
747
|
+
"structure.empty-section": "Sektion enthält keine Blöcke. Entferne sie oder füge Inhalt hinzu.",
|
|
748
|
+
"structure.empty-column": "Spalte {columnIndex} dieser Sektion ist leer. Füge Inhalt hinzu oder reduziere die Spaltenanzahl."
|
|
749
|
+
}, et = /* @__PURE__ */ d({ default: () => R }), R = {
|
|
750
|
+
"structure.duplicate-block-id": "Block id appears {count} times in the tree. Each block must have a unique id.",
|
|
751
|
+
"structure.section-column-mismatch": "Section uses layout \"{layout}\" (expects {expected} columns) but has {actual}. Indicates corrupted state.",
|
|
752
|
+
"structure.nested-section": "Section is nested inside another section. Sections cannot nest — the renderer will misbehave.",
|
|
753
|
+
"structure.empty-section": "Section has no blocks. Remove it or add content.",
|
|
754
|
+
"structure.empty-column": "Column {columnIndex} of this section is empty. Add content or reduce the column count."
|
|
755
|
+
}, z = /* @__PURE__ */ Object.assign({
|
|
756
|
+
"./de.ts": Qe,
|
|
757
|
+
"./en.ts": et
|
|
758
|
+
}), B = {};
|
|
759
|
+
for (let e in z) {
|
|
760
|
+
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
761
|
+
if (!t) continue;
|
|
762
|
+
let n = t[1];
|
|
763
|
+
n !== "index" && (B[n] = z[e].default);
|
|
764
|
+
}
|
|
765
|
+
var tt = Object.keys(B);
|
|
766
|
+
function V(e) {
|
|
767
|
+
return B[e.split("-")[0]?.toLowerCase() ?? "en"] ?? B.en ?? R;
|
|
768
|
+
}
|
|
769
|
+
function H(e, t, n) {
|
|
770
|
+
let r = V(e)[t] ?? R[t];
|
|
771
|
+
return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
|
|
772
|
+
let r = n[t];
|
|
773
|
+
return r === void 0 ? `{${t}}` : String(r);
|
|
774
|
+
}) : r;
|
|
775
|
+
}
|
|
776
|
+
//#endregion
|
|
777
|
+
//#region src/structure/rules/duplicate-block-id.ts
|
|
778
|
+
var nt = {
|
|
779
|
+
id: "structure.duplicate-block-id",
|
|
780
|
+
severity: "error"
|
|
781
|
+
};
|
|
782
|
+
function U(e, t) {
|
|
783
|
+
for (let n of e) if (t.set(n.id, (t.get(n.id) ?? 0) + 1), o(n)) for (let e of n.children) U(e, t);
|
|
784
|
+
}
|
|
785
|
+
var rt = {
|
|
786
|
+
meta: nt,
|
|
787
|
+
template(e) {
|
|
788
|
+
let t = /* @__PURE__ */ new Map();
|
|
789
|
+
U(e.blocks, t);
|
|
790
|
+
let n = [];
|
|
791
|
+
for (let [e, r] of t) r > 1 && n.push({
|
|
792
|
+
blockId: e,
|
|
793
|
+
params: { count: r }
|
|
794
|
+
});
|
|
795
|
+
return n;
|
|
796
|
+
}
|
|
797
|
+
}, it = {
|
|
798
|
+
id: "structure.empty-column",
|
|
799
|
+
severity: "warning"
|
|
800
|
+
};
|
|
801
|
+
function W(e, t) {
|
|
802
|
+
for (let n of e) {
|
|
803
|
+
if (!o(n)) continue;
|
|
804
|
+
let e = n;
|
|
805
|
+
e.children.length > 1 && e.children.forEach((n, r) => {
|
|
806
|
+
n.length === 0 && t.push({
|
|
807
|
+
blockId: e.id,
|
|
808
|
+
params: { columnIndex: r + 1 }
|
|
809
|
+
});
|
|
810
|
+
});
|
|
811
|
+
for (let n of e.children) W(n, t);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
var at = {
|
|
815
|
+
meta: it,
|
|
816
|
+
template(e) {
|
|
817
|
+
let t = [];
|
|
818
|
+
return W(e.blocks, t), t;
|
|
819
|
+
}
|
|
820
|
+
}, ot = {
|
|
821
|
+
id: "structure.empty-section",
|
|
822
|
+
severity: "warning"
|
|
823
|
+
};
|
|
824
|
+
function st(e) {
|
|
825
|
+
return e.children.length === 0 ? !0 : e.children.every((e) => e.length === 0);
|
|
826
|
+
}
|
|
827
|
+
var ct = {
|
|
828
|
+
meta: ot,
|
|
829
|
+
block(e) {
|
|
830
|
+
if (!o(e)) return null;
|
|
831
|
+
let t = e;
|
|
832
|
+
return st(t) ? {
|
|
833
|
+
blockId: t.id,
|
|
834
|
+
fix: {
|
|
835
|
+
description: "Remove the empty section",
|
|
836
|
+
apply: (e) => {
|
|
837
|
+
e.removeBlock(t.id);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
} : null;
|
|
841
|
+
}
|
|
842
|
+
}, lt = {
|
|
843
|
+
meta: {
|
|
844
|
+
id: "structure.nested-section",
|
|
845
|
+
severity: "error"
|
|
846
|
+
},
|
|
847
|
+
block(e, t) {
|
|
848
|
+
if (!o(e) || t.section === null) return null;
|
|
849
|
+
let n = t.section;
|
|
657
850
|
return {
|
|
658
|
-
blockId:
|
|
659
|
-
|
|
660
|
-
severity: t,
|
|
661
|
-
message: k(n.locale, e, r.params),
|
|
662
|
-
fix: r.fix
|
|
851
|
+
blockId: e.id,
|
|
852
|
+
params: { parentId: n.id }
|
|
663
853
|
};
|
|
664
854
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
855
|
+
}, ut = {
|
|
856
|
+
id: "structure.section-column-mismatch",
|
|
857
|
+
severity: "error"
|
|
858
|
+
};
|
|
859
|
+
function dt(e) {
|
|
860
|
+
return e === "1" ? 1 : e === "3" ? 3 : 2;
|
|
861
|
+
}
|
|
862
|
+
//#endregion
|
|
863
|
+
//#region src/structure/index.ts
|
|
864
|
+
var G = [
|
|
865
|
+
rt,
|
|
866
|
+
ct,
|
|
867
|
+
at,
|
|
868
|
+
lt,
|
|
869
|
+
{
|
|
870
|
+
meta: ut,
|
|
871
|
+
block(e) {
|
|
872
|
+
if (!o(e)) return null;
|
|
873
|
+
let t = e, n = dt(t.columns), r = t.children.length;
|
|
874
|
+
return r === n ? null : {
|
|
875
|
+
blockId: t.id,
|
|
876
|
+
params: {
|
|
877
|
+
layout: t.columns,
|
|
878
|
+
expected: n,
|
|
879
|
+
actual: r
|
|
880
|
+
}
|
|
881
|
+
};
|
|
671
882
|
}
|
|
672
|
-
});
|
|
673
|
-
for (let t of $) {
|
|
674
|
-
let a = n.severity(t.meta.id);
|
|
675
|
-
if (a === "off" || !t.template) continue;
|
|
676
|
-
let o = t.template(e, n);
|
|
677
|
-
for (let e of o) r.push(i(t.meta.id, a, e));
|
|
678
883
|
}
|
|
679
|
-
|
|
884
|
+
];
|
|
885
|
+
function ft(e, t = {}) {
|
|
886
|
+
if (t.disabled === !0 || t.structure === !1) return [];
|
|
887
|
+
let n = t.structure ?? {};
|
|
888
|
+
return b(e, G, se(t.locale, n, G), (e, t, n) => H(e, t, n));
|
|
680
889
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
890
|
+
//#endregion
|
|
891
|
+
//#region src/links/messages/de.ts
|
|
892
|
+
var pt = /* @__PURE__ */ d({ default: () => mt }), mt = {
|
|
893
|
+
"link.javascript-protocol": "Die URL verwendet das „javascript:\"-Protokoll, das aus Sicherheitsgründen beim Rendern entfernt wird. Ersetze sie durch eine echte URL oder entferne sie.",
|
|
894
|
+
"link.unsupported-protocol": "Die URL verwendet das Protokoll „{protocol}\", das von den meisten E-Mail-Clients nicht unterstützt wird. Verwende http, https, mailto, tel oder sms.",
|
|
895
|
+
"link.malformed-mailto": "Der mailto:-Link ist fehlerhaft. Erwartet wird eine einzelne Empfängeradresse vor einer eventuellen Querystring (z. B. mailto:hallo@example.com).",
|
|
896
|
+
"link.malformed-tel": "Der tel:-Link enthält Zeichen, die keine Ziffern, +, Leerzeichen, Bindestriche, Klammern oder Punkte sind.",
|
|
897
|
+
"link.localhost-or-staging": "Der URL-Host „{host}\" entspricht einem Nicht-Produktionsmuster. Ersetze ihn vor dem Versand durch die Produktions-URL."
|
|
898
|
+
}, ht = /* @__PURE__ */ d({ default: () => K }), K = {
|
|
899
|
+
"link.javascript-protocol": "URL uses the \"javascript:\" protocol, which is stripped at render time for safety. Replace it with a real link or remove the URL.",
|
|
900
|
+
"link.unsupported-protocol": "URL uses the \"{protocol}\" protocol, which most email clients do not support. Use http, https, mailto, tel, or sms.",
|
|
901
|
+
"link.malformed-mailto": "mailto: link is malformed. Expected a single recipient address before any query string (e.g. mailto:hello@example.com).",
|
|
902
|
+
"link.malformed-tel": "tel: link contains characters that are not digits, +, spaces, dashes, parentheses, or dots.",
|
|
903
|
+
"link.localhost-or-staging": "URL host \"{host}\" matches a non-production pattern. Replace with the production URL before sending."
|
|
904
|
+
}, q = /* @__PURE__ */ Object.assign({
|
|
905
|
+
"./de.ts": pt,
|
|
906
|
+
"./en.ts": ht
|
|
907
|
+
}), J = {};
|
|
908
|
+
for (let e in q) {
|
|
909
|
+
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
910
|
+
if (!t) continue;
|
|
911
|
+
let n = t[1];
|
|
912
|
+
n !== "index" && (J[n] = q[e].default);
|
|
913
|
+
}
|
|
914
|
+
var gt = Object.keys(J);
|
|
915
|
+
function Y(e) {
|
|
916
|
+
return J[e.split("-")[0]?.toLowerCase() ?? "en"] ?? J.en ?? K;
|
|
917
|
+
}
|
|
918
|
+
function X(e, t, n) {
|
|
919
|
+
let r = Y(e)[t] ?? K[t];
|
|
920
|
+
return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
|
|
921
|
+
let r = n[t];
|
|
922
|
+
return r === void 0 ? `{${t}}` : String(r);
|
|
923
|
+
}) : r;
|
|
924
|
+
}
|
|
925
|
+
//#endregion
|
|
926
|
+
//#region src/url-walker.ts
|
|
927
|
+
function Z(e) {
|
|
928
|
+
let o = [];
|
|
929
|
+
return y(e, (e) => {
|
|
930
|
+
if (c(e) || a(e) || n(e)) {
|
|
931
|
+
for (let t of M(e.content)) o.push({
|
|
932
|
+
url: t.href,
|
|
933
|
+
blockId: e.id,
|
|
934
|
+
source: "anchor",
|
|
935
|
+
label: t.text
|
|
936
|
+
});
|
|
937
|
+
return;
|
|
693
938
|
}
|
|
694
|
-
|
|
939
|
+
if (t(e)) {
|
|
940
|
+
o.push({
|
|
941
|
+
url: e.url,
|
|
942
|
+
blockId: e.id,
|
|
943
|
+
source: "button",
|
|
944
|
+
label: e.text
|
|
945
|
+
});
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (r(e)) {
|
|
949
|
+
e.linkUrl && e.linkUrl !== "" && o.push({
|
|
950
|
+
url: e.linkUrl,
|
|
951
|
+
blockId: e.id,
|
|
952
|
+
source: "image-link",
|
|
953
|
+
label: e.alt || void 0
|
|
954
|
+
});
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
if (te(e)) {
|
|
958
|
+
o.push({
|
|
959
|
+
url: e.url,
|
|
960
|
+
blockId: e.id,
|
|
961
|
+
source: "video",
|
|
962
|
+
label: e.alt || void 0
|
|
963
|
+
});
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (i(e)) {
|
|
967
|
+
for (let t of e.items) o.push({
|
|
968
|
+
url: t.url,
|
|
969
|
+
blockId: e.id,
|
|
970
|
+
source: "menu-item",
|
|
971
|
+
label: t.text
|
|
972
|
+
});
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (s(e)) {
|
|
976
|
+
for (let t of e.icons) o.push({
|
|
977
|
+
url: t.url,
|
|
978
|
+
blockId: e.id,
|
|
979
|
+
source: "social-icon",
|
|
980
|
+
label: t.platform
|
|
981
|
+
});
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
}), o;
|
|
985
|
+
}
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/links/rules/javascript-protocol.ts
|
|
988
|
+
var _t = {
|
|
989
|
+
id: "link.javascript-protocol",
|
|
990
|
+
severity: "error"
|
|
991
|
+
};
|
|
992
|
+
function vt(e) {
|
|
993
|
+
if (!e) return !1;
|
|
994
|
+
let t = e.replace(/\s+/g, "");
|
|
995
|
+
return /^javascript:/i.test(t);
|
|
996
|
+
}
|
|
997
|
+
var yt = {
|
|
998
|
+
meta: _t,
|
|
999
|
+
template(e) {
|
|
1000
|
+
let t = [];
|
|
1001
|
+
for (let n of Z(e)) vt(n.url) && t.push({ blockId: n.blockId });
|
|
1002
|
+
return t;
|
|
1003
|
+
}
|
|
1004
|
+
}, bt = {
|
|
1005
|
+
id: "link.unsupported-protocol",
|
|
1006
|
+
severity: "warning"
|
|
1007
|
+
}, xt = new Set([
|
|
1008
|
+
"http",
|
|
1009
|
+
"https",
|
|
1010
|
+
"mailto",
|
|
1011
|
+
"tel",
|
|
1012
|
+
"sms"
|
|
1013
|
+
]);
|
|
1014
|
+
function St(e) {
|
|
1015
|
+
if (!e) return null;
|
|
1016
|
+
let t = e.trim(), n = /^([a-z][a-z0-9+\-.]*):/i.exec(t);
|
|
1017
|
+
return n ? n[1].toLowerCase() : null;
|
|
1018
|
+
}
|
|
1019
|
+
var Ct = {
|
|
1020
|
+
meta: bt,
|
|
1021
|
+
template(e) {
|
|
1022
|
+
let t = [];
|
|
1023
|
+
for (let n of Z(e)) {
|
|
1024
|
+
let e = St(n.url);
|
|
1025
|
+
e !== null && e !== "javascript" && (xt.has(e) || t.push({
|
|
1026
|
+
blockId: n.blockId,
|
|
1027
|
+
params: { protocol: e }
|
|
1028
|
+
}));
|
|
1029
|
+
}
|
|
1030
|
+
return t;
|
|
1031
|
+
}
|
|
1032
|
+
}, wt = {
|
|
1033
|
+
id: "link.malformed-mailto",
|
|
1034
|
+
severity: "warning"
|
|
1035
|
+
};
|
|
1036
|
+
function Tt(e) {
|
|
1037
|
+
let t = e.trim();
|
|
1038
|
+
if (!/^mailto:/i.test(t)) return !1;
|
|
1039
|
+
let [n] = t.slice(7).split("?", 2);
|
|
1040
|
+
if (n.trim() === "") return !0;
|
|
1041
|
+
let r = n.split(",").map((e) => e.trim());
|
|
1042
|
+
for (let e of r) {
|
|
1043
|
+
if (e === "") return !0;
|
|
1044
|
+
let t = e.split("@");
|
|
1045
|
+
if (t.length !== 2) return !0;
|
|
1046
|
+
let [n, r] = t;
|
|
1047
|
+
if (n === "" || r === "" || !r.includes(".")) return !0;
|
|
1048
|
+
}
|
|
1049
|
+
return !1;
|
|
1050
|
+
}
|
|
1051
|
+
var Et = {
|
|
1052
|
+
meta: wt,
|
|
1053
|
+
template(e) {
|
|
1054
|
+
let t = [];
|
|
1055
|
+
for (let n of Z(e)) Tt(n.url) && t.push({ blockId: n.blockId });
|
|
1056
|
+
return t;
|
|
1057
|
+
}
|
|
1058
|
+
}, Dt = {
|
|
1059
|
+
id: "link.malformed-tel",
|
|
1060
|
+
severity: "warning"
|
|
1061
|
+
}, Q = /^[+0-9\s().\-]+$/, Ot = /^[A-Za-z0-9-]+(=[^;]+)?$/;
|
|
1062
|
+
function kt(e) {
|
|
1063
|
+
let t = e.trim();
|
|
1064
|
+
if (!/^tel:/i.test(t)) return !1;
|
|
1065
|
+
let n = t.slice(4).trim();
|
|
1066
|
+
if (n === "") return !0;
|
|
1067
|
+
let [r, ...i] = n.split(";");
|
|
1068
|
+
return Q.test(r) ? i.some((e) => !Ot.test(e)) : !0;
|
|
1069
|
+
}
|
|
1070
|
+
var At = {
|
|
1071
|
+
meta: Dt,
|
|
1072
|
+
template(e) {
|
|
1073
|
+
let t = [];
|
|
1074
|
+
for (let n of Z(e)) kt(n.url) && t.push({ blockId: n.blockId });
|
|
1075
|
+
return t;
|
|
1076
|
+
}
|
|
1077
|
+
}, jt = {
|
|
1078
|
+
id: "link.localhost-or-staging",
|
|
1079
|
+
severity: "warning"
|
|
1080
|
+
};
|
|
1081
|
+
function Mt(e) {
|
|
1082
|
+
let t = e.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
1083
|
+
return RegExp(`^${t}$`, "i");
|
|
1084
|
+
}
|
|
1085
|
+
function Nt(e) {
|
|
1086
|
+
if (!e) return null;
|
|
1087
|
+
let t = e.trim();
|
|
1088
|
+
if (!/^(https?|ftps?):\/\//i.test(t)) return null;
|
|
1089
|
+
try {
|
|
1090
|
+
return new URL(t).hostname.toLowerCase();
|
|
1091
|
+
} catch {
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
//#endregion
|
|
1096
|
+
//#region src/links/index.ts
|
|
1097
|
+
var $ = [
|
|
1098
|
+
yt,
|
|
1099
|
+
Ct,
|
|
1100
|
+
Et,
|
|
1101
|
+
At,
|
|
1102
|
+
{
|
|
1103
|
+
meta: jt,
|
|
1104
|
+
template(e, t) {
|
|
1105
|
+
let n = t.links.nonProductionHosts;
|
|
1106
|
+
if (n.length === 0) return [];
|
|
1107
|
+
let r = n.map(Mt), i = [];
|
|
1108
|
+
for (let t of Z(e)) {
|
|
1109
|
+
let e = Nt(t.url);
|
|
1110
|
+
e !== null && r.some((t) => t.test(e)) && i.push({
|
|
1111
|
+
blockId: t.blockId,
|
|
1112
|
+
params: { host: e }
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
return i;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
];
|
|
1119
|
+
function Pt(e, t = {}) {
|
|
1120
|
+
if (t.disabled === !0 || t.links === !1) return [];
|
|
1121
|
+
let n = t.links ?? {};
|
|
1122
|
+
return b(e, $, ce(t.locale, n, $), (e, t, n) => X(e, t, n));
|
|
1123
|
+
}
|
|
1124
|
+
//#endregion
|
|
1125
|
+
//#region src/util.ts
|
|
1126
|
+
function Ft(e) {
|
|
1127
|
+
return e ? e.disabled === !0 ? !0 : e.accessibility === !1 && e.structure === !1 && e.links === !1 : !1;
|
|
695
1128
|
}
|
|
696
1129
|
//#endregion
|
|
697
|
-
export {
|
|
1130
|
+
export { L as ACCESSIBILITY_RULES, f as DEFAULT_A11Y_THRESHOLDS, p as DEFAULT_NON_PRODUCTION_HOSTS, $ as LINK_RULES, G as STRUCTURE_RULES, we as SUPPORTED_DICTIONARY_LOCALES, gt as SUPPORTED_LINK_MESSAGE_LOCALES, fe as SUPPORTED_MESSAGE_LOCALES, tt as SUPPORTED_STRUCTURE_MESSAGE_LOCALES, M as extractAnchors, N as extractText, X as formatLinkMessage, E as formatMessage, H as formatStructureMessage, m as getContrastRatio, k as getDictionary, Y as getLinkMessages, T as getMessages, V as getStructureMessages, Ft as isLintFullyDisabled, g as isOpaqueHex, Ze as lintAccessibility, Pt as lintLinks, ft as lintStructure, h as parseHex, y as walkBlocks, Z as walkUrls };
|
|
698
1131
|
|
|
699
1132
|
//# sourceMappingURL=index.js.map
|