ya-bbcode 5.0.0 → 5.1.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/LICENSE +21 -21
- package/README.md +4 -2
- package/dist/ya-bbcode.cjs +164 -85
- package/dist/ya-bbcode.cjs.map +1 -1
- package/dist/ya-bbcode.d.cts +6 -4
- package/dist/ya-bbcode.d.mts +6 -4
- package/dist/ya-bbcode.mjs +153 -73
- package/dist/ya-bbcode.mjs.map +1 -1
- package/package.json +18 -17
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2018 Nodecraft Inc.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Nodecraft Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -3,8 +3,7 @@ Yet another BBCode parser.
|
|
|
3
3
|
`npm install ya-bbcode --save`
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/ya-bbcode)
|
|
6
|
-
[](https://coveralls.io/github/nodecraft/ya-bbcode)
|
|
6
|
+
[](https://github.com/nodecraft/ya-bbcode/actions/workflows/ci.yml)
|
|
8
7
|
|
|
9
8
|
## Usage
|
|
10
9
|
|
|
@@ -37,3 +36,6 @@ parser.clearTags();
|
|
|
37
36
|
- **Zero dependencies**: Lightweight with no external dependencies
|
|
38
37
|
- **Correct parsing**: Parent nodes are parsed before children in nested format
|
|
39
38
|
- **Customizable**: Easy to add or override tags
|
|
39
|
+
|
|
40
|
+
### Changelog
|
|
41
|
+
Release notes are published on [GitHub Releases](https://github.com/nodecraft/ya-bbcode/releases). Notes for 5.0.0 and earlier are in [CHANGELOG.md](CHANGELOG.md).
|
package/dist/ya-bbcode.cjs
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/ya-bbcode.ts
|
|
2
|
+
const PLACEHOLDER_START = "\0";
|
|
3
|
+
const PLACEHOLDER_END = ";";
|
|
4
|
+
function placeholder(index) {
|
|
5
|
+
return PLACEHOLDER_START + index + PLACEHOLDER_END;
|
|
6
|
+
}
|
|
7
|
+
function replaceFirst(content, search, replacement) {
|
|
8
|
+
const index = content.indexOf(search);
|
|
9
|
+
if (index === -1) return content;
|
|
10
|
+
return content.slice(0, index) + replacement + content.slice(index + search.length);
|
|
11
|
+
}
|
|
12
|
+
const sanitizedEntities = {
|
|
13
|
+
"&": "&",
|
|
14
|
+
"<": "<",
|
|
15
|
+
">": ">",
|
|
16
|
+
""": "\"",
|
|
17
|
+
"'": "'"
|
|
18
|
+
};
|
|
19
|
+
function unescapeSanitized(value) {
|
|
20
|
+
return value.replaceAll(/&(?:amp|lt|gt|quot|#39);/g, (entity) => sanitizedEntities[entity]);
|
|
21
|
+
}
|
|
3
22
|
/**
|
|
4
23
|
* Escapes HTML attribute values to prevent XSS
|
|
5
24
|
* Encodes quotes, angle brackets, and other special characters
|
|
@@ -14,12 +33,19 @@ function escapeHtmlAttr(value) {
|
|
|
14
33
|
};
|
|
15
34
|
return String(value).replaceAll(/["&'<>]/g, (char) => attrEntities[char] || char);
|
|
16
35
|
}
|
|
36
|
+
function normalizeUrl(url) {
|
|
37
|
+
let start = 0;
|
|
38
|
+
let end = url.length;
|
|
39
|
+
while (start < end && url.charAt(start) <= " ") start++;
|
|
40
|
+
while (end > start && url.charAt(end - 1) <= " ") end--;
|
|
41
|
+
return url.slice(start, end).replaceAll(/[\t\n\r]/g, "");
|
|
42
|
+
}
|
|
17
43
|
/**
|
|
18
44
|
* Validates and sanitizes URLs to prevent javascript: and data: URL attacks
|
|
19
45
|
* Only allows safe URL schemes based on the allowedSchemes list
|
|
20
46
|
*/
|
|
21
47
|
function sanitizeUrl(url, allowedSchemes) {
|
|
22
|
-
const trimmed = String(url)
|
|
48
|
+
const trimmed = normalizeUrl(String(url));
|
|
23
49
|
if (!trimmed) return "#";
|
|
24
50
|
if (/^\.{0,2}\//.test(trimmed)) return trimmed;
|
|
25
51
|
if (trimmed.startsWith("#")) return trimmed;
|
|
@@ -46,7 +72,7 @@ function parseTagAttributes(tagContent) {
|
|
|
46
72
|
const parts = tagContent.split(/\s+/);
|
|
47
73
|
const attrs = Object.create(null);
|
|
48
74
|
let firstValue = "";
|
|
49
|
-
const dangerousKeys = new Set([
|
|
75
|
+
const dangerousKeys = /* @__PURE__ */ new Set([
|
|
50
76
|
"__proto__",
|
|
51
77
|
"constructor",
|
|
52
78
|
"prototype"
|
|
@@ -75,7 +101,7 @@ var yabbcode = class {
|
|
|
75
101
|
tags = {
|
|
76
102
|
"url": {
|
|
77
103
|
type: "replace",
|
|
78
|
-
open: (attr) => `<a href="${escapeHtmlAttr(sanitizeUrl(attr, this.config.allowedSchemes ?? [
|
|
104
|
+
open: (attr) => `<a href="${escapeHtmlAttr(sanitizeUrl(this.#unsanitize(attr), this.config.allowedSchemes ?? [
|
|
79
105
|
"http",
|
|
80
106
|
"https",
|
|
81
107
|
"mailto",
|
|
@@ -86,7 +112,7 @@ var yabbcode = class {
|
|
|
86
112
|
},
|
|
87
113
|
"quote": {
|
|
88
114
|
type: "replace",
|
|
89
|
-
open: (attr) => `<blockquote author="${escapeHtmlAttr(attr)}">`,
|
|
115
|
+
open: (attr) => `<blockquote author="${escapeHtmlAttr(this.#unsanitize(attr))}">`,
|
|
90
116
|
close: "</blockquote>"
|
|
91
117
|
},
|
|
92
118
|
"b": {
|
|
@@ -139,6 +165,11 @@ var yabbcode = class {
|
|
|
139
165
|
open: "<code>",
|
|
140
166
|
close: "</code>"
|
|
141
167
|
},
|
|
168
|
+
"hr": {
|
|
169
|
+
type: "replace",
|
|
170
|
+
open: "<hr/>",
|
|
171
|
+
close: null
|
|
172
|
+
},
|
|
142
173
|
"strike": {
|
|
143
174
|
type: "replace",
|
|
144
175
|
open: "<span class=\"yabbcode-strike\">",
|
|
@@ -162,40 +193,41 @@ var yabbcode = class {
|
|
|
162
193
|
"*": {
|
|
163
194
|
type: "replace",
|
|
164
195
|
open: "<li>",
|
|
165
|
-
close:
|
|
196
|
+
close: "</li>",
|
|
197
|
+
autoClose: "</li>"
|
|
166
198
|
},
|
|
167
199
|
"img": {
|
|
168
200
|
type: "content",
|
|
169
201
|
replace: (attr, content) => {
|
|
170
202
|
if (!content) return "";
|
|
171
|
-
return `<img src="${escapeHtmlAttr(content)}" alt="${escapeHtmlAttr(attr)}"/>`;
|
|
203
|
+
return `<img src="${escapeHtmlAttr(this.#unsanitize(content))}" alt="${escapeHtmlAttr(this.#unsanitize(attr))}"/>`;
|
|
172
204
|
}
|
|
173
205
|
},
|
|
174
206
|
"noparse": { type: "ignore" }
|
|
175
207
|
};
|
|
176
208
|
regex = {
|
|
177
|
-
tags: /(\[[^\]^]
|
|
209
|
+
tags: /(\[[^\]^]+\])/g,
|
|
178
210
|
newline: /\r\n|\r|\n/g,
|
|
179
|
-
placeholders: /\
|
|
211
|
+
placeholders: /\0(\d+);/g
|
|
180
212
|
};
|
|
181
213
|
config;
|
|
182
214
|
contentModules = {
|
|
183
|
-
replace: (tag, module
|
|
184
|
-
let open = module
|
|
185
|
-
let close = module
|
|
215
|
+
replace: (tag, module, content) => {
|
|
216
|
+
let open = module.open;
|
|
217
|
+
let close = module.close;
|
|
186
218
|
if (typeof open === "function") open = open(tag.attr, tag.attrs);
|
|
187
219
|
if (typeof close === "function") close = close(tag.attr, tag.attrs);
|
|
188
|
-
if (open && !tag.isClosing) content = content
|
|
189
|
-
if (close && tag.closing) content = content
|
|
220
|
+
if (open && !tag.isClosing) content = replaceFirst(content, placeholder(tag.index), open);
|
|
221
|
+
if (close && tag.closing) content = replaceFirst(content, placeholder(tag.closing.index), close);
|
|
190
222
|
return content;
|
|
191
223
|
},
|
|
192
|
-
content: (tag, module
|
|
224
|
+
content: (tag, module, content) => {
|
|
193
225
|
if (!tag.closing) return content;
|
|
194
|
-
const openTag =
|
|
195
|
-
const closeTag =
|
|
226
|
+
const openTag = placeholder(tag.index);
|
|
227
|
+
const closeTag = placeholder(tag.closing.index);
|
|
196
228
|
const start = content.indexOf(openTag);
|
|
197
229
|
const end = content.indexOf(closeTag);
|
|
198
|
-
let replace = module
|
|
230
|
+
let replace = module.replace;
|
|
199
231
|
const innerContent = content.slice(start + openTag.length, end);
|
|
200
232
|
if (typeof replace === "function") replace = replace(tag.attr, innerContent, tag.attrs);
|
|
201
233
|
const contentStart = content.slice(0, Math.max(0, start));
|
|
@@ -203,12 +235,12 @@ var yabbcode = class {
|
|
|
203
235
|
return contentStart + replace + contentEnd;
|
|
204
236
|
},
|
|
205
237
|
ignore: (tag, _module, content) => {
|
|
206
|
-
const openTag =
|
|
238
|
+
const openTag = placeholder(tag.index);
|
|
207
239
|
const start = content.indexOf(openTag);
|
|
208
240
|
let closeTag = "";
|
|
209
241
|
let end = content.length;
|
|
210
242
|
if (tag.closing) {
|
|
211
|
-
closeTag =
|
|
243
|
+
closeTag = placeholder(tag.closing.index);
|
|
212
244
|
end = content.indexOf(closeTag);
|
|
213
245
|
}
|
|
214
246
|
let innerContent = content.slice(start + openTag.length, end);
|
|
@@ -238,56 +270,107 @@ var yabbcode = class {
|
|
|
238
270
|
if (config.sanitizeHtml !== void 0) this.config.sanitizeHtml = config.sanitizeHtml;
|
|
239
271
|
if (config.allowedSchemes !== void 0) this.config.allowedSchemes = config.allowedSchemes;
|
|
240
272
|
}
|
|
273
|
+
#getTag(name) {
|
|
274
|
+
if (!Object.hasOwn(this.tags, name)) return;
|
|
275
|
+
return this.tags[name];
|
|
276
|
+
}
|
|
277
|
+
#convertNewlines(input) {
|
|
278
|
+
if (!this.config.newline) return input;
|
|
279
|
+
if (this.config.paragraph) return input.replace(this.regex.newline, "</p><p>");
|
|
280
|
+
return input.replace(this.regex.newline, "<br/>");
|
|
281
|
+
}
|
|
282
|
+
#applyLayout(input) {
|
|
283
|
+
input = this.#convertNewlines(input);
|
|
284
|
+
if (this.config.paragraph) input = "<p>" + input + "</p>";
|
|
285
|
+
return input;
|
|
286
|
+
}
|
|
287
|
+
#unsanitize(value) {
|
|
288
|
+
if (!this.config.sanitizeHtml) return value;
|
|
289
|
+
return unescapeSanitized(value);
|
|
290
|
+
}
|
|
241
291
|
#ignoreLoop(tagsMap, content) {
|
|
242
292
|
for (const tag of tagsMap) {
|
|
243
|
-
content = content
|
|
244
|
-
if (tag.closing) content = content
|
|
293
|
+
content = replaceFirst(content, placeholder(tag.index), tag.raw);
|
|
294
|
+
if (tag.closing) content = replaceFirst(content, placeholder(tag.closing.index), tag.closing.raw);
|
|
245
295
|
if (tag.children.length > 0) content = this.#ignoreLoop(tag.children, content);
|
|
246
296
|
}
|
|
247
297
|
return content;
|
|
248
298
|
}
|
|
249
|
-
#contentLoop(tagsMap, content) {
|
|
299
|
+
#contentLoop(tagsMap, content, parentClosingTagIndex) {
|
|
250
300
|
if (tagsMap.length > 50) {
|
|
251
|
-
|
|
252
|
-
const module
|
|
253
|
-
|
|
254
|
-
|
|
301
|
+
const hasSpecialType = (tag) => {
|
|
302
|
+
const module = this.#getTag(tag.module);
|
|
303
|
+
if (module) {
|
|
304
|
+
if (module.type === "content" || module.type === "ignore") return true;
|
|
305
|
+
if (module.type === "replace" && module.autoClose) return true;
|
|
306
|
+
}
|
|
307
|
+
return tag.children.some((child) => hasSpecialType(child));
|
|
308
|
+
};
|
|
309
|
+
if (!tagsMap.some((tag) => hasSpecialType(tag))) return this.#contentLoopOptimized(tagsMap, content);
|
|
255
310
|
}
|
|
256
|
-
for (
|
|
257
|
-
|
|
258
|
-
if (!
|
|
311
|
+
for (let i = 0; i < tagsMap.length; i++) {
|
|
312
|
+
const tag = tagsMap[i];
|
|
313
|
+
if (!tag) continue;
|
|
314
|
+
let module = this.#getTag(tag.module);
|
|
315
|
+
if (!module) module = {
|
|
259
316
|
type: "replace",
|
|
260
317
|
open: tag.raw,
|
|
261
318
|
close: tag.closing ? tag.closing.raw : ""
|
|
262
319
|
};
|
|
263
|
-
if (!this.contentModules[module
|
|
264
|
-
if (module
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
320
|
+
if (!this.contentModules[module.type]) throw new Error("Cannot parse content block. Invalid block type [" + module.type + "] provided for tag [" + tag.module + "]");
|
|
321
|
+
if (module.type === "replace") {
|
|
322
|
+
const replaceModule = module;
|
|
323
|
+
let open = replaceModule.open;
|
|
324
|
+
if (typeof open === "function") open = open(tag.attr, tag.attrs);
|
|
325
|
+
if (open && !tag.isClosing) content = replaceFirst(content, placeholder(tag.index), open);
|
|
326
|
+
if (tag.children.length > 0) {
|
|
327
|
+
const closingIndex = tag.closing?.index;
|
|
328
|
+
content = this.#contentLoop(tag.children, content, closingIndex);
|
|
329
|
+
}
|
|
330
|
+
if (replaceModule.autoClose && !tag.closing) {
|
|
331
|
+
let autoClose = replaceModule.autoClose;
|
|
332
|
+
if (typeof autoClose === "function") autoClose = autoClose(tag.attr, tag.attrs);
|
|
333
|
+
let insertBefore;
|
|
334
|
+
for (let siblingIndex = i + 1; siblingIndex < tagsMap.length; siblingIndex++) {
|
|
335
|
+
const sibling = tagsMap[siblingIndex];
|
|
336
|
+
if (sibling?.module === tag.module) {
|
|
337
|
+
insertBefore = placeholder(sibling.index);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (insertBefore === void 0 && parentClosingTagIndex !== void 0) insertBefore = placeholder(parentClosingTagIndex);
|
|
342
|
+
if (insertBefore && autoClose) content = replaceFirst(content, insertBefore, autoClose + insertBefore);
|
|
343
|
+
}
|
|
344
|
+
let close = replaceModule.close;
|
|
345
|
+
if (typeof close === "function") close = close(tag.attr, tag.attrs);
|
|
346
|
+
if (close && tag.closing) content = replaceFirst(content, placeholder(tag.closing.index), close);
|
|
347
|
+
} else if (module.type === "content") {
|
|
348
|
+
if (tag.children.length > 0) content = this.#contentLoop(tag.children, content, tag.closing?.index);
|
|
349
|
+
content = this.contentModules.content(tag, module, content);
|
|
350
|
+
} else if (module.type === "ignore") content = this.contentModules.ignore(tag, module, content);
|
|
268
351
|
}
|
|
269
352
|
return content;
|
|
270
353
|
}
|
|
271
354
|
#contentLoopOptimized(tagsMap, content) {
|
|
272
355
|
const replacements = /* @__PURE__ */ new Map();
|
|
273
356
|
const processTag = (tag) => {
|
|
274
|
-
let module
|
|
275
|
-
if (!module
|
|
357
|
+
let module = this.#getTag(tag.module);
|
|
358
|
+
if (!module) module = {
|
|
276
359
|
type: "replace",
|
|
277
360
|
open: tag.raw,
|
|
278
361
|
close: tag.closing ? tag.closing.raw : ""
|
|
279
362
|
};
|
|
280
|
-
if (module
|
|
281
|
-
let open = module
|
|
282
|
-
let close = module
|
|
363
|
+
if (module.type !== "replace") return;
|
|
364
|
+
let open = module.open;
|
|
365
|
+
let close = module.close;
|
|
283
366
|
if (typeof open === "function") open = open(tag.attr, tag.attrs);
|
|
284
367
|
if (typeof close === "function") close = close(tag.attr, tag.attrs);
|
|
285
|
-
if (open && !tag.isClosing) replacements.set(
|
|
286
|
-
if (close && tag.closing) replacements.set(
|
|
287
|
-
|
|
368
|
+
if (open && !tag.isClosing) replacements.set(placeholder(tag.index), open);
|
|
369
|
+
if (close && tag.closing) replacements.set(placeholder(tag.closing.index), close);
|
|
370
|
+
for (const child of tag.children) processTag(child);
|
|
288
371
|
};
|
|
289
372
|
for (const tag of tagsMap) processTag(tag);
|
|
290
|
-
return content.replaceAll(
|
|
373
|
+
return content.replaceAll(this.regex.placeholders, (match) => replacements.get(match) ?? match);
|
|
291
374
|
}
|
|
292
375
|
#tagLoop(tagsMap, parent) {
|
|
293
376
|
const stack = [];
|
|
@@ -308,10 +391,9 @@ var yabbcode = class {
|
|
|
308
391
|
openTag.children = [];
|
|
309
392
|
for (let childIndex = childStart; childIndex < i; childIndex++) {
|
|
310
393
|
const childTag = tagsMap[childIndex];
|
|
311
|
-
if (childTag && !childTag.isClosing && childTag.parent === null)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
394
|
+
if (!(childTag && !childTag.isClosing && childTag.parent === null)) continue;
|
|
395
|
+
childTag.parent = openTag.index;
|
|
396
|
+
openTag.children.push(childTag);
|
|
315
397
|
}
|
|
316
398
|
}
|
|
317
399
|
stack.splice(stackIndex, 1);
|
|
@@ -326,23 +408,21 @@ var yabbcode = class {
|
|
|
326
408
|
for (const tag of stack) {
|
|
327
409
|
tag.matchTag = false;
|
|
328
410
|
const childStart = (tag.stackIndex ?? 0) + 1;
|
|
329
|
-
if (childStart < tagsMap.length)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
}
|
|
411
|
+
if (!(childStart < tagsMap.length)) continue;
|
|
412
|
+
tag.children = [];
|
|
413
|
+
for (let childIndex = childStart; childIndex < tagsMap.length; childIndex++) {
|
|
414
|
+
const childTag = tagsMap[childIndex];
|
|
415
|
+
if (!(childTag && !childTag.isClosing && childTag.parent === null)) continue;
|
|
416
|
+
childTag.parent = tag.index;
|
|
417
|
+
tag.children.push(childTag);
|
|
338
418
|
}
|
|
339
419
|
}
|
|
340
420
|
const result = [];
|
|
341
|
-
for (const tag of tagsMap)
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
421
|
+
for (const tag of tagsMap) {
|
|
422
|
+
if (tag.isClosing) continue;
|
|
423
|
+
if (!(parent === void 0 && tag.parent === null || parent !== void 0 && tag.parent === parent)) continue;
|
|
424
|
+
if (tag.children.length > 0) tag.children = this.#tagLoop(tag.children, tag.index);
|
|
425
|
+
result.push(tag);
|
|
346
426
|
}
|
|
347
427
|
return result;
|
|
348
428
|
}
|
|
@@ -362,7 +442,7 @@ var yabbcode = class {
|
|
|
362
442
|
}
|
|
363
443
|
parse(bbcInput) {
|
|
364
444
|
if (typeof bbcInput === "boolean" || typeof bbcInput !== "string" && Number.isNaN(Number(bbcInput))) return "";
|
|
365
|
-
let input = String(bbcInput);
|
|
445
|
+
let input = String(bbcInput).replaceAll(PLACEHOLDER_START, "");
|
|
366
446
|
if (this.config.sanitizeHtml) {
|
|
367
447
|
const htmlEntities = {
|
|
368
448
|
"&": "&",
|
|
@@ -375,10 +455,7 @@ var yabbcode = class {
|
|
|
375
455
|
}
|
|
376
456
|
let tagsMap = [];
|
|
377
457
|
const tags = String(input).match(this.regex.tags);
|
|
378
|
-
if (
|
|
379
|
-
else input = input.replace(this.regex.newline, "<br/>");
|
|
380
|
-
if (this.config.paragraph) input = "<p>" + input + "</p>";
|
|
381
|
-
if (!tags || tags.length === 0) return input;
|
|
458
|
+
if (!tags || tags.length === 0) return this.#applyLayout(input);
|
|
382
459
|
for (const [i, tag] of tags.entries()) {
|
|
383
460
|
const tagContent = tag.slice(1, -1);
|
|
384
461
|
const isClosing = tagContent.startsWith("/");
|
|
@@ -394,17 +471,17 @@ var yabbcode = class {
|
|
|
394
471
|
attrs: {}
|
|
395
472
|
};
|
|
396
473
|
} else if (spaceIndex === -1 || equalsIndex !== -1 && equalsIndex < spaceIndex) {
|
|
397
|
-
moduleName = (contentToParse.split("=")[0] ?? "").toLowerCase();
|
|
474
|
+
moduleName = (contentToParse.split("=", 1)[0] ?? "").toLowerCase();
|
|
398
475
|
parsedAttrs = parseTagAttributes(contentToParse);
|
|
399
476
|
} else {
|
|
400
|
-
moduleName = (contentToParse.split(/\s
|
|
477
|
+
moduleName = (contentToParse.split(/\s+/, 1)[0] ?? "").toLowerCase();
|
|
401
478
|
parsedAttrs = parseTagAttributes(contentToParse);
|
|
402
479
|
}
|
|
403
480
|
const item = {
|
|
404
481
|
index: i,
|
|
405
482
|
module: moduleName,
|
|
406
483
|
isClosing,
|
|
407
|
-
raw: tag,
|
|
484
|
+
raw: this.#convertNewlines(tag),
|
|
408
485
|
attr: parsedAttrs.attr,
|
|
409
486
|
attrs: parsedAttrs.attrs,
|
|
410
487
|
children: [],
|
|
@@ -413,20 +490,19 @@ var yabbcode = class {
|
|
|
413
490
|
};
|
|
414
491
|
tagsMap.push(item);
|
|
415
492
|
}
|
|
416
|
-
if (tags.length <= 3) for (const [i, tag] of tags.entries()) input = input
|
|
493
|
+
if (tags.length <= 3) for (const [i, tag] of tags.entries()) input = replaceFirst(input, tag, placeholder(i));
|
|
417
494
|
else {
|
|
418
495
|
const tagPositions = [];
|
|
419
496
|
let lastIndex = 0;
|
|
420
497
|
for (const [i, tag] of tags.entries()) {
|
|
421
498
|
const pos = input.indexOf(tag, lastIndex);
|
|
422
|
-
if (pos
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
499
|
+
if (pos === -1) continue;
|
|
500
|
+
tagPositions.push({
|
|
501
|
+
pos,
|
|
502
|
+
tag,
|
|
503
|
+
placeholder: placeholder(i)
|
|
504
|
+
});
|
|
505
|
+
lastIndex = pos + tag.length;
|
|
430
506
|
}
|
|
431
507
|
if (tagPositions.length > 0) {
|
|
432
508
|
const parts = [];
|
|
@@ -440,14 +516,17 @@ var yabbcode = class {
|
|
|
440
516
|
input = parts.join("");
|
|
441
517
|
}
|
|
442
518
|
}
|
|
519
|
+
input = this.#applyLayout(input);
|
|
443
520
|
tagsMap = this.#tagLoop(tagsMap);
|
|
444
521
|
input = this.#contentLoop(tagsMap, input);
|
|
445
|
-
if (this.config.cleanUnmatchable)
|
|
446
|
-
return input
|
|
522
|
+
if (this.config.cleanUnmatchable) return input.replaceAll(this.regex.placeholders, "");
|
|
523
|
+
return input.replaceAll(this.regex.placeholders, (_match, index) => {
|
|
524
|
+
let raw = tags[Number(index)] ?? "";
|
|
525
|
+
if (!this.config.sanitizeHtml) raw = escapeHtmlAttr(raw);
|
|
526
|
+
return this.#convertNewlines(raw);
|
|
527
|
+
});
|
|
447
528
|
}
|
|
448
529
|
};
|
|
449
|
-
var ya_bbcode_default = yabbcode;
|
|
450
530
|
|
|
451
|
-
|
|
452
|
-
module.exports = ya_bbcode_default;
|
|
531
|
+
module.exports = yabbcode;
|
|
453
532
|
//# sourceMappingURL=ya-bbcode.cjs.map
|