ya-bbcode 3.1.0 → 4.0.0
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/package.json +9 -10
- package/ya-bbcode.d.ts +38 -37
- package/ya-bbcode.js +62 -49
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ya-bbcode",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Yet another BBCode Parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bbc",
|
|
@@ -34,23 +34,22 @@
|
|
|
34
34
|
"lint-staged": {
|
|
35
35
|
"*.js": "eslint --cache"
|
|
36
36
|
},
|
|
37
|
-
"dependencies": {},
|
|
38
37
|
"devDependencies": {
|
|
39
|
-
"@commitlint/cli": "^17.
|
|
40
|
-
"@commitlint/config-conventional": "^17.
|
|
41
|
-
"@nodecraft/eslint-config": "^
|
|
42
|
-
"ava": "^5.
|
|
38
|
+
"@commitlint/cli": "^17.6.5",
|
|
39
|
+
"@commitlint/config-conventional": "^17.6.5",
|
|
40
|
+
"@nodecraft/eslint-config": "^25.0.1",
|
|
41
|
+
"ava": "^5.3.0",
|
|
43
42
|
"coveralls": "^3.1.1",
|
|
44
|
-
"eslint": "^8.
|
|
43
|
+
"eslint": "^8.41.0",
|
|
45
44
|
"eslint-plugin-json": "^3.1.0",
|
|
46
45
|
"eslint-plugin-node": "^11.1.0",
|
|
47
|
-
"eslint-plugin-unicorn": "^
|
|
46
|
+
"eslint-plugin-unicorn": "^47.0.0",
|
|
48
47
|
"husky": "^8.0.3",
|
|
49
|
-
"lint-staged": "^13.
|
|
48
|
+
"lint-staged": "^13.2.2",
|
|
50
49
|
"nyc": "^15.1.0"
|
|
51
50
|
},
|
|
52
51
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
52
|
+
"node": ">=16",
|
|
54
53
|
"npm": ">=8"
|
|
55
54
|
}
|
|
56
55
|
}
|
package/ya-bbcode.d.ts
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
export = yabbcode
|
|
2
|
-
|
|
3
|
-
declare class yabbcode {
|
|
4
|
-
/**
|
|
5
|
-
* Create a new bbcode parser
|
|
6
|
-
*/
|
|
7
|
-
constructor(config?: {
|
|
8
|
-
newline?: boolean
|
|
9
|
-
paragraph?: boolean
|
|
10
|
-
cleanUnmatchable?: boolean
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
* @param
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
export = yabbcode
|
|
2
|
+
|
|
3
|
+
declare class yabbcode {
|
|
4
|
+
/**
|
|
5
|
+
* Create a new bbcode parser
|
|
6
|
+
*/
|
|
7
|
+
constructor(config?: {
|
|
8
|
+
newline?: boolean
|
|
9
|
+
paragraph?: boolean
|
|
10
|
+
cleanUnmatchable?: boolean
|
|
11
|
+
sanitizeHtml?: boolean
|
|
12
|
+
})
|
|
13
|
+
/**
|
|
14
|
+
* Parse bbcode
|
|
15
|
+
* @param bbc bbcode to parse
|
|
16
|
+
*/
|
|
17
|
+
parse(bbc: any): string
|
|
18
|
+
/**
|
|
19
|
+
* Remove all default or registered tags
|
|
20
|
+
*/
|
|
21
|
+
clearTags(): this
|
|
22
|
+
/**
|
|
23
|
+
* Add custom tags
|
|
24
|
+
* @param tag tag name
|
|
25
|
+
* @param options options to parse the tag
|
|
26
|
+
*/
|
|
27
|
+
registerTag(tag: string, options: {
|
|
28
|
+
type: 'replace'
|
|
29
|
+
open: ((attr: string) => string) | string
|
|
30
|
+
close: ((attr: string) => string) | string | null
|
|
31
|
+
}): this
|
|
32
|
+
registerTag(tag: string, options: {
|
|
33
|
+
type: 'content',
|
|
34
|
+
replace: (attr: string, content: string) => string
|
|
35
|
+
}): this
|
|
36
|
+
registerTag(tag: string, options: {
|
|
37
|
+
type: 'ignore'
|
|
38
|
+
}): this
|
|
38
39
|
}
|
package/ya-bbcode.js
CHANGED
|
@@ -90,7 +90,7 @@ class yabbcode {
|
|
|
90
90
|
'img': {
|
|
91
91
|
type: 'content',
|
|
92
92
|
replace: (attr, content) => {
|
|
93
|
-
if(!content){
|
|
93
|
+
if(!content) {
|
|
94
94
|
return '';
|
|
95
95
|
}
|
|
96
96
|
return `<img src="${content}" alt="${attr || ''}"/>`;
|
|
@@ -109,32 +109,32 @@ class yabbcode {
|
|
|
109
109
|
replace: (tag, module, content) => {
|
|
110
110
|
let open = module.open;
|
|
111
111
|
let close = module.close;
|
|
112
|
-
if(typeof(open) === 'function'){
|
|
112
|
+
if(typeof(open) === 'function') {
|
|
113
113
|
open = open(tag.attr);
|
|
114
114
|
}
|
|
115
|
-
if(typeof(close) === 'function'){
|
|
115
|
+
if(typeof(close) === 'function') {
|
|
116
116
|
close = close(tag.attr);
|
|
117
117
|
}
|
|
118
118
|
// do the replace
|
|
119
|
-
if(open && !tag.isClosing){
|
|
119
|
+
if(open && !tag.isClosing) {
|
|
120
120
|
//content = content
|
|
121
121
|
content = content.replace('[TAG-' + tag.index + ']', open);
|
|
122
122
|
}
|
|
123
|
-
if(close && tag.closing){
|
|
123
|
+
if(close && tag.closing) {
|
|
124
124
|
content = content.replace('[TAG-' + tag.closing.index + ']', close);
|
|
125
125
|
}
|
|
126
126
|
return content;
|
|
127
127
|
},
|
|
128
128
|
content: (tag, module, content) => {
|
|
129
|
-
if(!tag.closing){ return content; }
|
|
130
|
-
const openTag =
|
|
131
|
-
const closeTag =
|
|
129
|
+
if(!tag.closing) { return content; }
|
|
130
|
+
const openTag = '[TAG-' + tag.index + ']';
|
|
131
|
+
const closeTag = '[TAG-' + tag.closing.index + ']';
|
|
132
132
|
const start = content.indexOf(openTag);
|
|
133
133
|
const end = content.indexOf(closeTag);
|
|
134
134
|
let replace = module.replace;
|
|
135
135
|
|
|
136
136
|
const innerContent = content.slice(start + openTag.length, end);
|
|
137
|
-
if(typeof(replace) === 'function'){
|
|
137
|
+
if(typeof(replace) === 'function') {
|
|
138
138
|
replace = replace(tag.attr, innerContent);
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -144,12 +144,12 @@ class yabbcode {
|
|
|
144
144
|
return contentStart + replace + contentEnd;
|
|
145
145
|
},
|
|
146
146
|
ignore: (tag, module, content) => {
|
|
147
|
-
const openTag =
|
|
147
|
+
const openTag = '[TAG-' + tag.index + ']';
|
|
148
148
|
const start = content.indexOf(openTag);
|
|
149
|
-
let closeTag =
|
|
149
|
+
let closeTag = '';
|
|
150
150
|
let end = content.length;
|
|
151
|
-
if(tag.closing){
|
|
152
|
-
closeTag =
|
|
151
|
+
if(tag.closing) {
|
|
152
|
+
closeTag = '[TAG-' + tag.closing.index + ']';
|
|
153
153
|
end = content.indexOf(closeTag);
|
|
154
154
|
}
|
|
155
155
|
let innerContent = content.slice(start + openTag.length, end);
|
|
@@ -159,40 +159,44 @@ class yabbcode {
|
|
|
159
159
|
return contentStart + innerContent + contentEnd;
|
|
160
160
|
},
|
|
161
161
|
};
|
|
162
|
-
constructor(config = {}){
|
|
162
|
+
constructor(config = {}) {
|
|
163
163
|
this.config = {
|
|
164
164
|
newline: true,
|
|
165
165
|
paragraph: false,
|
|
166
166
|
cleanUnmatchable: true,
|
|
167
|
+
sanitizeHtml: true,
|
|
167
168
|
};
|
|
168
|
-
if(config.newline !== undefined){
|
|
169
|
+
if(config.newline !== undefined) {
|
|
169
170
|
this.config.newline = config.newline;
|
|
170
171
|
}
|
|
171
|
-
if(config.paragraph !== undefined){
|
|
172
|
+
if(config.paragraph !== undefined) {
|
|
172
173
|
this.config.paragraph = config.paragraph;
|
|
173
174
|
}
|
|
174
|
-
if(config.cleanUnmatchable !== undefined){
|
|
175
|
+
if(config.cleanUnmatchable !== undefined) {
|
|
175
176
|
this.config.cleanUnmatchable = config.cleanUnmatchable;
|
|
176
177
|
}
|
|
178
|
+
if(config.sanitizeHtml !== undefined) {
|
|
179
|
+
this.config.sanitizeHtml = config.sanitizeHtml;
|
|
180
|
+
}
|
|
177
181
|
}
|
|
178
182
|
|
|
179
|
-
#ignoreLoop = function(tagsMap, content){
|
|
180
|
-
for(const tag of tagsMap){
|
|
183
|
+
#ignoreLoop = function(tagsMap, content) {
|
|
184
|
+
for(const tag of tagsMap) {
|
|
181
185
|
content = content.replace('[TAG-' + tag.index + ']', tag.raw);
|
|
182
|
-
if(tag.closing){
|
|
186
|
+
if(tag.closing) {
|
|
183
187
|
content = content.replace('[TAG-' + tag.closing.index + ']', tag.closing.raw);
|
|
184
188
|
}
|
|
185
|
-
if(tag.children.length > 0){
|
|
189
|
+
if(tag.children.length > 0) {
|
|
186
190
|
content = this.#ignoreLoop(tag.children, content);
|
|
187
191
|
}
|
|
188
192
|
}
|
|
189
193
|
return content;
|
|
190
194
|
};
|
|
191
195
|
|
|
192
|
-
#contentLoop = function(tagsMap, content){
|
|
193
|
-
for(const tag of tagsMap){
|
|
196
|
+
#contentLoop = function(tagsMap, content) {
|
|
197
|
+
for(const tag of tagsMap) {
|
|
194
198
|
let module = this.tags[tag.module];
|
|
195
|
-
if(!module){
|
|
199
|
+
if(!module) {
|
|
196
200
|
// ignore invalid BBCode
|
|
197
201
|
module = {
|
|
198
202
|
type: 'replace',
|
|
@@ -200,11 +204,11 @@ class yabbcode {
|
|
|
200
204
|
close: tag.closing && tag.closing.raw || '',
|
|
201
205
|
};
|
|
202
206
|
}
|
|
203
|
-
if(!this.contentModules[module.type]){
|
|
204
|
-
throw new Error(
|
|
207
|
+
if(!this.contentModules[module.type]) {
|
|
208
|
+
throw new Error('Cannot parse content block. Invalid block type [' + module.type + '] provided for tag [' + tag.module + ']');
|
|
205
209
|
}
|
|
206
210
|
content = this.contentModules[module.type](tag, module, content);
|
|
207
|
-
if(tag.children.length > 0 && module.type !== 'ignore'){
|
|
211
|
+
if(tag.children.length > 0 && module.type !== 'ignore') {
|
|
208
212
|
content = this.#contentLoop(tag.children, content);
|
|
209
213
|
}
|
|
210
214
|
}
|
|
@@ -212,15 +216,15 @@ class yabbcode {
|
|
|
212
216
|
return content;
|
|
213
217
|
};
|
|
214
218
|
|
|
215
|
-
#tagLoop = function(tagsMap, parent){
|
|
219
|
+
#tagLoop = function(tagsMap, parent) {
|
|
216
220
|
let currentTagIndex = 0;
|
|
217
|
-
while(currentTagIndex < tagsMap.length){
|
|
221
|
+
while(currentTagIndex < tagsMap.length) {
|
|
218
222
|
let found = false;
|
|
219
|
-
if(tagsMap[currentTagIndex].matchTag !== null || tagsMap[currentTagIndex].isClosing){
|
|
223
|
+
if(tagsMap[currentTagIndex].matchTag !== null || tagsMap[currentTagIndex].isClosing) {
|
|
220
224
|
currentTagIndex++;
|
|
221
225
|
continue; // already handled this tag / not closing
|
|
222
226
|
}
|
|
223
|
-
for(const [i, item] of tagsMap.entries()){
|
|
227
|
+
for(const [i, item] of tagsMap.entries()) {
|
|
224
228
|
if(
|
|
225
229
|
found ||
|
|
226
230
|
tagsMap[currentTagIndex].matchTag !== null ||
|
|
@@ -228,7 +232,7 @@ class yabbcode {
|
|
|
228
232
|
item.matchTag !== null ||
|
|
229
233
|
!item.isClosing ||
|
|
230
234
|
tagsMap[currentTagIndex].module !== item.module
|
|
231
|
-
){
|
|
235
|
+
) {
|
|
232
236
|
continue;
|
|
233
237
|
}
|
|
234
238
|
tagsMap[i].matchTag = tagsMap[currentTagIndex].index;
|
|
@@ -238,7 +242,7 @@ class yabbcode {
|
|
|
238
242
|
|
|
239
243
|
const childStart = currentTagIndex + 1;
|
|
240
244
|
|
|
241
|
-
if(found !== false){
|
|
245
|
+
if(found !== false) {
|
|
242
246
|
tagsMap[currentTagIndex].closing = tagsMap[tagsMap[currentTagIndex].matchTag];
|
|
243
247
|
}else{
|
|
244
248
|
tagsMap[currentTagIndex].matchTag = false;
|
|
@@ -246,7 +250,7 @@ class yabbcode {
|
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
// sweep children
|
|
249
|
-
if(childStart < found){
|
|
253
|
+
if(childStart < found) {
|
|
250
254
|
tagsMap[currentTagIndex].children = tagsMap.slice(childStart, found).map((child) => {
|
|
251
255
|
child.parent = tagsMap[currentTagIndex].index;
|
|
252
256
|
return child;
|
|
@@ -254,7 +258,7 @@ class yabbcode {
|
|
|
254
258
|
}
|
|
255
259
|
|
|
256
260
|
let i = childStart;
|
|
257
|
-
while(i < found){
|
|
261
|
+
while(i < found) {
|
|
258
262
|
tagsMap[i].parent = tagsMap[currentTagIndex].index;
|
|
259
263
|
i++;
|
|
260
264
|
}
|
|
@@ -267,48 +271,57 @@ class yabbcode {
|
|
|
267
271
|
tagsMap = tagsMap.filter(item => !((parent === undefined && item.parent !== null) || (item.parent !== null && item.parent !== parent) || item.isClosing));
|
|
268
272
|
|
|
269
273
|
return tagsMap.map((tag) => {
|
|
270
|
-
if(tag.children.length > 0){
|
|
274
|
+
if(tag.children.length > 0) {
|
|
271
275
|
tag.children = this.#tagLoop(tag.children, tag.index);
|
|
272
276
|
}
|
|
273
277
|
return tag;
|
|
274
278
|
});
|
|
275
279
|
};
|
|
276
280
|
|
|
277
|
-
clearTags(){
|
|
281
|
+
clearTags() {
|
|
278
282
|
this.tags = {};
|
|
279
283
|
return this;
|
|
280
284
|
}
|
|
281
285
|
|
|
282
|
-
registerTag(tag, options){
|
|
286
|
+
registerTag(tag, options) {
|
|
283
287
|
this.tags[String(tag).toLowerCase()] = options;
|
|
284
288
|
return this;
|
|
285
289
|
}
|
|
286
290
|
|
|
287
|
-
parse(bbcInput){
|
|
288
|
-
if(typeof(bbcInput) === 'boolean' || typeof(bbcInput) !== 'string' && Number.isNaN(Number(bbcInput))){ return ''; }
|
|
291
|
+
parse(bbcInput) {
|
|
292
|
+
if(typeof(bbcInput) === 'boolean' || typeof(bbcInput) !== 'string' && Number.isNaN(Number(bbcInput))) { return ''; }
|
|
289
293
|
// eslint-disable-next-line unicorn/prefer-spread
|
|
290
294
|
let input = String(bbcInput).slice(0); // cheap string clone
|
|
295
|
+
if(this.config.sanitizeHtml) {
|
|
296
|
+
input = input
|
|
297
|
+
.replaceAll('&', '&')
|
|
298
|
+
.replaceAll('<', '<')
|
|
299
|
+
.replaceAll('>', '>')
|
|
300
|
+
.replaceAll('"', '"')
|
|
301
|
+
.replaceAll('\'', ''');
|
|
302
|
+
}
|
|
303
|
+
|
|
291
304
|
// reset
|
|
292
305
|
let tagsMap = [];
|
|
293
306
|
// split input into tags by index
|
|
294
307
|
const tags = String(input).match(this.regex.tags);
|
|
295
308
|
|
|
296
|
-
if(this.config.newline){
|
|
297
|
-
if(this.config.paragraph){
|
|
298
|
-
input = input.replace(this.regex.newline,
|
|
309
|
+
if(this.config.newline) {
|
|
310
|
+
if(this.config.paragraph) {
|
|
311
|
+
input = input.replace(this.regex.newline, '</p><p>');
|
|
299
312
|
}else{
|
|
300
|
-
input = input.replace(this.regex.newline,
|
|
313
|
+
input = input.replace(this.regex.newline, '<br/>');
|
|
301
314
|
}
|
|
302
315
|
}
|
|
303
|
-
if(this.config.paragraph){
|
|
316
|
+
if(this.config.paragraph) {
|
|
304
317
|
input = '<p>' + input + '</p>';
|
|
305
318
|
}
|
|
306
319
|
|
|
307
320
|
// handle when no tags are present
|
|
308
|
-
if(!tags || tags.length === 0){
|
|
321
|
+
if(!tags || tags.length === 0) {
|
|
309
322
|
return input;
|
|
310
323
|
}
|
|
311
|
-
for(const [i, tag] of tags.entries()){
|
|
324
|
+
for(const [i, tag] of tags.entries()) {
|
|
312
325
|
const parts = tag.slice(1, -1).split('=');
|
|
313
326
|
const item = {
|
|
314
327
|
index: i,
|
|
@@ -321,7 +334,7 @@ class yabbcode {
|
|
|
321
334
|
parent: null,
|
|
322
335
|
matchTag: null,
|
|
323
336
|
};
|
|
324
|
-
if(item.isClosing){
|
|
337
|
+
if(item.isClosing) {
|
|
325
338
|
item.module = item.module.slice(1);
|
|
326
339
|
}
|
|
327
340
|
|
|
@@ -332,7 +345,7 @@ class yabbcode {
|
|
|
332
345
|
tagsMap = this.#tagLoop(tagsMap);
|
|
333
346
|
// put back all non-found matches?
|
|
334
347
|
input = this.#contentLoop(tagsMap, input);
|
|
335
|
-
if(this.config.cleanUnmatchable){
|
|
348
|
+
if(this.config.cleanUnmatchable) {
|
|
336
349
|
input = input.replace(this.regex.placeholders, '');
|
|
337
350
|
}
|
|
338
351
|
return input;
|