micro-models-agent 2.4.0 → 2.8.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/CHANGELOG.md +109 -0
- package/README.md +88 -38
- package/bin/mma.mjs +41 -41
- package/dist/assets/app.js +246 -3032
- package/dist/assets/hljs.js +1244 -0
- package/dist/assets/index.html +15 -1
- package/dist/assets/js/api.js +28 -0
- package/dist/assets/js/dialog.js +105 -0
- package/dist/assets/js/dom.js +157 -0
- package/dist/assets/js/feed.js +995 -0
- package/dist/assets/js/format.js +114 -0
- package/dist/assets/js/i18n.js +46 -0
- package/dist/assets/js/settings.js +1126 -0
- package/dist/assets/js/store.js +79 -0
- package/dist/assets/js/tabs.js +672 -0
- package/dist/assets/md.js +277 -0
- package/dist/assets/setup.js +557 -0
- package/dist/assets/smd.js +1665 -0
- package/dist/assets/styles.css +271 -21
- package/dist/i18n/en.json +171 -3
- package/dist/i18n/ru.json +171 -3
- package/dist/main.js +4958 -2588
- package/dist/modules/browser/bridge-server.mjs +3 -3
- package/package.json +10 -2
|
@@ -0,0 +1,1665 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Streaming Markdown Parser and Renderer
|
|
3
|
+
MIT License
|
|
4
|
+
Copyright 2024 Damian Tarnawski
|
|
5
|
+
https://github.com/thetarnav/streaming-markdown
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const
|
|
9
|
+
DOCUMENT = 1,
|
|
10
|
+
PARAGRAPH = 2,
|
|
11
|
+
HEADING_1 = 3,
|
|
12
|
+
HEADING_2 = 4,
|
|
13
|
+
HEADING_3 = 5,
|
|
14
|
+
HEADING_4 = 6,
|
|
15
|
+
HEADING_5 = 7,
|
|
16
|
+
HEADING_6 = 8,
|
|
17
|
+
CODE_BLOCK = 9,
|
|
18
|
+
CODE_FENCE = 10,
|
|
19
|
+
CODE_INLINE = 11,
|
|
20
|
+
ITALIC_AST = 12,
|
|
21
|
+
ITALIC_UND = 13,
|
|
22
|
+
STRONG_AST = 14,
|
|
23
|
+
STRONG_UND = 15,
|
|
24
|
+
STRIKE = 16,
|
|
25
|
+
LINK = 17,
|
|
26
|
+
RAW_URL = 18,
|
|
27
|
+
IMAGE = 19,
|
|
28
|
+
BLOCKQUOTE = 20,
|
|
29
|
+
LINE_BREAK = 21,
|
|
30
|
+
RULE = 22,
|
|
31
|
+
LIST_UNORDERED = 23,
|
|
32
|
+
LIST_ORDERED = 24,
|
|
33
|
+
LIST_ITEM = 25,
|
|
34
|
+
CHECKBOX = 26,
|
|
35
|
+
TABLE = 27,
|
|
36
|
+
TABLE_ROW = 28,
|
|
37
|
+
TABLE_CELL = 29,
|
|
38
|
+
EQUATION_BLOCK = 30,
|
|
39
|
+
EQUATION_INLINE = 31,
|
|
40
|
+
NEWLINE = 101,
|
|
41
|
+
MAYBE_URL = 102,
|
|
42
|
+
MAYBE_TASK = 103,
|
|
43
|
+
MAYBE_BR = 104,
|
|
44
|
+
MAYBE_EQ_BLOCK = 105
|
|
45
|
+
|
|
46
|
+
/** @enum {(typeof Token)[keyof typeof Token]} */
|
|
47
|
+
export const Token = /** @type {const} */({
|
|
48
|
+
Document: DOCUMENT,
|
|
49
|
+
Blockquote: BLOCKQUOTE,
|
|
50
|
+
Paragraph: PARAGRAPH,
|
|
51
|
+
Heading_1: HEADING_1,
|
|
52
|
+
Heading_2: HEADING_2,
|
|
53
|
+
Heading_3: HEADING_3,
|
|
54
|
+
Heading_4: HEADING_4,
|
|
55
|
+
Heading_5: HEADING_5,
|
|
56
|
+
Heading_6: HEADING_6,
|
|
57
|
+
Code_Block: CODE_BLOCK,
|
|
58
|
+
Code_Fence: CODE_FENCE,
|
|
59
|
+
Code_Inline: CODE_INLINE,
|
|
60
|
+
Italic_Ast: ITALIC_AST,
|
|
61
|
+
Italic_Und: ITALIC_UND,
|
|
62
|
+
Strong_Ast: STRONG_AST,
|
|
63
|
+
Strong_Und: STRONG_UND,
|
|
64
|
+
Strike: STRIKE,
|
|
65
|
+
Link: LINK,
|
|
66
|
+
Raw_URL: RAW_URL,
|
|
67
|
+
Image: IMAGE,
|
|
68
|
+
Line_Break: LINE_BREAK,
|
|
69
|
+
Rule: RULE,
|
|
70
|
+
List_Unordered: LIST_UNORDERED,
|
|
71
|
+
List_Ordered: LIST_ORDERED,
|
|
72
|
+
List_Item: LIST_ITEM,
|
|
73
|
+
Checkbox: CHECKBOX,
|
|
74
|
+
Table: TABLE,
|
|
75
|
+
Table_Row: TABLE_ROW,
|
|
76
|
+
Table_Cell: TABLE_CELL,
|
|
77
|
+
Equation_Block: EQUATION_BLOCK,
|
|
78
|
+
Equation_Inline:EQUATION_INLINE,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {Token} type
|
|
83
|
+
* @returns {string } */
|
|
84
|
+
export function token_to_string(type) {
|
|
85
|
+
switch (type) {
|
|
86
|
+
case DOCUMENT: return "Document"
|
|
87
|
+
case BLOCKQUOTE: return "Blockquote"
|
|
88
|
+
case PARAGRAPH: return "Paragraph"
|
|
89
|
+
case HEADING_1: return "Heading_1"
|
|
90
|
+
case HEADING_2: return "Heading_2"
|
|
91
|
+
case HEADING_3: return "Heading_3"
|
|
92
|
+
case HEADING_4: return "Heading_4"
|
|
93
|
+
case HEADING_5: return "Heading_5"
|
|
94
|
+
case HEADING_6: return "Heading_6"
|
|
95
|
+
case CODE_BLOCK: return "Code_Block"
|
|
96
|
+
case CODE_FENCE: return "Code_Fence"
|
|
97
|
+
case CODE_INLINE: return "Code_Inline"
|
|
98
|
+
case ITALIC_AST: return "Italic_Ast"
|
|
99
|
+
case ITALIC_UND: return "Italic_Und"
|
|
100
|
+
case STRONG_AST: return "Strong_Ast"
|
|
101
|
+
case STRONG_UND: return "Strong_Und"
|
|
102
|
+
case STRIKE: return "Strike"
|
|
103
|
+
case LINK: return "Link"
|
|
104
|
+
case RAW_URL: return "Raw URL"
|
|
105
|
+
case IMAGE: return "Image"
|
|
106
|
+
case LINE_BREAK: return "Line_Break"
|
|
107
|
+
case RULE: return "Rule"
|
|
108
|
+
case LIST_UNORDERED: return "List_Unordered"
|
|
109
|
+
case LIST_ORDERED: return "List_Ordered"
|
|
110
|
+
case LIST_ITEM: return "List_Item"
|
|
111
|
+
case CHECKBOX: return "Checkbox"
|
|
112
|
+
case TABLE: return "Table"
|
|
113
|
+
case TABLE_ROW: return "Table_Row"
|
|
114
|
+
case TABLE_CELL: return "Table_Cell"
|
|
115
|
+
case EQUATION_BLOCK: return "Equation_Block"
|
|
116
|
+
case EQUATION_INLINE:return "Equation_Inline"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const
|
|
121
|
+
HREF = 1,
|
|
122
|
+
SRC = 2,
|
|
123
|
+
LANG = 4,
|
|
124
|
+
CHECKED = 8,
|
|
125
|
+
START = 16
|
|
126
|
+
|
|
127
|
+
/** @enum {(typeof Attr)[keyof typeof Attr]} */
|
|
128
|
+
export const Attr = /** @type {const} */({
|
|
129
|
+
Href : HREF,
|
|
130
|
+
Src : SRC,
|
|
131
|
+
Lang : LANG,
|
|
132
|
+
Checked: CHECKED,
|
|
133
|
+
Start : START,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {Attr} type
|
|
138
|
+
* @returns {string } */
|
|
139
|
+
export function attr_to_html_attr(type) {
|
|
140
|
+
switch (type) {
|
|
141
|
+
case HREF: return "href"
|
|
142
|
+
case SRC : return "src"
|
|
143
|
+
case LANG: return "class"
|
|
144
|
+
case CHECKED: return "checked"
|
|
145
|
+
case START: return "start"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @param {number} level
|
|
151
|
+
* @returns {Token } */
|
|
152
|
+
export const level_to_heading = (level) => {
|
|
153
|
+
switch (level) {
|
|
154
|
+
case 1: return HEADING_1
|
|
155
|
+
case 2: return HEADING_2
|
|
156
|
+
case 3: return HEADING_3
|
|
157
|
+
case 4: return HEADING_4
|
|
158
|
+
case 5: return HEADING_5
|
|
159
|
+
default: return HEADING_6
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export const heading_from_level = level_to_heading
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @param {Token} token
|
|
166
|
+
* @returns {number} */
|
|
167
|
+
export const heading_to_level = (token) => {
|
|
168
|
+
switch (token) {
|
|
169
|
+
case HEADING_1: return 1
|
|
170
|
+
case HEADING_2: return 2
|
|
171
|
+
case HEADING_3: return 3
|
|
172
|
+
case HEADING_4: return 4
|
|
173
|
+
case HEADING_5: return 5
|
|
174
|
+
case HEADING_6: return 6
|
|
175
|
+
default: return 0
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @typedef {object } Parser
|
|
181
|
+
* @property {Any_Renderer} renderer - {@link Renderer} interface
|
|
182
|
+
* @property {string } text - Text to be added to the last token in the next flush
|
|
183
|
+
* @property {string } pending - Characters for identifying tokens
|
|
184
|
+
* @property {Uint32Array } tokens - Current token and it's parents (a slice of a tree)
|
|
185
|
+
* @property {number } len - Number of tokens in types without root
|
|
186
|
+
* @property {number } token - Last token in the tree
|
|
187
|
+
* @property {Uint8Array } spaces
|
|
188
|
+
* @property {string } indent
|
|
189
|
+
* @property {number } indent_len
|
|
190
|
+
* @property {number } fence_end - For {@link Token.Code_Fence} parsing
|
|
191
|
+
* @property {number } fence_start
|
|
192
|
+
* @property {number } blockquote_idx - For Blockquote parsing
|
|
193
|
+
* @property {string } hr_char - For horizontal rule parsing
|
|
194
|
+
* @property {number } hr_chars - For horizontal rule parsing
|
|
195
|
+
* @property {number } table_state
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
const TOKEN_ARRAY_CAP = 24
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Makes a new Parser object.
|
|
202
|
+
* @param {Any_Renderer} renderer
|
|
203
|
+
* @returns {Parser } */
|
|
204
|
+
export function parser(renderer) {
|
|
205
|
+
const tokens = new Uint32Array(TOKEN_ARRAY_CAP)
|
|
206
|
+
tokens[0] = DOCUMENT
|
|
207
|
+
return {
|
|
208
|
+
renderer : renderer,
|
|
209
|
+
text : "",
|
|
210
|
+
pending : "",
|
|
211
|
+
tokens : tokens,
|
|
212
|
+
len : 0,
|
|
213
|
+
token : DOCUMENT,
|
|
214
|
+
fence_end: 0,
|
|
215
|
+
blockquote_idx: 0,
|
|
216
|
+
hr_char : '',
|
|
217
|
+
hr_chars : 0,
|
|
218
|
+
fence_start: 0,
|
|
219
|
+
spaces : new Uint8Array(TOKEN_ARRAY_CAP),
|
|
220
|
+
indent : "",
|
|
221
|
+
indent_len : 0,
|
|
222
|
+
table_state: 0,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Finish rendering the markdown - flushes any remaining text.
|
|
228
|
+
* @param {Parser} p
|
|
229
|
+
* @returns {void } */
|
|
230
|
+
export function parser_end(p) {
|
|
231
|
+
if (p.pending.length > 0) {
|
|
232
|
+
parser_write(p, "\n")
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @param {Parser} p
|
|
238
|
+
* @returns {void } */
|
|
239
|
+
function add_text(p) {
|
|
240
|
+
if (p.text.length === 0) return
|
|
241
|
+
console.assert(p.len > 0, "Never adding text to root")
|
|
242
|
+
p.renderer.add_text(p.renderer.data, p.text)
|
|
243
|
+
p.text = ""
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @param {Parser} p
|
|
248
|
+
* @returns {void } */
|
|
249
|
+
function ensure_paragraph(p) {
|
|
250
|
+
switch (p.token) {
|
|
251
|
+
case LINE_BREAK:
|
|
252
|
+
case DOCUMENT:
|
|
253
|
+
case BLOCKQUOTE:
|
|
254
|
+
case LIST_ORDERED:
|
|
255
|
+
case LIST_UNORDERED:
|
|
256
|
+
add_token(p, PARAGRAPH)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @param {Parser} p
|
|
262
|
+
* @param {string} text
|
|
263
|
+
* @returns {void } */
|
|
264
|
+
function push_text(p, text) {
|
|
265
|
+
ensure_paragraph(p)
|
|
266
|
+
p.text += text
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {Parser} p
|
|
271
|
+
* @returns {void } */
|
|
272
|
+
function end_token(p) {
|
|
273
|
+
console.assert(p.len > 0, "No nodes to end")
|
|
274
|
+
p.len -= 1
|
|
275
|
+
p.token = /** @type {Token} */ (p.tokens[p.len])
|
|
276
|
+
p.renderer.end_token(p.renderer.data)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @param {Parser} p
|
|
281
|
+
* @param {Token } token
|
|
282
|
+
* @returns {void } */
|
|
283
|
+
function add_token(p, token) {
|
|
284
|
+
/*
|
|
285
|
+
If a list doesn't start with a list item
|
|
286
|
+
it means that there was a newline after the list:
|
|
287
|
+
|
|
288
|
+
1. foo
|
|
289
|
+
2. bar
|
|
290
|
+
<empty line>
|
|
291
|
+
<not_a_list_item> <- new token
|
|
292
|
+
*/
|
|
293
|
+
if ((p.tokens[p.len] === LIST_ORDERED || p.tokens[p.len] === LIST_UNORDERED) &&
|
|
294
|
+
token !== LIST_ITEM
|
|
295
|
+
) {
|
|
296
|
+
end_token(p)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
p.len += 1
|
|
300
|
+
p.tokens[p.len] = token
|
|
301
|
+
p.token = token
|
|
302
|
+
p.renderer.add_token(p.renderer.data, token)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @param {Parser} p
|
|
307
|
+
* @param {number} token
|
|
308
|
+
* @param {number} start_idx
|
|
309
|
+
* @returns {number} */
|
|
310
|
+
function idx_of_token(p, token, start_idx) {
|
|
311
|
+
while (start_idx <= p.len) {
|
|
312
|
+
if (p.tokens[start_idx] === token) {
|
|
313
|
+
return start_idx
|
|
314
|
+
}
|
|
315
|
+
start_idx += 1
|
|
316
|
+
}
|
|
317
|
+
return -1
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* End tokens until the parser has the given length.
|
|
322
|
+
* @param {Parser} p
|
|
323
|
+
* @param {number} len
|
|
324
|
+
* @returns {void } */
|
|
325
|
+
function end_tokens_to_len(p, len) {
|
|
326
|
+
|
|
327
|
+
// TODO: specific token state should be reset only when the token ends
|
|
328
|
+
p.fence_start = 0
|
|
329
|
+
|
|
330
|
+
while (p.len > len) {
|
|
331
|
+
end_token(p)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* @param {Parser} p
|
|
337
|
+
* @param {number} indent
|
|
338
|
+
* @returns {number} */
|
|
339
|
+
function end_tokens_to_indent(p, indent) {
|
|
340
|
+
|
|
341
|
+
let idx = 0
|
|
342
|
+
for (let i = 0; i <= p.len; i += 1) {
|
|
343
|
+
indent -= p.spaces[i]
|
|
344
|
+
if (indent < 0) {
|
|
345
|
+
break
|
|
346
|
+
}
|
|
347
|
+
switch (p.tokens[i]) {
|
|
348
|
+
case CODE_BLOCK:
|
|
349
|
+
case CODE_FENCE:
|
|
350
|
+
case BLOCKQUOTE:
|
|
351
|
+
case LIST_ITEM:
|
|
352
|
+
idx = i
|
|
353
|
+
break
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
while (p.len > idx) {end_token(p)}
|
|
358
|
+
|
|
359
|
+
return indent
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* @param {Parser } p
|
|
364
|
+
* @param {Token } list_token
|
|
365
|
+
* @returns {boolean} added a new list */
|
|
366
|
+
function continue_or_add_list(p, list_token) {
|
|
367
|
+
/* will create a new list inside the last item
|
|
368
|
+
if the amount of spaces is greater than the last one (with prefix)
|
|
369
|
+
1. foo
|
|
370
|
+
- bar <- new nested ul
|
|
371
|
+
- baz <- new nested ul
|
|
372
|
+
12. qux <- cannot be nested in "baz" or "bar",
|
|
373
|
+
so it's a new list in "foo"
|
|
374
|
+
*/
|
|
375
|
+
let list_idx = -1
|
|
376
|
+
let item_idx = -1
|
|
377
|
+
|
|
378
|
+
for (let i = p.blockquote_idx+1; i <= p.len; i += 1) {
|
|
379
|
+
if (p.tokens[i] === LIST_ITEM) {
|
|
380
|
+
if (p.indent_len < p.spaces[i]) {
|
|
381
|
+
item_idx = -1
|
|
382
|
+
break
|
|
383
|
+
}
|
|
384
|
+
item_idx = i
|
|
385
|
+
} else if (p.tokens[i] === list_token) {
|
|
386
|
+
list_idx = i
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (item_idx === -1) {
|
|
391
|
+
if (list_idx === -1) {
|
|
392
|
+
end_tokens_to_len(p, p.blockquote_idx)
|
|
393
|
+
add_token(p, list_token)
|
|
394
|
+
return true
|
|
395
|
+
}
|
|
396
|
+
end_tokens_to_len(p, list_idx)
|
|
397
|
+
return false
|
|
398
|
+
}
|
|
399
|
+
end_tokens_to_len(p, item_idx)
|
|
400
|
+
add_token(p, list_token)
|
|
401
|
+
return true
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Create a new list
|
|
406
|
+
* or continue the last one
|
|
407
|
+
* @param {Parser } p
|
|
408
|
+
* @param {number } prefix_length
|
|
409
|
+
* @returns {void } */
|
|
410
|
+
function add_list_item(p, prefix_length) {
|
|
411
|
+
add_token(p, LIST_ITEM)
|
|
412
|
+
p.spaces[p.len] = p.indent_len + prefix_length
|
|
413
|
+
clear_root_pending(p)
|
|
414
|
+
p.token = MAYBE_TASK
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* @param {Parser} p
|
|
419
|
+
* @returns {void } */
|
|
420
|
+
function clear_root_pending(p) {
|
|
421
|
+
p.indent = ""
|
|
422
|
+
p.indent_len = 0
|
|
423
|
+
p.pending = ""
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @param {number} charcode
|
|
428
|
+
* @returns {boolean} */
|
|
429
|
+
function is_digit(charcode) {
|
|
430
|
+
switch (charcode) {
|
|
431
|
+
case 48: case 49: case 50: case 51: case 52:
|
|
432
|
+
case 53: case 54: case 55: case 56: case 57:
|
|
433
|
+
return true
|
|
434
|
+
default:
|
|
435
|
+
return false
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* @param {number} charcode
|
|
441
|
+
* @returns {boolean} */
|
|
442
|
+
function is_delimeter(charcode) {
|
|
443
|
+
switch (charcode) {
|
|
444
|
+
// " " ":" ";" ")" "," "!" "." "?" "]" "\n"
|
|
445
|
+
case 32: case 58: case 59: case 41: case 44: case 33: case 46: case 63: case 93: case 10:
|
|
446
|
+
return true
|
|
447
|
+
default:
|
|
448
|
+
return false
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* @param {number} charcode
|
|
454
|
+
* @returns {boolean} */
|
|
455
|
+
function is_delimeter_or_number(charcode) {
|
|
456
|
+
return is_digit(charcode) || is_delimeter(charcode)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @param {number} charcode
|
|
461
|
+
* @returns {boolean} */
|
|
462
|
+
function is_alnum(charcode) {
|
|
463
|
+
return is_digit(charcode) || // 0-9
|
|
464
|
+
(charcode >= 65 && charcode <= 90) || // A-Z
|
|
465
|
+
(charcode >= 97 && charcode <= 122) // a-z
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Parse and render another chunk of markdown.
|
|
470
|
+
* @param {Parser} p
|
|
471
|
+
* @param {string} chunk
|
|
472
|
+
* @returns {void } */
|
|
473
|
+
export function parser_write(p, chunk) {
|
|
474
|
+
for (const char of chunk) {
|
|
475
|
+
|
|
476
|
+
/*
|
|
477
|
+
Handle newlines
|
|
478
|
+
*/
|
|
479
|
+
if (p.token === NEWLINE) {
|
|
480
|
+
switch (char) {
|
|
481
|
+
case ' ':
|
|
482
|
+
p.indent_len += 1
|
|
483
|
+
continue
|
|
484
|
+
case '\t':
|
|
485
|
+
p.indent_len += 4
|
|
486
|
+
continue
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let indent = end_tokens_to_indent(p, p.indent_len)
|
|
490
|
+
|
|
491
|
+
p.indent_len = 0
|
|
492
|
+
p.token = p.tokens[p.len]
|
|
493
|
+
|
|
494
|
+
if (indent > 0) {
|
|
495
|
+
parser_write(p, " ".repeat(indent))
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const pending_with_char = p.pending + char
|
|
500
|
+
|
|
501
|
+
/*
|
|
502
|
+
Token specific checks
|
|
503
|
+
*/
|
|
504
|
+
switch (p.token) {
|
|
505
|
+
case LINE_BREAK:
|
|
506
|
+
case DOCUMENT:
|
|
507
|
+
case BLOCKQUOTE:
|
|
508
|
+
case LIST_ORDERED:
|
|
509
|
+
case LIST_UNORDERED:
|
|
510
|
+
console.assert(p.text.length === 0, "Root should not have any text")
|
|
511
|
+
|
|
512
|
+
switch (p.pending[0]) {
|
|
513
|
+
case undefined:
|
|
514
|
+
p.pending = char
|
|
515
|
+
continue
|
|
516
|
+
case ' ':
|
|
517
|
+
console.assert(p.pending.length === 1)
|
|
518
|
+
p.pending = char
|
|
519
|
+
p.indent += ' '
|
|
520
|
+
p.indent_len += 1
|
|
521
|
+
continue
|
|
522
|
+
case '\t':
|
|
523
|
+
console.assert(p.pending.length === 1)
|
|
524
|
+
p.pending = char
|
|
525
|
+
p.indent += '\t'
|
|
526
|
+
p.indent_len += 4
|
|
527
|
+
continue
|
|
528
|
+
case '\n':
|
|
529
|
+
console.assert(p.pending.length === 1)
|
|
530
|
+
/*
|
|
531
|
+
Lists can have an empty line in between items:
|
|
532
|
+
1. foo
|
|
533
|
+
<empty>
|
|
534
|
+
2. bar
|
|
535
|
+
*/
|
|
536
|
+
if (p.tokens[p.len] === LIST_ITEM && p.token === LINE_BREAK) {
|
|
537
|
+
end_token(p)
|
|
538
|
+
clear_root_pending(p)
|
|
539
|
+
p.pending = char
|
|
540
|
+
continue
|
|
541
|
+
}
|
|
542
|
+
/*
|
|
543
|
+
Exit out of tokens
|
|
544
|
+
And ignore newlines in root
|
|
545
|
+
*/
|
|
546
|
+
end_tokens_to_len(p, p.blockquote_idx)
|
|
547
|
+
clear_root_pending(p)
|
|
548
|
+
p.blockquote_idx = 0
|
|
549
|
+
p.fence_start = 0
|
|
550
|
+
p.pending = char
|
|
551
|
+
continue
|
|
552
|
+
/* Heading */
|
|
553
|
+
case '#':
|
|
554
|
+
switch (char) {
|
|
555
|
+
case '#':
|
|
556
|
+
if (p.pending.length < 6) {
|
|
557
|
+
p.pending = pending_with_char
|
|
558
|
+
continue
|
|
559
|
+
}
|
|
560
|
+
break // fail
|
|
561
|
+
case ' ':
|
|
562
|
+
end_tokens_to_indent(p, p.indent_len)
|
|
563
|
+
add_token(p, heading_from_level(p.pending.length))
|
|
564
|
+
clear_root_pending(p)
|
|
565
|
+
continue
|
|
566
|
+
}
|
|
567
|
+
break // fail
|
|
568
|
+
/* Blockquote */
|
|
569
|
+
case '>': {
|
|
570
|
+
const next_blockquote_idx = idx_of_token(p, BLOCKQUOTE, p.blockquote_idx+1)
|
|
571
|
+
|
|
572
|
+
/*
|
|
573
|
+
Only when there is no blockquote to the right of blockquote_idx
|
|
574
|
+
a new blockquote can be created
|
|
575
|
+
*/
|
|
576
|
+
if (next_blockquote_idx === -1) {
|
|
577
|
+
end_tokens_to_len(p, p.blockquote_idx)
|
|
578
|
+
p.blockquote_idx += 1
|
|
579
|
+
p.fence_start = 0
|
|
580
|
+
add_token(p, BLOCKQUOTE)
|
|
581
|
+
} else {
|
|
582
|
+
p.blockquote_idx = next_blockquote_idx
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
clear_root_pending(p)
|
|
586
|
+
p.pending = char
|
|
587
|
+
continue
|
|
588
|
+
}
|
|
589
|
+
/* Horizontal Rule
|
|
590
|
+
"-- - --- - --"
|
|
591
|
+
*/
|
|
592
|
+
case '-':
|
|
593
|
+
case '*':
|
|
594
|
+
case '_':
|
|
595
|
+
if (p.hr_chars === 0) {
|
|
596
|
+
console.assert(p.pending.length === 1, "Pending should be one character")
|
|
597
|
+
p.hr_chars = 1
|
|
598
|
+
p.hr_char = p.pending
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (p.hr_chars > 0) {
|
|
602
|
+
switch (char) {
|
|
603
|
+
case p.hr_char:
|
|
604
|
+
p.hr_chars += 1
|
|
605
|
+
p.pending = pending_with_char
|
|
606
|
+
continue
|
|
607
|
+
case ' ':
|
|
608
|
+
p.pending = pending_with_char
|
|
609
|
+
continue
|
|
610
|
+
case '\n':
|
|
611
|
+
if (p.hr_chars < 3) break
|
|
612
|
+
end_tokens_to_indent(p, p.indent_len)
|
|
613
|
+
p.renderer.add_token(p.renderer.data, RULE)
|
|
614
|
+
p.renderer.end_token(p.renderer.data)
|
|
615
|
+
clear_root_pending(p)
|
|
616
|
+
p.hr_chars = 0
|
|
617
|
+
continue
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
p.hr_chars = 0
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/* Unordered list
|
|
624
|
+
/ * foo
|
|
625
|
+
/ * *bar*
|
|
626
|
+
/ * **baz**
|
|
627
|
+
/*/
|
|
628
|
+
if ('_' !== p.pending[0] &&
|
|
629
|
+
' ' === p.pending[1]
|
|
630
|
+
) {
|
|
631
|
+
continue_or_add_list(p, LIST_UNORDERED)
|
|
632
|
+
add_list_item(p, 2)
|
|
633
|
+
parser_write(p, pending_with_char.slice(2))
|
|
634
|
+
continue
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
break // fail
|
|
638
|
+
/* Code Fence */
|
|
639
|
+
case '`':
|
|
640
|
+
/* ``?
|
|
641
|
+
^
|
|
642
|
+
*/
|
|
643
|
+
if (p.pending.length < 3) {
|
|
644
|
+
if ('`' === char) {
|
|
645
|
+
p.pending = pending_with_char
|
|
646
|
+
p.fence_start = pending_with_char.length
|
|
647
|
+
continue
|
|
648
|
+
}
|
|
649
|
+
p.fence_start = 0
|
|
650
|
+
break // fail
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
switch (char) {
|
|
654
|
+
case '`':
|
|
655
|
+
/* ````?
|
|
656
|
+
^
|
|
657
|
+
*/
|
|
658
|
+
if (p.pending.length === p.fence_start) {
|
|
659
|
+
p.pending = pending_with_char
|
|
660
|
+
p.fence_start = pending_with_char.length
|
|
661
|
+
}
|
|
662
|
+
/* ```code`
|
|
663
|
+
^
|
|
664
|
+
*/
|
|
665
|
+
else {
|
|
666
|
+
add_token(p, PARAGRAPH)
|
|
667
|
+
clear_root_pending(p)
|
|
668
|
+
p.fence_start = 0
|
|
669
|
+
parser_write(p, pending_with_char)
|
|
670
|
+
}
|
|
671
|
+
continue
|
|
672
|
+
case '\n': {
|
|
673
|
+
/* ```lang\n
|
|
674
|
+
^
|
|
675
|
+
*/
|
|
676
|
+
end_tokens_to_indent(p, p.indent_len)
|
|
677
|
+
|
|
678
|
+
add_token(p, CODE_FENCE)
|
|
679
|
+
if (p.pending.length > p.fence_start) {
|
|
680
|
+
p.renderer.set_attr(p.renderer.data, LANG, p.pending.slice(p.fence_start))
|
|
681
|
+
}
|
|
682
|
+
clear_root_pending(p)
|
|
683
|
+
p.token = NEWLINE
|
|
684
|
+
continue
|
|
685
|
+
}
|
|
686
|
+
default:
|
|
687
|
+
/* ```lang\n
|
|
688
|
+
^
|
|
689
|
+
*/
|
|
690
|
+
p.pending = pending_with_char
|
|
691
|
+
continue
|
|
692
|
+
}
|
|
693
|
+
/*
|
|
694
|
+
List Unordered for '+'
|
|
695
|
+
The other list types are handled with HORIZONTAL_RULE
|
|
696
|
+
*/
|
|
697
|
+
case '+':
|
|
698
|
+
if (' ' !== char) break // fail
|
|
699
|
+
|
|
700
|
+
continue_or_add_list(p, LIST_UNORDERED)
|
|
701
|
+
add_list_item(p, 2)
|
|
702
|
+
continue
|
|
703
|
+
/* List Ordered */
|
|
704
|
+
case '0': case '1': case '2': case '3': case '4':
|
|
705
|
+
case '5': case '6': case '7': case '8': case '9':
|
|
706
|
+
/*
|
|
707
|
+
12. foo
|
|
708
|
+
^
|
|
709
|
+
*/
|
|
710
|
+
if ('.' === p.pending[p.pending.length-1]) {
|
|
711
|
+
if (' ' !== char) break // fail
|
|
712
|
+
|
|
713
|
+
if (continue_or_add_list(p, LIST_ORDERED) && p.pending !== "1.") {
|
|
714
|
+
p.renderer.set_attr(p.renderer.data, START, p.pending.slice(0, -1))
|
|
715
|
+
}
|
|
716
|
+
add_list_item(p, p.pending.length+1)
|
|
717
|
+
continue
|
|
718
|
+
} else {
|
|
719
|
+
const char_code = char.charCodeAt(0)
|
|
720
|
+
if (46 === char_code || // '.'
|
|
721
|
+
is_digit(char_code) // 0-9
|
|
722
|
+
) {
|
|
723
|
+
p.pending = pending_with_char
|
|
724
|
+
continue
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
break // fail
|
|
728
|
+
/* Table */
|
|
729
|
+
case '|':
|
|
730
|
+
end_tokens_to_len(p, p.blockquote_idx)
|
|
731
|
+
|
|
732
|
+
add_token(p, TABLE)
|
|
733
|
+
add_token(p, TABLE_ROW)
|
|
734
|
+
|
|
735
|
+
p.pending = ""
|
|
736
|
+
parser_write(p, char)
|
|
737
|
+
|
|
738
|
+
continue
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
let to_write = pending_with_char
|
|
742
|
+
|
|
743
|
+
/* Add a line break and continue in previous token */
|
|
744
|
+
if (p.token === LINE_BREAK) {
|
|
745
|
+
p.token = p.tokens[p.len]
|
|
746
|
+
p.renderer.add_token(p.renderer.data, LINE_BREAK)
|
|
747
|
+
p.renderer.end_token(p.renderer.data)
|
|
748
|
+
}
|
|
749
|
+
/* Code Block */
|
|
750
|
+
else if (p.indent_len >= 4) {
|
|
751
|
+
/*
|
|
752
|
+
Case where there are additional spaces
|
|
753
|
+
after the indent that makes the code block
|
|
754
|
+
_________________________
|
|
755
|
+
code
|
|
756
|
+
^^^^----indent
|
|
757
|
+
^^^-part of code
|
|
758
|
+
_________________________
|
|
759
|
+
\t code
|
|
760
|
+
^^-----indent
|
|
761
|
+
^^^-part of code
|
|
762
|
+
*/
|
|
763
|
+
let code_start = 0
|
|
764
|
+
for (; code_start < 4; code_start += 1) {
|
|
765
|
+
if (p.indent[code_start] === '\t') {
|
|
766
|
+
code_start = code_start+1
|
|
767
|
+
break
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
to_write = p.indent.slice(code_start) + pending_with_char
|
|
771
|
+
add_token(p, CODE_BLOCK)
|
|
772
|
+
}
|
|
773
|
+
/* Paragraph */
|
|
774
|
+
else {
|
|
775
|
+
add_token(p, PARAGRAPH)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
clear_root_pending(p)
|
|
779
|
+
parser_write(p, to_write)
|
|
780
|
+
continue
|
|
781
|
+
case TABLE:
|
|
782
|
+
if (p.table_state === 1) {
|
|
783
|
+
switch (char) {
|
|
784
|
+
case '-':
|
|
785
|
+
case ' ':
|
|
786
|
+
case '|':
|
|
787
|
+
case ':':
|
|
788
|
+
p.pending = pending_with_char
|
|
789
|
+
continue
|
|
790
|
+
case '\n':
|
|
791
|
+
p.table_state = 2
|
|
792
|
+
p.pending = ""
|
|
793
|
+
continue
|
|
794
|
+
default:
|
|
795
|
+
end_token(p)
|
|
796
|
+
p.table_state = 0
|
|
797
|
+
break
|
|
798
|
+
}
|
|
799
|
+
} else {
|
|
800
|
+
switch (p.pending) {
|
|
801
|
+
case "|":
|
|
802
|
+
add_token(p, TABLE_ROW)
|
|
803
|
+
p.pending = ""
|
|
804
|
+
parser_write(p, char)
|
|
805
|
+
continue
|
|
806
|
+
case "\n":
|
|
807
|
+
end_token(p)
|
|
808
|
+
p.pending = ""
|
|
809
|
+
p.table_state = 0
|
|
810
|
+
parser_write(p, char)
|
|
811
|
+
continue
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
break
|
|
815
|
+
case TABLE_ROW:
|
|
816
|
+
switch (p.pending) {
|
|
817
|
+
case "":
|
|
818
|
+
break
|
|
819
|
+
case "|":
|
|
820
|
+
add_token(p, TABLE_CELL)
|
|
821
|
+
end_token(p)
|
|
822
|
+
p.pending = ""
|
|
823
|
+
parser_write(p, char)
|
|
824
|
+
continue
|
|
825
|
+
case "\n":
|
|
826
|
+
end_token(p)
|
|
827
|
+
p.table_state = Math.min(p.table_state+1, 2)
|
|
828
|
+
p.pending = ""
|
|
829
|
+
parser_write(p, char)
|
|
830
|
+
continue
|
|
831
|
+
default:
|
|
832
|
+
add_token(p, TABLE_CELL)
|
|
833
|
+
parser_write(p, char)
|
|
834
|
+
continue
|
|
835
|
+
}
|
|
836
|
+
break
|
|
837
|
+
case TABLE_CELL:
|
|
838
|
+
if (p.pending === "|") {
|
|
839
|
+
add_text(p)
|
|
840
|
+
end_token(p)
|
|
841
|
+
p.pending = ""
|
|
842
|
+
parser_write(p, char)
|
|
843
|
+
continue
|
|
844
|
+
}
|
|
845
|
+
break
|
|
846
|
+
case CODE_BLOCK:
|
|
847
|
+
switch (pending_with_char) {
|
|
848
|
+
case "\n ":
|
|
849
|
+
case "\n \t":
|
|
850
|
+
case "\n \t":
|
|
851
|
+
case "\n \t":
|
|
852
|
+
case "\n\t":
|
|
853
|
+
p.text += "\n"
|
|
854
|
+
p.pending = ""
|
|
855
|
+
continue
|
|
856
|
+
case "\n":
|
|
857
|
+
case "\n ":
|
|
858
|
+
case "\n ":
|
|
859
|
+
case "\n ":
|
|
860
|
+
p.pending = pending_with_char
|
|
861
|
+
continue
|
|
862
|
+
default:
|
|
863
|
+
if (p.pending.length !== 0) {
|
|
864
|
+
add_text(p)
|
|
865
|
+
end_token(p)
|
|
866
|
+
p.pending = char
|
|
867
|
+
} else {
|
|
868
|
+
p.text += char
|
|
869
|
+
}
|
|
870
|
+
continue
|
|
871
|
+
}
|
|
872
|
+
case CODE_FENCE:
|
|
873
|
+
switch (char) {
|
|
874
|
+
case '`':
|
|
875
|
+
/* ```\n<code>\n``??
|
|
876
|
+
| ^
|
|
877
|
+
*/
|
|
878
|
+
p.pending = pending_with_char
|
|
879
|
+
continue
|
|
880
|
+
case '\n':
|
|
881
|
+
/* ```\n<code>\n```\n
|
|
882
|
+
| ^
|
|
883
|
+
*/
|
|
884
|
+
if (pending_with_char.length === p.fence_start + p.fence_end + 1) {
|
|
885
|
+
add_text(p)
|
|
886
|
+
end_token(p)
|
|
887
|
+
p.pending = ""
|
|
888
|
+
p.fence_start = 0
|
|
889
|
+
p.fence_end = 0
|
|
890
|
+
p.token = NEWLINE
|
|
891
|
+
continue
|
|
892
|
+
}
|
|
893
|
+
p.token = NEWLINE
|
|
894
|
+
break
|
|
895
|
+
case ' ':
|
|
896
|
+
/* ```\n<code>\n ??
|
|
897
|
+
| ^ (space after newline is allowed)
|
|
898
|
+
*/
|
|
899
|
+
if (p.pending[0] === '\n') {
|
|
900
|
+
p.pending = pending_with_char
|
|
901
|
+
p.fence_end += 1
|
|
902
|
+
continue
|
|
903
|
+
}
|
|
904
|
+
break
|
|
905
|
+
}
|
|
906
|
+
// any other char
|
|
907
|
+
p.text += p.pending
|
|
908
|
+
p.pending = char
|
|
909
|
+
p.fence_end = 1
|
|
910
|
+
continue
|
|
911
|
+
case CODE_INLINE:
|
|
912
|
+
switch (char) {
|
|
913
|
+
case '`':
|
|
914
|
+
if (pending_with_char.length ===
|
|
915
|
+
p.fence_start + Number(p.pending[0] === ' ') // 0 or 1 for space
|
|
916
|
+
) {
|
|
917
|
+
add_text(p)
|
|
918
|
+
end_token(p)
|
|
919
|
+
p.pending = ""
|
|
920
|
+
p.fence_start = 0
|
|
921
|
+
} else {
|
|
922
|
+
p.pending = pending_with_char
|
|
923
|
+
}
|
|
924
|
+
continue
|
|
925
|
+
case '\n':
|
|
926
|
+
p.text += p.pending
|
|
927
|
+
p.pending = ""
|
|
928
|
+
p.token = LINE_BREAK
|
|
929
|
+
p.blockquote_idx = 0
|
|
930
|
+
add_text(p)
|
|
931
|
+
continue
|
|
932
|
+
/* Trim space before ` */
|
|
933
|
+
case ' ':
|
|
934
|
+
p.text += p.pending
|
|
935
|
+
p.pending = char
|
|
936
|
+
continue
|
|
937
|
+
default:
|
|
938
|
+
p.text += pending_with_char
|
|
939
|
+
p.pending = ""
|
|
940
|
+
continue
|
|
941
|
+
}
|
|
942
|
+
/* Checkboxes */
|
|
943
|
+
case MAYBE_TASK:
|
|
944
|
+
switch (p.pending.length) {
|
|
945
|
+
case 0:
|
|
946
|
+
if ('[' !== char) break // fail
|
|
947
|
+
p.pending = pending_with_char
|
|
948
|
+
continue
|
|
949
|
+
case 1:
|
|
950
|
+
if (' ' !== char && 'x' !== char) break // fail
|
|
951
|
+
p.pending = pending_with_char
|
|
952
|
+
continue
|
|
953
|
+
case 2:
|
|
954
|
+
if (']' !== char) break // fail
|
|
955
|
+
p.pending = pending_with_char
|
|
956
|
+
continue
|
|
957
|
+
case 3:
|
|
958
|
+
if (' ' !== char) break // fail
|
|
959
|
+
p.renderer.add_token(p.renderer.data, CHECKBOX)
|
|
960
|
+
if ('x' === p.pending[1]) {
|
|
961
|
+
p.renderer.set_attr(p.renderer.data, CHECKED, "")
|
|
962
|
+
}
|
|
963
|
+
p.renderer.end_token(p.renderer.data)
|
|
964
|
+
p.pending = " "
|
|
965
|
+
continue
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
p.token = p.tokens[p.len]
|
|
969
|
+
p.pending = ""
|
|
970
|
+
parser_write(p, pending_with_char)
|
|
971
|
+
continue
|
|
972
|
+
case STRONG_AST:
|
|
973
|
+
case STRONG_UND: {
|
|
974
|
+
/** @type {string} */ let symbol = '*'
|
|
975
|
+
/** @type {Token } */ let italic = ITALIC_AST
|
|
976
|
+
if (p.token === STRONG_UND) {
|
|
977
|
+
symbol = '_'
|
|
978
|
+
italic = ITALIC_UND
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
if (symbol === p.pending) {
|
|
982
|
+
add_text(p)
|
|
983
|
+
/* **Bold**
|
|
984
|
+
^
|
|
985
|
+
*/
|
|
986
|
+
if (symbol === char) {
|
|
987
|
+
end_token(p)
|
|
988
|
+
p.pending = ""
|
|
989
|
+
continue
|
|
990
|
+
}
|
|
991
|
+
/* **Bold*Bold->Em*
|
|
992
|
+
^
|
|
993
|
+
*/
|
|
994
|
+
add_token(p, italic)
|
|
995
|
+
p.pending = char
|
|
996
|
+
continue
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
break
|
|
1000
|
+
}
|
|
1001
|
+
case ITALIC_AST:
|
|
1002
|
+
case ITALIC_UND: {
|
|
1003
|
+
/** @type {string} */ let symbol = '*'
|
|
1004
|
+
/** @type {Token } */ let strong = STRONG_AST
|
|
1005
|
+
if (p.token === ITALIC_UND) {
|
|
1006
|
+
symbol = '_'
|
|
1007
|
+
strong = STRONG_UND
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
switch (p.pending) {
|
|
1011
|
+
case symbol:
|
|
1012
|
+
if (symbol === char) {
|
|
1013
|
+
/* Decide between ***bold>em**em* and **bold*bold>em***
|
|
1014
|
+
^ ^
|
|
1015
|
+
With the help of the next character
|
|
1016
|
+
*/
|
|
1017
|
+
if (p.tokens[p.len-1] === strong) {
|
|
1018
|
+
p.pending = pending_with_char
|
|
1019
|
+
}
|
|
1020
|
+
/* *em**bold
|
|
1021
|
+
^
|
|
1022
|
+
*/
|
|
1023
|
+
else {
|
|
1024
|
+
add_text(p)
|
|
1025
|
+
add_token(p, strong)
|
|
1026
|
+
p.pending = ""
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/* *em*foo
|
|
1030
|
+
^
|
|
1031
|
+
*/
|
|
1032
|
+
else {
|
|
1033
|
+
add_text(p)
|
|
1034
|
+
end_token(p)
|
|
1035
|
+
p.pending = char
|
|
1036
|
+
}
|
|
1037
|
+
continue
|
|
1038
|
+
case symbol+symbol:
|
|
1039
|
+
const italic = p.token
|
|
1040
|
+
add_text(p)
|
|
1041
|
+
end_token(p)
|
|
1042
|
+
end_token(p)
|
|
1043
|
+
/* ***bold>em**em* or **bold*bold>em***
|
|
1044
|
+
^ ^
|
|
1045
|
+
*/
|
|
1046
|
+
if (symbol !== char) {
|
|
1047
|
+
add_token(p, italic)
|
|
1048
|
+
p.pending = char
|
|
1049
|
+
} else {
|
|
1050
|
+
p.pending = ""
|
|
1051
|
+
}
|
|
1052
|
+
continue
|
|
1053
|
+
}
|
|
1054
|
+
break
|
|
1055
|
+
}
|
|
1056
|
+
case STRIKE:
|
|
1057
|
+
if ("~~" === pending_with_char) {
|
|
1058
|
+
add_text(p)
|
|
1059
|
+
end_token(p)
|
|
1060
|
+
p.pending = ""
|
|
1061
|
+
continue
|
|
1062
|
+
}
|
|
1063
|
+
break
|
|
1064
|
+
case MAYBE_EQ_BLOCK:
|
|
1065
|
+
/*
|
|
1066
|
+
\[? or $$?
|
|
1067
|
+
^ ^
|
|
1068
|
+
*/
|
|
1069
|
+
if (char === '\n') {
|
|
1070
|
+
add_text(p)
|
|
1071
|
+
add_token(p, EQUATION_BLOCK)
|
|
1072
|
+
p.pending = ""
|
|
1073
|
+
} else {
|
|
1074
|
+
p.token = p.tokens[p.len]
|
|
1075
|
+
if (p.pending[0] === '\\') {
|
|
1076
|
+
p.text += '['
|
|
1077
|
+
} else {
|
|
1078
|
+
p.text += '$$'
|
|
1079
|
+
}
|
|
1080
|
+
p.pending = ""
|
|
1081
|
+
parser_write(p, char)
|
|
1082
|
+
}
|
|
1083
|
+
continue
|
|
1084
|
+
case EQUATION_BLOCK:
|
|
1085
|
+
if ("\\]" === pending_with_char || "$$" === pending_with_char) {
|
|
1086
|
+
add_text(p)
|
|
1087
|
+
end_token(p)
|
|
1088
|
+
p.pending = ""
|
|
1089
|
+
continue
|
|
1090
|
+
}
|
|
1091
|
+
break
|
|
1092
|
+
case EQUATION_INLINE:
|
|
1093
|
+
if ("\\)" === pending_with_char || "$" === p.pending[0]) {
|
|
1094
|
+
add_text(p)
|
|
1095
|
+
end_token(p)
|
|
1096
|
+
|
|
1097
|
+
if(char === ')'){
|
|
1098
|
+
p.pending = ""
|
|
1099
|
+
} else {
|
|
1100
|
+
p.pending = char
|
|
1101
|
+
}
|
|
1102
|
+
continue
|
|
1103
|
+
}
|
|
1104
|
+
break
|
|
1105
|
+
/* Raw URLs */
|
|
1106
|
+
case MAYBE_URL:
|
|
1107
|
+
if ("http://" === pending_with_char ||
|
|
1108
|
+
"https://" === pending_with_char
|
|
1109
|
+
) {
|
|
1110
|
+
add_text(p)
|
|
1111
|
+
add_token(p, RAW_URL)
|
|
1112
|
+
p.pending = pending_with_char
|
|
1113
|
+
p.text = pending_with_char
|
|
1114
|
+
}
|
|
1115
|
+
else
|
|
1116
|
+
if ("http:/" [p.pending.length] === char ||
|
|
1117
|
+
"https:/"[p.pending.length] === char
|
|
1118
|
+
) {
|
|
1119
|
+
p.pending = pending_with_char
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
p.token = p.tokens[p.len]
|
|
1123
|
+
parser_write(p, char)
|
|
1124
|
+
}
|
|
1125
|
+
continue
|
|
1126
|
+
case LINK:
|
|
1127
|
+
case IMAGE:
|
|
1128
|
+
if ("]" === p.pending) {
|
|
1129
|
+
/*
|
|
1130
|
+
[Link](url)
|
|
1131
|
+
^
|
|
1132
|
+
*/
|
|
1133
|
+
add_text(p)
|
|
1134
|
+
if ('(' === char) {
|
|
1135
|
+
p.pending = pending_with_char
|
|
1136
|
+
} else {
|
|
1137
|
+
end_token(p)
|
|
1138
|
+
p.pending = char
|
|
1139
|
+
}
|
|
1140
|
+
continue
|
|
1141
|
+
}
|
|
1142
|
+
if (']' === p.pending[0] &&
|
|
1143
|
+
'(' === p.pending[1]
|
|
1144
|
+
) {
|
|
1145
|
+
/*
|
|
1146
|
+
[Link](url)
|
|
1147
|
+
^
|
|
1148
|
+
*/
|
|
1149
|
+
if (')' === char) {
|
|
1150
|
+
const type = p.token === LINK ? HREF : SRC
|
|
1151
|
+
const url = p.pending.slice(2)
|
|
1152
|
+
p.renderer.set_attr(p.renderer.data, type, url)
|
|
1153
|
+
end_token(p)
|
|
1154
|
+
p.pending = ""
|
|
1155
|
+
} else {
|
|
1156
|
+
p.pending += char
|
|
1157
|
+
}
|
|
1158
|
+
continue
|
|
1159
|
+
}
|
|
1160
|
+
break
|
|
1161
|
+
case RAW_URL:
|
|
1162
|
+
/* http://example.com?
|
|
1163
|
+
^
|
|
1164
|
+
*/
|
|
1165
|
+
if (' ' === char ||
|
|
1166
|
+
'\n'=== char ||
|
|
1167
|
+
'\\'=== char
|
|
1168
|
+
) {
|
|
1169
|
+
p.renderer.set_attr(p.renderer.data, HREF, p.pending)
|
|
1170
|
+
add_text(p)
|
|
1171
|
+
end_token(p)
|
|
1172
|
+
p.pending = char
|
|
1173
|
+
} else {
|
|
1174
|
+
p.text += char
|
|
1175
|
+
p.pending = pending_with_char
|
|
1176
|
+
}
|
|
1177
|
+
continue
|
|
1178
|
+
case MAYBE_BR:
|
|
1179
|
+
if (pending_with_char.startsWith("<br")) {
|
|
1180
|
+
if (/* "<br" */
|
|
1181
|
+
pending_with_char.length === 3 ||
|
|
1182
|
+
/* "<br " */
|
|
1183
|
+
char === ' ' ||
|
|
1184
|
+
/* "<br/" | "<br /" */
|
|
1185
|
+
char === '/' && (pending_with_char.length === 4 ||
|
|
1186
|
+
p.pending[p.pending.length-1] === ' ')
|
|
1187
|
+
) {
|
|
1188
|
+
p.pending = pending_with_char
|
|
1189
|
+
continue
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/* "<br>" | "<br/>" */
|
|
1193
|
+
if (char === '>') {
|
|
1194
|
+
add_text(p)
|
|
1195
|
+
p.token = p.tokens[p.len]
|
|
1196
|
+
p.renderer.add_token(p.renderer.data, LINE_BREAK)
|
|
1197
|
+
p.renderer.end_token(p.renderer.data)
|
|
1198
|
+
p.pending = ""
|
|
1199
|
+
continue
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
// Fail
|
|
1203
|
+
p.token = p.tokens[p.len]
|
|
1204
|
+
p.text += '<'
|
|
1205
|
+
p.pending = p.pending.slice(1)
|
|
1206
|
+
parser_write(p, char)
|
|
1207
|
+
continue
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/*
|
|
1211
|
+
Common checks
|
|
1212
|
+
*/
|
|
1213
|
+
switch (p.pending[0]) {
|
|
1214
|
+
/* Escape character */
|
|
1215
|
+
case '\\':
|
|
1216
|
+
if (p.token === IMAGE ||
|
|
1217
|
+
p.token === EQUATION_BLOCK ||
|
|
1218
|
+
p.token === EQUATION_INLINE)
|
|
1219
|
+
break
|
|
1220
|
+
|
|
1221
|
+
switch (char) {
|
|
1222
|
+
case '(':
|
|
1223
|
+
add_text(p)
|
|
1224
|
+
add_token(p, EQUATION_INLINE)
|
|
1225
|
+
p.pending = ""
|
|
1226
|
+
continue
|
|
1227
|
+
case '[':
|
|
1228
|
+
p.token = MAYBE_EQ_BLOCK
|
|
1229
|
+
p.pending = pending_with_char
|
|
1230
|
+
continue
|
|
1231
|
+
case '\n':
|
|
1232
|
+
// Escaped newline has the same affect as unescaped one
|
|
1233
|
+
p.pending = char
|
|
1234
|
+
continue
|
|
1235
|
+
default:
|
|
1236
|
+
let charcode = char.charCodeAt(0)
|
|
1237
|
+
p.pending = ""
|
|
1238
|
+
p.text += is_digit(charcode) || // 0-9
|
|
1239
|
+
(charcode >= 65 && charcode <= 90) || // A-Z
|
|
1240
|
+
(charcode >= 97 && charcode <= 122) // a-z
|
|
1241
|
+
? pending_with_char
|
|
1242
|
+
: char
|
|
1243
|
+
continue
|
|
1244
|
+
}
|
|
1245
|
+
/* Newline */
|
|
1246
|
+
case '\n':
|
|
1247
|
+
switch (p.token) {
|
|
1248
|
+
case IMAGE:
|
|
1249
|
+
case EQUATION_BLOCK:
|
|
1250
|
+
case EQUATION_INLINE:
|
|
1251
|
+
break
|
|
1252
|
+
case HEADING_1:
|
|
1253
|
+
case HEADING_2:
|
|
1254
|
+
case HEADING_3:
|
|
1255
|
+
case HEADING_4:
|
|
1256
|
+
case HEADING_5:
|
|
1257
|
+
case HEADING_6:
|
|
1258
|
+
add_text(p)
|
|
1259
|
+
end_tokens_to_len(p, p.blockquote_idx)
|
|
1260
|
+
p.blockquote_idx = 0
|
|
1261
|
+
p.pending = char
|
|
1262
|
+
continue
|
|
1263
|
+
default:
|
|
1264
|
+
add_text(p)
|
|
1265
|
+
p.pending = char
|
|
1266
|
+
p.token = LINE_BREAK
|
|
1267
|
+
p.blockquote_idx = 0
|
|
1268
|
+
continue
|
|
1269
|
+
}
|
|
1270
|
+
break
|
|
1271
|
+
/* <br> */
|
|
1272
|
+
case '<':
|
|
1273
|
+
if (p.token !== IMAGE &&
|
|
1274
|
+
p.token !== EQUATION_BLOCK &&
|
|
1275
|
+
p.token !== EQUATION_INLINE
|
|
1276
|
+
) {
|
|
1277
|
+
add_text(p)
|
|
1278
|
+
p.pending = pending_with_char
|
|
1279
|
+
p.token = MAYBE_BR
|
|
1280
|
+
continue
|
|
1281
|
+
}
|
|
1282
|
+
break
|
|
1283
|
+
/* `Code Inline` */
|
|
1284
|
+
case '`':
|
|
1285
|
+
if (p.token === IMAGE) break
|
|
1286
|
+
|
|
1287
|
+
if ('`' === char) {
|
|
1288
|
+
p.fence_start += 1
|
|
1289
|
+
p.pending = pending_with_char
|
|
1290
|
+
} else {
|
|
1291
|
+
p.fence_start += 1 // started at 0, and first wasn't counted
|
|
1292
|
+
add_text(p)
|
|
1293
|
+
add_token(p, CODE_INLINE)
|
|
1294
|
+
p.text = ' ' === char || '\n' === char ? "" : char // trim leading space
|
|
1295
|
+
p.pending = ""
|
|
1296
|
+
}
|
|
1297
|
+
continue
|
|
1298
|
+
case '_':
|
|
1299
|
+
case '*': {
|
|
1300
|
+
if (p.token === IMAGE ||
|
|
1301
|
+
p.token === EQUATION_BLOCK ||
|
|
1302
|
+
p.token === EQUATION_INLINE ||
|
|
1303
|
+
p.token === STRONG_AST)
|
|
1304
|
+
break
|
|
1305
|
+
|
|
1306
|
+
/** @type {Token} */ let italic = ITALIC_AST
|
|
1307
|
+
/** @type {Token} */ let strong = STRONG_AST
|
|
1308
|
+
const symbol = p.pending[0]
|
|
1309
|
+
if ('_' === symbol) {
|
|
1310
|
+
italic = ITALIC_UND
|
|
1311
|
+
strong = STRONG_UND
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
if (p.pending.length === 1) {
|
|
1315
|
+
/* **Strong**
|
|
1316
|
+
^
|
|
1317
|
+
*/
|
|
1318
|
+
if (symbol === char) {
|
|
1319
|
+
p.pending = pending_with_char
|
|
1320
|
+
continue
|
|
1321
|
+
}
|
|
1322
|
+
/* *Em*
|
|
1323
|
+
^
|
|
1324
|
+
*/
|
|
1325
|
+
if (' ' !== char && '\n' !== char) {
|
|
1326
|
+
add_text(p)
|
|
1327
|
+
add_token(p, italic)
|
|
1328
|
+
p.pending = char
|
|
1329
|
+
continue
|
|
1330
|
+
}
|
|
1331
|
+
} else {
|
|
1332
|
+
/* ***Strong->Em***
|
|
1333
|
+
^
|
|
1334
|
+
*/
|
|
1335
|
+
if (symbol === char) {
|
|
1336
|
+
add_text(p)
|
|
1337
|
+
add_token(p, strong)
|
|
1338
|
+
add_token(p, italic)
|
|
1339
|
+
p.pending = ""
|
|
1340
|
+
continue
|
|
1341
|
+
}
|
|
1342
|
+
/* **Strong**
|
|
1343
|
+
^
|
|
1344
|
+
*/
|
|
1345
|
+
if (' ' !== char && '\n' !== char) {
|
|
1346
|
+
add_text(p)
|
|
1347
|
+
add_token(p, strong)
|
|
1348
|
+
p.pending = char
|
|
1349
|
+
continue
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
break
|
|
1354
|
+
}
|
|
1355
|
+
case '~':
|
|
1356
|
+
if (p.token !== IMAGE &&
|
|
1357
|
+
p.token !== STRIKE
|
|
1358
|
+
) {
|
|
1359
|
+
if ("~" === p.pending) {
|
|
1360
|
+
/* ~~Strike~~
|
|
1361
|
+
^
|
|
1362
|
+
*/
|
|
1363
|
+
if ('~' === char) {
|
|
1364
|
+
p.pending = pending_with_char
|
|
1365
|
+
continue
|
|
1366
|
+
}
|
|
1367
|
+
} else {
|
|
1368
|
+
/* ~~Strike~~
|
|
1369
|
+
| ^
|
|
1370
|
+
*/
|
|
1371
|
+
if (' ' !== char && '\n' !== char) {
|
|
1372
|
+
add_text(p)
|
|
1373
|
+
add_token(p, STRIKE)
|
|
1374
|
+
p.pending = char
|
|
1375
|
+
continue
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
break
|
|
1380
|
+
/* $eq$ | $$eq$$ */
|
|
1381
|
+
case '$':
|
|
1382
|
+
if (p.token !== IMAGE &&
|
|
1383
|
+
p.token !== STRIKE &&
|
|
1384
|
+
"$" === p.pending
|
|
1385
|
+
) {
|
|
1386
|
+
/* $$EQUATION_BLOCK$$
|
|
1387
|
+
^
|
|
1388
|
+
*/
|
|
1389
|
+
if ('$' === char) {
|
|
1390
|
+
p.token = MAYBE_EQ_BLOCK
|
|
1391
|
+
p.pending = pending_with_char
|
|
1392
|
+
continue
|
|
1393
|
+
}
|
|
1394
|
+
/* $123
|
|
1395
|
+
^
|
|
1396
|
+
*/
|
|
1397
|
+
else if (is_delimeter_or_number(char.charCodeAt(0))) {
|
|
1398
|
+
break
|
|
1399
|
+
}
|
|
1400
|
+
/* $EQUATION_INLINE$
|
|
1401
|
+
^
|
|
1402
|
+
*/
|
|
1403
|
+
else {
|
|
1404
|
+
add_text(p)
|
|
1405
|
+
add_token(p, EQUATION_INLINE)
|
|
1406
|
+
p.pending = char
|
|
1407
|
+
continue
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
break
|
|
1411
|
+
/* [Image](url) */
|
|
1412
|
+
case '[':
|
|
1413
|
+
if (p.token !== IMAGE &&
|
|
1414
|
+
p.token !== LINK &&
|
|
1415
|
+
p.token !== EQUATION_BLOCK &&
|
|
1416
|
+
p.token !== EQUATION_INLINE &&
|
|
1417
|
+
']' !== char
|
|
1418
|
+
) {
|
|
1419
|
+
add_text(p)
|
|
1420
|
+
add_token(p, LINK)
|
|
1421
|
+
p.pending = char
|
|
1422
|
+
continue
|
|
1423
|
+
}
|
|
1424
|
+
break
|
|
1425
|
+
/*  */
|
|
1426
|
+
case '!':
|
|
1427
|
+
if (!(p.token === IMAGE) &&
|
|
1428
|
+
'[' === char
|
|
1429
|
+
) {
|
|
1430
|
+
add_text(p)
|
|
1431
|
+
add_token(p, IMAGE)
|
|
1432
|
+
p.pending = ""
|
|
1433
|
+
continue
|
|
1434
|
+
}
|
|
1435
|
+
break
|
|
1436
|
+
/* Trim spaces */
|
|
1437
|
+
case ' ':
|
|
1438
|
+
if (p.pending.length === 1 && ' ' === char) {
|
|
1439
|
+
continue
|
|
1440
|
+
}
|
|
1441
|
+
break
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/* foo http://...
|
|
1445
|
+
| ^
|
|
1446
|
+
*/
|
|
1447
|
+
if (p.token !== IMAGE &&
|
|
1448
|
+
p.token !== LINK &&
|
|
1449
|
+
p.token !== EQUATION_BLOCK &&
|
|
1450
|
+
p.token !== EQUATION_INLINE &&
|
|
1451
|
+
'h' === char &&
|
|
1452
|
+
(" " === p.pending ||
|
|
1453
|
+
"" === p.pending)
|
|
1454
|
+
) {
|
|
1455
|
+
p.text += p.pending
|
|
1456
|
+
p.pending = char
|
|
1457
|
+
|
|
1458
|
+
p.token = MAYBE_URL
|
|
1459
|
+
continue
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
/*
|
|
1463
|
+
No check hit
|
|
1464
|
+
*/
|
|
1465
|
+
p.text += p.pending
|
|
1466
|
+
p.pending = char
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
add_text(p)
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
/**
|
|
1474
|
+
* @template T
|
|
1475
|
+
* @callback Renderer_Add_Token
|
|
1476
|
+
* @param {T } data
|
|
1477
|
+
* @param {Token} type
|
|
1478
|
+
* @returns {void } */
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
* @template T
|
|
1482
|
+
* @callback Renderer_End_Token
|
|
1483
|
+
* @param {T } data
|
|
1484
|
+
* @returns {void } */
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* @template T
|
|
1488
|
+
* @callback Renderer_Add_Text
|
|
1489
|
+
* @param {T } data
|
|
1490
|
+
* @param {string} text
|
|
1491
|
+
* @returns {void } */
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* @template T
|
|
1495
|
+
* @callback Renderer_Set_Attr
|
|
1496
|
+
* @param {T } data
|
|
1497
|
+
* @param {Attr } type
|
|
1498
|
+
* @param {string} value
|
|
1499
|
+
* @returns {void } */
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* The renderer interface.
|
|
1503
|
+
* @template T
|
|
1504
|
+
* @typedef {object } Renderer
|
|
1505
|
+
* @property {T } data User data object. Available as first param in callbacks.
|
|
1506
|
+
* @property {Renderer_Add_Token<T>} add_token When the tokens starts.
|
|
1507
|
+
* @property {Renderer_End_Token<T>} end_token When the token ends.
|
|
1508
|
+
* @property {Renderer_Add_Text <T>} add_text To append text to current token. Can be called multiple times or none.
|
|
1509
|
+
* @property {Renderer_Set_Attr <T>} set_attr Set additional attributes of current token eg. the link url.
|
|
1510
|
+
*/
|
|
1511
|
+
|
|
1512
|
+
/** @typedef {Renderer<any>} Any_Renderer */
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* @typedef {object} Default_Renderer_Data
|
|
1517
|
+
* @property {HTMLElement[]} nodes
|
|
1518
|
+
* @property {number } index
|
|
1519
|
+
*
|
|
1520
|
+
* @typedef {Renderer <Default_Renderer_Data>} Default_Renderer
|
|
1521
|
+
* @typedef {Renderer_Add_Token<Default_Renderer_Data>} Default_Renderer_Add_Token
|
|
1522
|
+
* @typedef {Renderer_End_Token<Default_Renderer_Data>} Default_Renderer_End_Token
|
|
1523
|
+
* @typedef {Renderer_Add_Text <Default_Renderer_Data>} Default_Renderer_Add_Text
|
|
1524
|
+
* @typedef {Renderer_Set_Attr <Default_Renderer_Data>} Default_Renderer_Set_Attr
|
|
1525
|
+
*/
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* @param {HTMLElement } root
|
|
1529
|
+
* @returns {Default_Renderer} */
|
|
1530
|
+
export function default_renderer(root) {
|
|
1531
|
+
return {
|
|
1532
|
+
add_token: default_add_token,
|
|
1533
|
+
end_token: default_end_token,
|
|
1534
|
+
add_text: default_add_text,
|
|
1535
|
+
set_attr: default_set_attr,
|
|
1536
|
+
data : {
|
|
1537
|
+
nodes: /**@type {*}*/([root,,,,,]),
|
|
1538
|
+
index: 0,
|
|
1539
|
+
},
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/** @type {Default_Renderer_Add_Token} */
|
|
1544
|
+
export function default_add_token(data, type) {
|
|
1545
|
+
|
|
1546
|
+
/**@type {Element}*/ let parent = data.nodes[data.index]
|
|
1547
|
+
|
|
1548
|
+
/**@type {HTMLElement}*/ let slot
|
|
1549
|
+
|
|
1550
|
+
switch (type) {
|
|
1551
|
+
case DOCUMENT: return // document is provided
|
|
1552
|
+
case BLOCKQUOTE: slot = document.createElement("blockquote");break
|
|
1553
|
+
case PARAGRAPH: slot = document.createElement("p") ;break
|
|
1554
|
+
case LINE_BREAK: slot = document.createElement("br") ;break
|
|
1555
|
+
case RULE: slot = document.createElement("hr") ;break
|
|
1556
|
+
case HEADING_1: slot = document.createElement("h1") ;break
|
|
1557
|
+
case HEADING_2: slot = document.createElement("h2") ;break
|
|
1558
|
+
case HEADING_3: slot = document.createElement("h3") ;break
|
|
1559
|
+
case HEADING_4: slot = document.createElement("h4") ;break
|
|
1560
|
+
case HEADING_5: slot = document.createElement("h5") ;break
|
|
1561
|
+
case HEADING_6: slot = document.createElement("h6") ;break
|
|
1562
|
+
case ITALIC_AST:
|
|
1563
|
+
case ITALIC_UND: slot = document.createElement("em") ;break
|
|
1564
|
+
case STRONG_AST:
|
|
1565
|
+
case STRONG_UND: slot = document.createElement("strong") ;break
|
|
1566
|
+
case STRIKE: slot = document.createElement("s") ;break
|
|
1567
|
+
case CODE_INLINE: slot = document.createElement("code") ;break
|
|
1568
|
+
case RAW_URL:
|
|
1569
|
+
case LINK: slot = document.createElement("a") ;break
|
|
1570
|
+
case IMAGE: slot = document.createElement("img") ;break
|
|
1571
|
+
case LIST_UNORDERED:slot = document.createElement("ul") ;break
|
|
1572
|
+
case LIST_ORDERED: slot = document.createElement("ol") ;break
|
|
1573
|
+
case LIST_ITEM: slot = document.createElement("li") ;break
|
|
1574
|
+
case CHECKBOX:
|
|
1575
|
+
let checkbox = slot = document.createElement("input")
|
|
1576
|
+
checkbox.type = "checkbox"
|
|
1577
|
+
checkbox.disabled = true
|
|
1578
|
+
break
|
|
1579
|
+
case CODE_BLOCK:
|
|
1580
|
+
case CODE_FENCE:
|
|
1581
|
+
parent = parent.appendChild(document.createElement("pre"))
|
|
1582
|
+
slot = document.createElement("code")
|
|
1583
|
+
break
|
|
1584
|
+
case TABLE:
|
|
1585
|
+
slot = document.createElement("table")
|
|
1586
|
+
break
|
|
1587
|
+
case TABLE_ROW:
|
|
1588
|
+
switch (parent.children.length) {
|
|
1589
|
+
case 0:
|
|
1590
|
+
parent = parent.appendChild(document.createElement("thead"))
|
|
1591
|
+
break
|
|
1592
|
+
case 1:
|
|
1593
|
+
parent = parent.appendChild(document.createElement("tbody"))
|
|
1594
|
+
break
|
|
1595
|
+
default:
|
|
1596
|
+
parent = parent.children[1]
|
|
1597
|
+
}
|
|
1598
|
+
slot = document.createElement("tr")
|
|
1599
|
+
break
|
|
1600
|
+
case TABLE_CELL:
|
|
1601
|
+
slot = document.createElement(parent.parentElement?.tagName === "THEAD" ? "th" : "td")
|
|
1602
|
+
break
|
|
1603
|
+
case EQUATION_BLOCK: slot = document.createElement("equation-block"); break
|
|
1604
|
+
case EQUATION_INLINE: slot = document.createElement("equation-inline"); break
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
data.nodes[++data.index] = parent.appendChild(slot)
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
/** @type {Default_Renderer_End_Token} */
|
|
1611
|
+
export function default_end_token(data) {
|
|
1612
|
+
data.index -= 1
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/** @type {Default_Renderer_Add_Text} */
|
|
1616
|
+
export function default_add_text(data, text) {
|
|
1617
|
+
data.nodes[data.index].appendChild(document.createTextNode(text))
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/** @type {Default_Renderer_Set_Attr} */
|
|
1621
|
+
export function default_set_attr(data, type, value) {
|
|
1622
|
+
data.nodes[data.index].setAttribute(attr_to_html_attr(type), value)
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* @typedef {undefined} Logger_Renderer_Data
|
|
1628
|
+
*
|
|
1629
|
+
* @typedef {Renderer <Logger_Renderer_Data>} Logger_Renderer
|
|
1630
|
+
* @typedef {Renderer_Add_Token<Logger_Renderer_Data>} Logger_Renderer_Add_Token
|
|
1631
|
+
* @typedef {Renderer_End_Token<Logger_Renderer_Data>} Logger_Renderer_End_Token
|
|
1632
|
+
* @typedef {Renderer_Add_Text <Logger_Renderer_Data>} Logger_Renderer_Add_Text
|
|
1633
|
+
* @typedef {Renderer_Set_Attr <Logger_Renderer_Data>} Logger_Renderer_Set_Attr
|
|
1634
|
+
*/
|
|
1635
|
+
|
|
1636
|
+
/** @returns {Logger_Renderer} */
|
|
1637
|
+
export function logger_renderer() {
|
|
1638
|
+
return {
|
|
1639
|
+
data: undefined,
|
|
1640
|
+
add_token: logger_add_token,
|
|
1641
|
+
end_token: logger_end_token,
|
|
1642
|
+
add_text: logger_add_text,
|
|
1643
|
+
set_attr: logger_set_attr,
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/** @type {Logger_Renderer_Add_Token} */
|
|
1648
|
+
export function logger_add_token(data, type) {
|
|
1649
|
+
console.log("add_token:", token_to_string(type))
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/** @type {Logger_Renderer_End_Token} */
|
|
1653
|
+
export function logger_end_token(data) {
|
|
1654
|
+
console.log("end_token")
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/** @type {Logger_Renderer_Add_Text} */
|
|
1658
|
+
export function logger_add_text(data, text) {
|
|
1659
|
+
console.log('add_text: "%s"', text)
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/** @type {Logger_Renderer_Set_Attr} */
|
|
1663
|
+
export function logger_set_attr(data, type, value) {
|
|
1664
|
+
console.log('set_attr: %s="%s"', attr_to_html_attr(type), value)
|
|
1665
|
+
}
|