@happyvertical/smrt-content 0.37.2 → 0.37.4
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/body-format.js +334 -515
- package/dist/body-format.js.map +1 -1
- package/dist/chunks/content-transparency-OvUNs-bU.js +587 -0
- package/dist/chunks/content-transparency-OvUNs-bU.js.map +1 -0
- package/dist/content-editor-assistant.js +76 -84
- package/dist/content-editor-assistant.js.map +1 -1
- package/dist/index.js +11156 -11547
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/mock-smrt-client.js +300 -382
- package/dist/mock-smrt-client.js.map +1 -1
- package/dist/playground.js +364 -428
- package/dist/playground.js.map +1 -1
- package/dist/publish-readiness.js +49 -70
- package/dist/publish-readiness.js.map +1 -1
- package/dist/smrt-knowledge.json +15 -15
- package/dist/ui.js +48 -40
- package/dist/ui.js.map +1 -1
- package/package.json +34 -34
package/dist/body-format.js
CHANGED
|
@@ -1,604 +1,423 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
//#region src/body-format.ts
|
|
2
|
+
var DEFAULT_CONTENT_BODY_FORMAT = "html";
|
|
3
|
+
var HTML_TAG_PATTERN = /<\/?(?:article|aside|blockquote|br|div|figure|figcaption|h[1-6]|hr|img|li|ol|p|pre|section|span|strong|em|b|i|u|a|ul|table|tbody|td|th|thead|tr)(?:\s[^>]*)?>/i;
|
|
4
|
+
var ATTR_BOUNDARY = "(?:([\\s/]+)|(?<=[\"'`]))";
|
|
4
5
|
function reemitSeparator(consumed) {
|
|
5
|
-
|
|
6
|
+
return consumed ? " " : "";
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"tbody",
|
|
44
|
-
"td",
|
|
45
|
-
"th",
|
|
46
|
-
"thead",
|
|
47
|
-
"tr",
|
|
48
|
-
"ul"
|
|
8
|
+
var URL_ATTRIBUTE_PATTERN = new RegExp(`${ATTR_BOUNDARY}(href|src|xlink:href|formaction|action|poster)\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`, "gi");
|
|
9
|
+
var SRCSET_ATTRIBUTE_PATTERN = new RegExp(`${ATTR_BOUNDARY}srcset\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`, "gi");
|
|
10
|
+
var BLOCK_TAGS = [
|
|
11
|
+
"address",
|
|
12
|
+
"article",
|
|
13
|
+
"aside",
|
|
14
|
+
"blockquote",
|
|
15
|
+
"div",
|
|
16
|
+
"dl",
|
|
17
|
+
"fieldset",
|
|
18
|
+
"figcaption",
|
|
19
|
+
"figure",
|
|
20
|
+
"footer",
|
|
21
|
+
"form",
|
|
22
|
+
"h1",
|
|
23
|
+
"h2",
|
|
24
|
+
"h3",
|
|
25
|
+
"h4",
|
|
26
|
+
"h5",
|
|
27
|
+
"h6",
|
|
28
|
+
"header",
|
|
29
|
+
"hr",
|
|
30
|
+
"li",
|
|
31
|
+
"main",
|
|
32
|
+
"nav",
|
|
33
|
+
"ol",
|
|
34
|
+
"p",
|
|
35
|
+
"pre",
|
|
36
|
+
"section",
|
|
37
|
+
"table",
|
|
38
|
+
"tbody",
|
|
39
|
+
"td",
|
|
40
|
+
"th",
|
|
41
|
+
"thead",
|
|
42
|
+
"tr",
|
|
43
|
+
"ul"
|
|
49
44
|
];
|
|
50
45
|
function isContentBodyFormat(value) {
|
|
51
|
-
|
|
46
|
+
return value === "markdown" || value === "html";
|
|
52
47
|
}
|
|
53
48
|
function looksLikeHtml(value) {
|
|
54
|
-
|
|
49
|
+
return typeof value === "string" && HTML_TAG_PATTERN.test(value);
|
|
55
50
|
}
|
|
56
51
|
function resolveBodyFormat(format, body = "") {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return format.toLowerCase();
|
|
62
|
-
}
|
|
63
|
-
if (looksLikeHtml(body)) {
|
|
64
|
-
return "html";
|
|
65
|
-
}
|
|
66
|
-
return body ? "markdown" : DEFAULT_CONTENT_BODY_FORMAT;
|
|
52
|
+
if (isContentBodyFormat(format)) return format;
|
|
53
|
+
if (typeof format === "string" && isContentBodyFormat(format.toLowerCase())) return format.toLowerCase();
|
|
54
|
+
if (looksLikeHtml(body)) return "html";
|
|
55
|
+
return body ? "markdown" : DEFAULT_CONTENT_BODY_FORMAT;
|
|
67
56
|
}
|
|
68
57
|
function escapeHtml(value) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
case "'":
|
|
80
|
-
return "'";
|
|
81
|
-
default:
|
|
82
|
-
return char;
|
|
83
|
-
}
|
|
84
|
-
});
|
|
58
|
+
return value.replace(/[&<>"']/g, (char) => {
|
|
59
|
+
switch (char) {
|
|
60
|
+
case "&": return "&";
|
|
61
|
+
case "<": return "<";
|
|
62
|
+
case ">": return ">";
|
|
63
|
+
case "\"": return """;
|
|
64
|
+
case "'": return "'";
|
|
65
|
+
default: return char;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
85
68
|
}
|
|
86
69
|
function escapeAttribute(value) {
|
|
87
|
-
|
|
70
|
+
return escapeHtml(value).replace(/`/g, "`");
|
|
88
71
|
}
|
|
89
72
|
function decodeBasicEntities(value) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return decodeCodePoint(codePoint);
|
|
97
|
-
}).replace(/ /gi, " ").replace(/"/gi, '"').replace(/'/gi, "'").replace(/'/gi, "'").replace(/</gi, "<").replace(/>/gi, ">").replace(/&/gi, "&");
|
|
73
|
+
const decodeCodePoint = (codePoint) => Number.isFinite(codePoint) && codePoint >= 0 && codePoint <= 1114111 ? String.fromCodePoint(codePoint) : "";
|
|
74
|
+
return value.replace(/&#x([0-9a-f]+);?/gi, (_match, hex) => {
|
|
75
|
+
return decodeCodePoint(Number.parseInt(hex, 16));
|
|
76
|
+
}).replace(/&#(\d+);?/g, (_match, decimal) => {
|
|
77
|
+
return decodeCodePoint(Number.parseInt(decimal, 10));
|
|
78
|
+
}).replace(/ /gi, " ").replace(/"/gi, "\"").replace(/'/gi, "'").replace(/'/gi, "'").replace(/</gi, "<").replace(/>/gi, ">").replace(/&/gi, "&");
|
|
98
79
|
}
|
|
99
80
|
function sanitizeUrl(value) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
if (/^(?:javascript|vbscript):/.test(compactScheme)) {
|
|
113
|
-
return "#";
|
|
114
|
-
}
|
|
115
|
-
if (compactScheme.startsWith("data:") && !/^data:image\/(?:png|gif|jpe?g|webp);/.test(compactScheme)) {
|
|
116
|
-
return "#";
|
|
117
|
-
}
|
|
118
|
-
return trimmed;
|
|
81
|
+
const trimmed = decodeBasicEntities(value).trim();
|
|
82
|
+
if (!trimmed) return "";
|
|
83
|
+
let compactScheme = "";
|
|
84
|
+
for (const char of trimmed) {
|
|
85
|
+
const code = char.charCodeAt(0);
|
|
86
|
+
if (code <= 31 || code === 127 || char.trim() === "") continue;
|
|
87
|
+
compactScheme += char.toLowerCase();
|
|
88
|
+
}
|
|
89
|
+
if (/^(?:javascript|vbscript):/.test(compactScheme)) return "#";
|
|
90
|
+
if (compactScheme.startsWith("data:") && !/^data:image\/(?:png|gif|jpe?g|webp);/.test(compactScheme)) return "#";
|
|
91
|
+
return trimmed;
|
|
119
92
|
}
|
|
120
93
|
function sanitizeSrcset(value) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const descriptors = parts.filter(
|
|
128
|
-
(part) => /^(?:\d+(?:\.\d+)?x|\d+w)$/.test(part)
|
|
129
|
-
);
|
|
130
|
-
return [url, ...descriptors].join(" ");
|
|
131
|
-
}).filter(Boolean).join(", ");
|
|
94
|
+
return decodeBasicEntities(value).split(",").map((candidate) => {
|
|
95
|
+
const parts = candidate.trim().split(/\s+/);
|
|
96
|
+
const url = sanitizeUrl(parts.shift() || "");
|
|
97
|
+
if (!url || url === "#") return "";
|
|
98
|
+
return [url, ...parts.filter((part) => /^(?:\d+(?:\.\d+)?x|\d+w)$/.test(part))].join(" ");
|
|
99
|
+
}).filter(Boolean).join(", ");
|
|
132
100
|
}
|
|
133
101
|
function sanitizeStyle(value) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
safeRules.push("height: auto");
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return safeRules.join("; ");
|
|
102
|
+
const safeRules = [];
|
|
103
|
+
for (const rawRule of decodeBasicEntities(value).split(";")) {
|
|
104
|
+
const [rawName, ...rawValueParts] = rawRule.split(":");
|
|
105
|
+
const name = rawName?.trim().toLowerCase();
|
|
106
|
+
const ruleValue = rawValueParts.join(":").trim().toLowerCase();
|
|
107
|
+
if (!name || !ruleValue) continue;
|
|
108
|
+
if ((name === "width" || name === "max-width") && /^(?:\d{1,4}(?:\.\d+)?px|100%)$/.test(ruleValue)) {
|
|
109
|
+
safeRules.push(`${name}: ${ruleValue}`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (name === "height" && ruleValue === "auto") safeRules.push("height: auto");
|
|
113
|
+
}
|
|
114
|
+
return safeRules.join("; ");
|
|
151
115
|
}
|
|
152
116
|
function sanitizeHtml(value) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
"gi"
|
|
177
|
-
),
|
|
178
|
-
""
|
|
179
|
-
);
|
|
180
|
-
html = html.replace(
|
|
181
|
-
new RegExp(
|
|
182
|
-
`${ATTR_BOUNDARY}style\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`,
|
|
183
|
-
"gi"
|
|
184
|
-
),
|
|
185
|
-
(_match, separator, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
186
|
-
const safeStyle = sanitizeStyle(doubleValue || singleValue || bareValue);
|
|
187
|
-
const sep = reemitSeparator(separator);
|
|
188
|
-
return safeStyle ? `${sep}style="${escapeAttribute(safeStyle)}"` : sep;
|
|
189
|
-
}
|
|
190
|
-
);
|
|
191
|
-
html = html.replace(
|
|
192
|
-
SRCSET_ATTRIBUTE_PATTERN,
|
|
193
|
-
(_match, separator, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
194
|
-
const safeSrcset = sanitizeSrcset(
|
|
195
|
-
doubleValue || singleValue || bareValue
|
|
196
|
-
);
|
|
197
|
-
const sep = reemitSeparator(separator);
|
|
198
|
-
return safeSrcset ? `${sep}srcset="${escapeAttribute(safeSrcset)}"` : sep;
|
|
199
|
-
}
|
|
200
|
-
);
|
|
201
|
-
html = html.replace(
|
|
202
|
-
URL_ATTRIBUTE_PATTERN,
|
|
203
|
-
(_match, separator, name, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
204
|
-
const quote = doubleValue ? '"' : singleValue ? "'" : '"';
|
|
205
|
-
const rawValue = doubleValue || singleValue || bareValue;
|
|
206
|
-
return `${reemitSeparator(separator)}${name}=${quote}${escapeAttribute(sanitizeUrl(rawValue))}${quote}`;
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
return html.trim();
|
|
117
|
+
if (!value || typeof value !== "string") return "";
|
|
118
|
+
let html = value;
|
|
119
|
+
html = html.replace(/<!--[\s\S]*?-->/g, "");
|
|
120
|
+
html = html.replace(/<\s*(script|style|iframe|object|embed|link|meta|base|svg|math)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi, "");
|
|
121
|
+
html = html.replace(/<\s*(script|style|iframe|object|embed|link|meta|base|svg|math)\b[^>]*\/?>/gi, "");
|
|
122
|
+
html = html.replace(new RegExp(`${ATTR_BOUNDARY}on[a-z]+\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>]*)`, "gi"), "");
|
|
123
|
+
html = html.replace(new RegExp(`${ATTR_BOUNDARY}data-smrt-(?:selected|moving|resizing)\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>]*)`, "gi"), "");
|
|
124
|
+
html = html.replace(new RegExp(`${ATTR_BOUNDARY}style\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`, "gi"), (_match, separator, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
125
|
+
const safeStyle = sanitizeStyle(doubleValue || singleValue || bareValue);
|
|
126
|
+
const sep = reemitSeparator(separator);
|
|
127
|
+
return safeStyle ? `${sep}style="${escapeAttribute(safeStyle)}"` : sep;
|
|
128
|
+
});
|
|
129
|
+
html = html.replace(SRCSET_ATTRIBUTE_PATTERN, (_match, separator, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
130
|
+
const safeSrcset = sanitizeSrcset(doubleValue || singleValue || bareValue);
|
|
131
|
+
const sep = reemitSeparator(separator);
|
|
132
|
+
return safeSrcset ? `${sep}srcset="${escapeAttribute(safeSrcset)}"` : sep;
|
|
133
|
+
});
|
|
134
|
+
html = html.replace(URL_ATTRIBUTE_PATTERN, (_match, separator, name, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
135
|
+
const quote = doubleValue ? "\"" : singleValue ? "'" : "\"";
|
|
136
|
+
const rawValue = doubleValue || singleValue || bareValue;
|
|
137
|
+
return `${reemitSeparator(separator)}${name}=${quote}${escapeAttribute(sanitizeUrl(rawValue))}${quote}`;
|
|
138
|
+
});
|
|
139
|
+
return html.trim();
|
|
210
140
|
}
|
|
211
141
|
function renderInlineMarkdown(value) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
);
|
|
229
|
-
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
230
|
-
html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
231
|
-
html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
|
232
|
-
return html;
|
|
142
|
+
let html = value;
|
|
143
|
+
html = html.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_match, alt, src, title = "") => {
|
|
144
|
+
const safeSrc = sanitizeUrl(src);
|
|
145
|
+
const safeAlt = escapeAttribute(decodeBasicEntities(alt));
|
|
146
|
+
const titleAttr = title ? ` title="${escapeAttribute(decodeBasicEntities(title))}"` : "";
|
|
147
|
+
return `<img src="${escapeAttribute(safeSrc)}" alt="${safeAlt}"${titleAttr}>`;
|
|
148
|
+
});
|
|
149
|
+
html = html.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_match, label, href, title = "") => {
|
|
150
|
+
const titleAttr = title ? ` title="${escapeAttribute(decodeBasicEntities(title))}"` : "";
|
|
151
|
+
return `<a href="${escapeAttribute(sanitizeUrl(href))}"${titleAttr}>${label}</a>`;
|
|
152
|
+
});
|
|
153
|
+
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
154
|
+
html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
155
|
+
html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
|
156
|
+
return html;
|
|
233
157
|
}
|
|
234
158
|
function renderMarkdownToHtml(markdown) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
result.push(renderInlineMarkdown(line));
|
|
289
|
-
}
|
|
290
|
-
closeList();
|
|
291
|
-
closeParagraph();
|
|
292
|
-
return sanitizeHtml(result.join("\n"));
|
|
159
|
+
if (!markdown || typeof markdown !== "string") return "";
|
|
160
|
+
const lines = escapeHtml(markdown).split("\n");
|
|
161
|
+
const result = [];
|
|
162
|
+
let inList = false;
|
|
163
|
+
let inParagraph = false;
|
|
164
|
+
function closeParagraph() {
|
|
165
|
+
if (inParagraph) {
|
|
166
|
+
result.push("</p>");
|
|
167
|
+
inParagraph = false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function closeList() {
|
|
171
|
+
if (inList) {
|
|
172
|
+
result.push("</ul>");
|
|
173
|
+
inList = false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (const line of lines) {
|
|
177
|
+
const trimmed = line.trim();
|
|
178
|
+
if (!trimmed) {
|
|
179
|
+
closeList();
|
|
180
|
+
closeParagraph();
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const headingMatch = /^(#{1,3})\s+(.+)$/.exec(trimmed);
|
|
184
|
+
if (headingMatch) {
|
|
185
|
+
closeList();
|
|
186
|
+
closeParagraph();
|
|
187
|
+
const level = headingMatch[1].length;
|
|
188
|
+
result.push(`<h${level}>${renderInlineMarkdown(headingMatch[2])}</h${level}>`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const listMatch = /^[-*]\s+(.+)$/.exec(trimmed);
|
|
192
|
+
if (listMatch) {
|
|
193
|
+
closeParagraph();
|
|
194
|
+
if (!inList) {
|
|
195
|
+
result.push("<ul>");
|
|
196
|
+
inList = true;
|
|
197
|
+
}
|
|
198
|
+
result.push(`<li>${renderInlineMarkdown(listMatch[1])}</li>`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
closeList();
|
|
202
|
+
if (!inParagraph) {
|
|
203
|
+
result.push("<p>");
|
|
204
|
+
inParagraph = true;
|
|
205
|
+
} else result.push("<br>");
|
|
206
|
+
result.push(renderInlineMarkdown(line));
|
|
207
|
+
}
|
|
208
|
+
closeList();
|
|
209
|
+
closeParagraph();
|
|
210
|
+
return sanitizeHtml(result.join("\n"));
|
|
293
211
|
}
|
|
294
212
|
function parseHtmlAttributes(value) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
);
|
|
302
|
-
return "";
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
return attributes;
|
|
213
|
+
const attributes = {};
|
|
214
|
+
value.replace(/([:\w-]+)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/g, (_match, name, _raw, doubleValue = "", singleValue = "", bareValue = "") => {
|
|
215
|
+
attributes[name.toLowerCase()] = decodeBasicEntities(doubleValue || singleValue || bareValue || "");
|
|
216
|
+
return "";
|
|
217
|
+
});
|
|
218
|
+
return attributes;
|
|
306
219
|
}
|
|
307
220
|
function normalizeImagePlacement(value) {
|
|
308
|
-
|
|
221
|
+
return value === "block" || value === "left" || value === "right" || value === "center" || value === "full" ? value : void 0;
|
|
309
222
|
}
|
|
310
223
|
function parseImageWidth(value) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return void 0;
|
|
317
|
-
}
|
|
318
|
-
const width = Math.round(Number(match[1]));
|
|
319
|
-
return Number.isFinite(width) && width > 0 ? width : void 0;
|
|
224
|
+
if (typeof value !== "string" && typeof value !== "number") return;
|
|
225
|
+
const match = String(value).match(/(\d+(?:\.\d+)?)/);
|
|
226
|
+
if (!match) return;
|
|
227
|
+
const width = Math.round(Number(match[1]));
|
|
228
|
+
return Number.isFinite(width) && width > 0 ? width : void 0;
|
|
320
229
|
}
|
|
321
230
|
function parseStyleWidth(value) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
/(?:^|;)\s*(?:max-)?width\s*:\s*(\d+(?:\.\d+)?)px/i
|
|
327
|
-
);
|
|
328
|
-
if (!match) {
|
|
329
|
-
return void 0;
|
|
330
|
-
}
|
|
331
|
-
return parseImageWidth(match[1]);
|
|
231
|
+
if (typeof value !== "string") return;
|
|
232
|
+
const match = value.match(/(?:^|;)\s*(?:max-)?width\s*:\s*(\d+(?:\.\d+)?)px/i);
|
|
233
|
+
if (!match) return;
|
|
234
|
+
return parseImageWidth(match[1]);
|
|
332
235
|
}
|
|
333
236
|
function fallbackHtmlToMarkdown(html) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
return `
|
|
237
|
+
let markdown = sanitizeHtml(html);
|
|
238
|
+
markdown = markdown.replace(/<img\b([^>]*)>/gi, (_match, attrs) => {
|
|
239
|
+
const parsed = parseHtmlAttributes(attrs);
|
|
240
|
+
const src = sanitizeUrl(parsed.src || "");
|
|
241
|
+
if (!src) return "";
|
|
242
|
+
return `
|
|
342
243
|
|
|
343
244
|

|
|
344
245
|
|
|
345
246
|
`;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
markdown = markdown.replace(/<li\b[^>]*>([\s\S]*?)<\/li>/gi, "\n- $1");
|
|
363
|
-
markdown = markdown.replace(/<\/(?:p|div|section|article|ul|ol)>/gi, "\n\n");
|
|
364
|
-
markdown = markdown.replace(/<br\s*\/?>/gi, "\n");
|
|
365
|
-
markdown = markdown.replace(
|
|
366
|
-
/<(strong|b)\b[^>]*>([\s\S]*?)<\/\1>/gi,
|
|
367
|
-
"**$2**"
|
|
368
|
-
);
|
|
369
|
-
markdown = markdown.replace(/<(em|i)\b[^>]*>([\s\S]*?)<\/\1>/gi, "*$2*");
|
|
370
|
-
markdown = stripHtml(markdown);
|
|
371
|
-
return normalizeMarkdownWhitespace(markdown);
|
|
247
|
+
});
|
|
248
|
+
markdown = markdown.replace(/<a\b([^>]*)>([\s\S]*?)<\/a>/gi, (_match, attrs, label) => {
|
|
249
|
+
const href = sanitizeUrl(parseHtmlAttributes(attrs).href || "");
|
|
250
|
+
const text = stripHtml(label).trim() || href;
|
|
251
|
+
return href ? `[${text}](${href})` : text;
|
|
252
|
+
});
|
|
253
|
+
markdown = markdown.replace(/<h1\b[^>]*>([\s\S]*?)<\/h1>/gi, "\n\n# $1\n\n");
|
|
254
|
+
markdown = markdown.replace(/<h2\b[^>]*>([\s\S]*?)<\/h2>/gi, "\n\n## $1\n\n");
|
|
255
|
+
markdown = markdown.replace(/<h3\b[^>]*>([\s\S]*?)<\/h3>/gi, "\n\n### $1\n\n");
|
|
256
|
+
markdown = markdown.replace(/<li\b[^>]*>([\s\S]*?)<\/li>/gi, "\n- $1");
|
|
257
|
+
markdown = markdown.replace(/<\/(?:p|div|section|article|ul|ol)>/gi, "\n\n");
|
|
258
|
+
markdown = markdown.replace(/<br\s*\/?>/gi, "\n");
|
|
259
|
+
markdown = markdown.replace(/<(strong|b)\b[^>]*>([\s\S]*?)<\/\1>/gi, "**$2**");
|
|
260
|
+
markdown = markdown.replace(/<(em|i)\b[^>]*>([\s\S]*?)<\/\1>/gi, "*$2*");
|
|
261
|
+
markdown = stripHtml(markdown);
|
|
262
|
+
return normalizeMarkdownWhitespace(markdown);
|
|
372
263
|
}
|
|
373
264
|
function stripHtml(value) {
|
|
374
|
-
|
|
265
|
+
return decodeBasicEntities(value.replace(/<[^>]*>/g, ""));
|
|
375
266
|
}
|
|
376
267
|
function normalizeMarkdownWhitespace(value) {
|
|
377
|
-
|
|
268
|
+
return decodeBasicEntities(value).replace(/\r\n?/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
378
269
|
}
|
|
379
|
-
|
|
380
|
-
|
|
270
|
+
var MARKDOWN_TEXT_NODE = 3;
|
|
271
|
+
var MARKDOWN_ELEMENT_NODE = 1;
|
|
381
272
|
function nodeToMarkdown(node) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const trimmedChildren = children.trim();
|
|
391
|
-
switch (tag) {
|
|
392
|
-
case "br":
|
|
393
|
-
return "\n";
|
|
394
|
-
case "h1":
|
|
395
|
-
return `
|
|
273
|
+
if (node.nodeType === MARKDOWN_TEXT_NODE) return node.textContent || "";
|
|
274
|
+
if (node.nodeType !== MARKDOWN_ELEMENT_NODE) return "";
|
|
275
|
+
const tag = node.tagName?.toLowerCase() || "";
|
|
276
|
+
const children = Array.from(node.childNodes || []).map(nodeToMarkdown).join("");
|
|
277
|
+
const trimmedChildren = children.trim();
|
|
278
|
+
switch (tag) {
|
|
279
|
+
case "br": return "\n";
|
|
280
|
+
case "h1": return `
|
|
396
281
|
|
|
397
282
|
# ${trimmedChildren}
|
|
398
283
|
|
|
399
284
|
`;
|
|
400
|
-
|
|
401
|
-
return `
|
|
285
|
+
case "h2": return `
|
|
402
286
|
|
|
403
287
|
## ${trimmedChildren}
|
|
404
288
|
|
|
405
289
|
`;
|
|
406
|
-
|
|
407
|
-
return `
|
|
290
|
+
case "h3": return `
|
|
408
291
|
|
|
409
292
|
### ${trimmedChildren}
|
|
410
293
|
|
|
411
294
|
`;
|
|
412
|
-
|
|
413
|
-
return `
|
|
295
|
+
case "p": return `
|
|
414
296
|
|
|
415
297
|
${trimmedChildren}
|
|
416
298
|
|
|
417
299
|
`;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const src = sanitizeUrl(node.getAttribute?.("src") || "");
|
|
432
|
-
if (!src) {
|
|
433
|
-
return "";
|
|
434
|
-
}
|
|
435
|
-
const alt = node.getAttribute?.("alt") || "";
|
|
436
|
-
return `
|
|
300
|
+
case "strong":
|
|
301
|
+
case "b": return `**${trimmedChildren}**`;
|
|
302
|
+
case "em":
|
|
303
|
+
case "i": return `*${trimmedChildren}*`;
|
|
304
|
+
case "code": return `\`${trimmedChildren}\``;
|
|
305
|
+
case "a": {
|
|
306
|
+
const href = sanitizeUrl(node.getAttribute?.("href") || "");
|
|
307
|
+
return href ? `[${trimmedChildren || href}](${href})` : trimmedChildren;
|
|
308
|
+
}
|
|
309
|
+
case "img": {
|
|
310
|
+
const src = sanitizeUrl(node.getAttribute?.("src") || "");
|
|
311
|
+
if (!src) return "";
|
|
312
|
+
return `
|
|
437
313
|
|
|
438
|
-

|
|
314
|
+

|
|
439
315
|
|
|
440
316
|
`;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return `- ${trimmedChildren}
|
|
317
|
+
}
|
|
318
|
+
case "li": return `- ${trimmedChildren}
|
|
444
319
|
`;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return `
|
|
320
|
+
case "ul":
|
|
321
|
+
case "ol": return `
|
|
448
322
|
${children}
|
|
449
323
|
`;
|
|
450
|
-
|
|
451
|
-
return BLOCK_TAGS.includes(tag) ? `
|
|
324
|
+
default: return BLOCK_TAGS.includes(tag) ? `
|
|
452
325
|
|
|
453
326
|
${trimmedChildren}
|
|
454
327
|
|
|
455
328
|
` : children;
|
|
456
|
-
|
|
329
|
+
}
|
|
457
330
|
}
|
|
458
331
|
function htmlToMarkdown(html) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
return fallbackHtmlToMarkdown(sanitized);
|
|
466
|
-
}
|
|
467
|
-
const parser = new Parser();
|
|
468
|
-
const document = parser.parseFromString(
|
|
469
|
-
`<body>${sanitized}</body>`,
|
|
470
|
-
"text/html"
|
|
471
|
-
);
|
|
472
|
-
const markdown = Array.from(
|
|
473
|
-
document.body?.childNodes || []
|
|
474
|
-
).map(nodeToMarkdown).join("");
|
|
475
|
-
return normalizeMarkdownWhitespace(markdown);
|
|
332
|
+
const sanitized = sanitizeHtml(html);
|
|
333
|
+
if (!sanitized) return "";
|
|
334
|
+
const Parser = globalThis.DOMParser;
|
|
335
|
+
if (typeof Parser !== "function") return fallbackHtmlToMarkdown(sanitized);
|
|
336
|
+
const document = new Parser().parseFromString(`<body>${sanitized}</body>`, "text/html");
|
|
337
|
+
return normalizeMarkdownWhitespace(Array.from(document.body?.childNodes || []).map(nodeToMarkdown).join(""));
|
|
476
338
|
}
|
|
477
339
|
function normalizeEditorHtml(html) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
return sanitized.replace(
|
|
483
|
-
/<p>\s*((?:<br\s*\/?>\s*)?<img\b[^>]*>(?:\s*<br\s*\/?>)?)\s*<\/p>/gi,
|
|
484
|
-
(_match, imageHtml) => imageHtml.replace(/^\s*<br\s*\/?>\s*|\s*<br\s*\/?>\s*$/gi, "")
|
|
485
|
-
);
|
|
340
|
+
const sanitized = sanitizeHtml(html);
|
|
341
|
+
if (!sanitized) return "";
|
|
342
|
+
return sanitized.replace(/<p>\s*((?:<br\s*\/?>\s*)?<img\b[^>]*>(?:\s*<br\s*\/?>)?)\s*<\/p>/gi, (_match, imageHtml) => imageHtml.replace(/^\s*<br\s*\/?>\s*|\s*<br\s*\/?>\s*$/gi, ""));
|
|
486
343
|
}
|
|
487
344
|
function bodyToEditorHtml(body, format) {
|
|
488
|
-
|
|
345
|
+
return format === "markdown" ? normalizeEditorHtml(renderMarkdownToHtml(body || "")) : normalizeEditorHtml(body || "");
|
|
489
346
|
}
|
|
490
347
|
function editorHtmlToBody(html, format) {
|
|
491
|
-
|
|
348
|
+
return format === "markdown" ? htmlToMarkdown(html || "") : normalizeEditorHtml(html || "");
|
|
492
349
|
}
|
|
493
350
|
function extractBodyImages(body, format = resolveBodyFormat(void 0, body)) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const src = sanitizeUrl(parsed.src || "");
|
|
550
|
-
if (src) {
|
|
551
|
-
const placement = normalizeImagePlacement(parsed["data-smrt-placement"]);
|
|
552
|
-
const width = parseImageWidth(parsed["data-smrt-width"]) || parseStyleWidth(parsed.style);
|
|
553
|
-
images.push({
|
|
554
|
-
src,
|
|
555
|
-
alt: parsed.alt || "",
|
|
556
|
-
...parsed.title ? { title: parsed.title } : {},
|
|
557
|
-
...parsed["data-smrt-asset-id"] ? { assetId: parsed["data-smrt-asset-id"] } : {},
|
|
558
|
-
...placement ? { placement } : {},
|
|
559
|
-
...width ? { width } : {},
|
|
560
|
-
index: images.length
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
return "";
|
|
564
|
-
});
|
|
565
|
-
return images;
|
|
351
|
+
if (!body || typeof body !== "string") return [];
|
|
352
|
+
if (format === "markdown") {
|
|
353
|
+
const images2 = [];
|
|
354
|
+
body.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_match, alt, src, title = "") => {
|
|
355
|
+
const safeSrc = sanitizeUrl(src);
|
|
356
|
+
if (safeSrc) images2.push({
|
|
357
|
+
src: safeSrc,
|
|
358
|
+
alt: decodeBasicEntities(alt),
|
|
359
|
+
...title ? { title: decodeBasicEntities(title) } : {},
|
|
360
|
+
index: images2.length
|
|
361
|
+
});
|
|
362
|
+
return "";
|
|
363
|
+
});
|
|
364
|
+
return images2;
|
|
365
|
+
}
|
|
366
|
+
const images = [];
|
|
367
|
+
body.replace(/<figure\b([^>]*)>([\s\S]*?)<\/figure>/gi, (_match, figureAttrs, figureContent) => {
|
|
368
|
+
const imageMatch = /<img\b([^>]*)>/i.exec(figureContent);
|
|
369
|
+
if (!imageMatch) return "";
|
|
370
|
+
const parsedFigure = parseHtmlAttributes(figureAttrs);
|
|
371
|
+
const parsedImage = parseHtmlAttributes(imageMatch[1] || "");
|
|
372
|
+
const src = sanitizeUrl(parsedImage.src || "");
|
|
373
|
+
if (src) {
|
|
374
|
+
const placement = normalizeImagePlacement(parsedFigure["data-smrt-placement"] || parsedImage["data-smrt-placement"]);
|
|
375
|
+
const width = parseImageWidth(parsedFigure["data-smrt-width"] || parsedImage["data-smrt-width"]) || parseStyleWidth(parsedFigure.style) || parseStyleWidth(parsedImage.style);
|
|
376
|
+
images.push({
|
|
377
|
+
src,
|
|
378
|
+
alt: parsedImage.alt || "",
|
|
379
|
+
...parsedImage.title ? { title: parsedImage.title } : {},
|
|
380
|
+
...parsedImage["data-smrt-asset-id"] ? { assetId: parsedImage["data-smrt-asset-id"] } : {},
|
|
381
|
+
...placement ? { placement } : {},
|
|
382
|
+
...width ? { width } : {},
|
|
383
|
+
index: images.length
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return "";
|
|
387
|
+
}).replace(/<img\b([^>]*)>/gi, (_match, attrs) => {
|
|
388
|
+
const parsed = parseHtmlAttributes(attrs);
|
|
389
|
+
const src = sanitizeUrl(parsed.src || "");
|
|
390
|
+
if (src) {
|
|
391
|
+
const placement = normalizeImagePlacement(parsed["data-smrt-placement"]);
|
|
392
|
+
const width = parseImageWidth(parsed["data-smrt-width"]) || parseStyleWidth(parsed.style);
|
|
393
|
+
images.push({
|
|
394
|
+
src,
|
|
395
|
+
alt: parsed.alt || "",
|
|
396
|
+
...parsed.title ? { title: parsed.title } : {},
|
|
397
|
+
...parsed["data-smrt-asset-id"] ? { assetId: parsed["data-smrt-asset-id"] } : {},
|
|
398
|
+
...placement ? { placement } : {},
|
|
399
|
+
...width ? { width } : {},
|
|
400
|
+
index: images.length
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return "";
|
|
404
|
+
});
|
|
405
|
+
return images;
|
|
566
406
|
}
|
|
567
407
|
function getImageSource(asset) {
|
|
568
|
-
|
|
408
|
+
return String(asset?.sourceUri || asset?.url || asset?.src || "");
|
|
569
409
|
}
|
|
570
410
|
function getImageAlt(asset) {
|
|
571
|
-
|
|
411
|
+
return String(asset?.alt || asset?.name || asset?.title || "Image");
|
|
572
412
|
}
|
|
573
413
|
function imageAssetToHtml(asset) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const width = Math.max(
|
|
580
|
-
160,
|
|
581
|
-
Math.min(520, Math.round(Number(asset?.width) || 520))
|
|
582
|
-
);
|
|
583
|
-
return `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(getImageAlt(asset))}"${assetId} data-smrt-inline-image="true" data-smrt-placement="block" data-smrt-width="${width}" style="width: ${width}px; max-width: 100%; height: auto">`;
|
|
414
|
+
const src = sanitizeUrl(getImageSource(asset));
|
|
415
|
+
if (!src) return "";
|
|
416
|
+
const assetId = asset?.id ? ` data-smrt-asset-id="${escapeAttribute(String(asset.id))}"` : "";
|
|
417
|
+
const width = Math.max(160, Math.min(520, Math.round(Number(asset?.width) || 520)));
|
|
418
|
+
return `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(getImageAlt(asset))}"${assetId} data-smrt-inline-image="true" data-smrt-placement="block" data-smrt-width="${width}" style="width: ${width}px; max-width: 100%; height: auto">`;
|
|
584
419
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
escapeAttribute,
|
|
590
|
-
escapeHtml,
|
|
591
|
-
extractBodyImages,
|
|
592
|
-
getImageAlt,
|
|
593
|
-
getImageSource,
|
|
594
|
-
htmlToMarkdown,
|
|
595
|
-
imageAssetToHtml,
|
|
596
|
-
isContentBodyFormat,
|
|
597
|
-
looksLikeHtml,
|
|
598
|
-
normalizeEditorHtml,
|
|
599
|
-
renderMarkdownToHtml,
|
|
600
|
-
resolveBodyFormat,
|
|
601
|
-
sanitizeHtml,
|
|
602
|
-
stripHtml
|
|
603
|
-
};
|
|
604
|
-
//# sourceMappingURL=body-format.js.map
|
|
420
|
+
//#endregion
|
|
421
|
+
export { DEFAULT_CONTENT_BODY_FORMAT, bodyToEditorHtml, editorHtmlToBody, escapeAttribute, escapeHtml, extractBodyImages, getImageAlt, getImageSource, htmlToMarkdown, imageAssetToHtml, isContentBodyFormat, looksLikeHtml, normalizeEditorHtml, renderMarkdownToHtml, resolveBodyFormat, sanitizeHtml, stripHtml };
|
|
422
|
+
|
|
423
|
+
//# sourceMappingURL=body-format.js.map
|