this repo has no description

cleanup

12Me21 ac42160e 577a120b

+88 -98
+1 -1
package.json
··· 61 61 "objects": "always-multiline", 62 62 "functions": "never" 63 63 }], 64 + "no-useless-escape": ["off"], 64 65 "comma-spacing": ["warn"], 65 66 "comma-style": ["warn"], 66 67 "computed-property-spacing": ["warn"], 67 68 "dot-location": ["error", "property"], 68 69 "func-call-spacing": ["error"], 69 - "key-spacing": ["warn"], 70 70 "keyword-spacing": ["warn"], 71 71 "max-len": ["error", {"code": 400}], 72 72 "new-parens": ["warn"],
+86 -96
parse.js
··· 107 107 '\\key': ARGS_WORD, 108 108 } 109 109 110 - function find_style(token) { 111 - for (let c=current; 'style'===c.type; c=c.parent) 112 - if (c.token===token) 113 - return c 114 - } 115 - 116 110 // process a token 117 111 // ๐Ÿ“ฅ _token_type ๐Ÿท TokenType ๐Ÿ“ 118 112 // ๐Ÿ“ฅ token ๐Ÿท Text ๐Ÿ“ token text, including arguments ··· 129 123 let level = base_token.length 130 124 let args = {level} 131 125 // todo: anchor name (and, can this be chosen automatically based on contents?) 132 - OPEN('heading', token, args, body) 126 + OPEN('heading', args, body) 133 127 } break; case 'DIVIDER': { 134 128 BLOCK('divider') 135 129 } break; case 'BLOCK_END': { ··· 149 143 TEXT("}") 150 144 } 151 145 } break; case 'NULL_ENV': { 152 - OPEN('null_env', token, null, true) 146 + OPEN('null_env', null, true) 153 147 current.prev = current.parent.prev 154 148 } break; case 'ESCAPED': { 155 149 if ("\\\n"===token) ··· 161 155 } else 162 156 TEXT(token.substr(1)) 163 157 } break; case 'QUOTE': { 164 - OPEN('quote', token, {cite: rargs[0]}, body) 158 + OPEN('quote', {cite: rargs[0]}, body) 165 159 } break; case 'CODE_BLOCK': { 166 160 let lang = rargs 167 161 BLOCK('code', {text: body, lang}) 168 162 } break; case 'INLINE_CODE': { 169 163 BLOCK('icode', {text: token.replace(/`(`)?/g, "$1")}) 170 - /* } break; case 'INLINE_CODE_2': { 171 - token = token.slice(2, token.endsWith("``") ? -2 : 1/0) 172 - BLOCK('icode', {text: token}) 173 - } break; case 'LINE_CODE': { 174 - BLOCK('icode', {text: token.substring(2)})*/ 175 164 } break; case 'EMBED': { 176 165 let url = base_token.substr(1) // ehh better 177 166 let [type, args] = process_embed(url, rargs) ··· 180 169 let url = base_token 181 170 let args = {url} 182 171 if (body) { 183 - OPEN('link', token, args, body) 172 + OPEN('link', args, body) 184 173 } else { 185 174 args.text = rargs[0] 186 175 BLOCK('simple_link', args) 187 176 } 188 177 } break; case 'TABLE_START': { 189 - OPEN('table_row', token, {}) 190 - OPEN('table_cell', "", rargs, body) 178 + OPEN('table_row', token) // special OPEN call 179 + OPEN('table_cell', rargs, body) 191 180 } break; case 'TABLE_CELL': { 192 181 while (current.type!=='table_cell') 193 182 CANCEL() 194 183 CLOSE() // cell 195 184 // we don't know whether these are row args or cell args, 196 185 // so just pass the raw args directly, and parse them later. 197 - OPEN('table_cell', token, rargs, body) 186 + OPEN('table_cell', rargs, body) 198 187 } break; case 'INVALID_TAG': { 199 188 if (body) 200 - OPEN('invalid', token, {text: token, reason: "invalid tag"}, body) 189 + OPEN('invalid', {text: token, reason: "invalid tag"}, body) 201 190 else 202 191 BLOCK('invalid', {text: token, reason: "invalid tag"}) 203 192 } break; case 'LIST_ITEM': { 204 193 let indent = token.indexOf("-") 205 - OPEN('list_item', token, {indent}, body) 194 + OPEN('list_item', {indent}, body) 206 195 207 196 } break; case '\\sub': { 208 - OPEN('subscript', token, null, body) 197 + OPEN('subscript', null, body) 209 198 } break; case '\\sup': { 210 - OPEN('superscript', token, null, body) 199 + OPEN('superscript', null, body) 211 200 } break; case '\\b': { 212 - OPEN('bold', token, null, body) 201 + OPEN('bold', null, body) 213 202 } break; case '\\i': { 214 - OPEN('italic', token, null, body) 203 + OPEN('italic', null, body) 215 204 } break; case '\\u': { 216 - OPEN('underline', token, null, body) 205 + OPEN('underline', null, body) 217 206 } break; case '\\s': { 218 - OPEN('strikethrough', token, null, body) 207 + OPEN('strikethrough', null, body) 219 208 } break; case '\\quote': { 220 - OPEN('quote', token, {cite: rargs[0]}, body) 209 + OPEN('quote', {cite: rargs[0]}, body) 221 210 } break; case '\\align': { 222 211 let a = rargs[0] 223 212 if (!['left', 'right', 'center'].includes(a)) 224 213 a = 'center' 225 - OPEN('align', token, {align: a}, body) 214 + OPEN('align', {align: a}, body) 226 215 } break; case '\\spoiler': { 227 216 let label = arg0(rargs, "spoiler") // todo: handle this default value in the renderer 228 - OPEN('spoiler', token, {label}, body) 217 + OPEN('spoiler', {label}, body) 229 218 } break; case '\\ruby': { 230 219 let text = arg0(rargs, "true") 231 - OPEN('ruby', token, {text}, body) 220 + OPEN('ruby', {text}, body) 232 221 } break; case '\\key': { 233 - OPEN('key', token, null, body) 222 + OPEN('key', null, body) 234 223 } } 235 224 } 236 225 ··· 301 290 } 302 291 303 292 // start a new block 304 - function OPEN(type, token, args, body) { 293 + function OPEN(type, args, body) { 305 294 current = Object.seal({ 306 295 type, args, content: [], 307 - token, body, parent: current, 296 + body, parent: current, 308 297 prev: 'all_newline', 309 298 }) 310 299 if (body) ··· 319 308 return o 320 309 } 321 310 322 - function can_cancel(o) { 323 - return 'style'===o.type 324 - } 325 - 326 311 function CANCEL() { 327 - if (can_cancel(current)) { 312 + if ('style'===current.type) { 328 313 let o = pop() 329 - 330 - if (o.token) 331 - current.content.push(o.token) 332 - else if ('block'===current.prev && "\n"===o.content[0]) 333 - o.content.shift() // strip newline 334 - 335 - current.content.push(...o.content) 314 + current.content.push(o.args, ...o.content) 336 315 current.prev = o.prev 337 - } else if ('table_cell'===current.type && !current.content.length) { 338 - // cancelling an empty table cell means it's the end of the row 339 - // so, discard the cell 340 - let o = pop() 341 - // if the row is empty (i.e. we just have a single | ) 342 - if (!current.content.length) { 343 - let o = pop() // discard the row 344 - TEXT(o.token) 345 - return 346 - } 347 - // transfer args to the row, and parse as table row args: 348 - let ret = current.args 349 - for (let arg of o.args) { 350 - if ("*"===arg || "#"===arg) { 351 - ret.header = true 316 + return 317 + } 318 + if ('table_cell'===current.type) { 319 + if (current.content.length) { 320 + CLOSE() // table_cell 321 + current.args = {} 322 + } else { 323 + // cancelling an empty table cell means: 324 + // it's the end of the row, so discard the cell 325 + let o = pop() 326 + // if the ROW is empty (i.e. we just have a single | ) 327 + if (!current.content.length) { 328 + let o = pop() // discard the row 329 + TEXT(o.args) 330 + return 331 + // todo: maybe also cancel rows with 1 unclosed cell? 332 + // like `| abc` -> text 333 + } 334 + // transfer args to the row, and parse as table row args: 335 + let ret = current.args = {} 336 + for (let arg of o.args) { 337 + if ("*"===arg || "#"===arg) { 338 + ret.header = true 339 + } 352 340 } 353 341 } 354 - // close the row 355 - CLOSE() 356 - } else 357 - CLOSE() 358 - // todo: maybe also cancel rows with just 1 unclosed cell? 359 - // like `| abc` -> text 342 + // fallthrough to close the table_row 343 + } 344 + CLOSE() 360 345 } 361 346 362 347 function get_last(block) { ··· 418 403 if (+h > 1) ret.rowspan = +h 419 404 } 420 405 } 406 + } else if ('style'===o.type) { 407 + node.type = { 408 + __proto__:null, 409 + '**': 'bold', '__': 'underline', 410 + '~~': 'strikethrough', '/': 'italic', 411 + }[o.args] 412 + node.args = null 421 413 } 422 414 423 415 dest.content.push(node) ··· 448 440 449 441 function in_table() { 450 442 for (let c=current; ; c=c.parent) { 451 - if (c.type==='table_cell') 443 + if ('table_cell'===c.type) 452 444 return true 453 - if (!can_cancel(c)) 445 + if ('style'!==c.type) 454 446 return false 455 447 } 448 + } 449 + // todo: this should check for body 450 + function find_style(token) { 451 + for (let c=current; 'style'===c.type; c=c.parent) 452 + if (c.args===token) 453 + return c 456 454 } 457 455 458 456 function parse(text) { ··· 466 464 let match 467 465 function nevermind() { 468 466 REGEX.lastIndex = match.index+1 469 - //last = match.index 470 467 } 471 - function accept(i) { 468 + function accept() { 472 469 TEXT(text.substring(last, match.index)) 473 - last = i 470 + last = REGEX.lastIndex 471 + } 472 + function start_line() { 473 + text = text.substring(last) 474 + last = REGEX.lastIndex = 0 475 + prev = -1 474 476 } 475 477 main: while (match = REGEX.exec(text)) { 476 478 // check for infinite loops 477 479 if (match.index===prev) 478 480 throw ["INFINITE LOOP", match] 479 481 prev = match.index 480 - // 1: insert the text from after previous token 481 - // idea: defer this until we actually KNOW we matched a token 482 - //TEXT(text.substring(last, match.index)) 483 482 // 2: figure out which token type was matched 484 483 let token_text = match[0] 485 484 let group_num = match.indexOf("", 1)-1 ··· 487 486 // 3: get type + argument pattern 488 487 let type = GROUPS[group_num] 489 488 let argregex 489 + // 4: special cases: 490 490 if ('TAG'===type) { 491 491 if (token_text in TAGS) { 492 492 type = token_text ··· 502 502 if ('style'===current.type && !` \t\n,'"`.includes(before) && `- \t\n.,:;!?'")}`.includes(after)) { 503 503 let c = find_style(token_text) 504 504 if (c) { 505 - accept(REGEX.lastIndex) 505 + accept() 506 506 while (current != c) 507 507 CANCEL() 508 - current.type = { 509 - __proto__:null, 510 - '**': 'bold', '__': 'underline', 511 - '~~': 'strikethrough', '/': 'italic', 512 - }[current.token] 513 508 CLOSE() 514 509 continue main 515 510 } 516 511 } 517 512 // open? 518 513 if (` \t\n({'"`.includes(before) && !` \t\n,'"`.includes(after)) { 519 - accept(REGEX.lastIndex) 514 + accept() 520 515 OPEN('style', token_text) 521 516 continue main 522 517 } ··· 528 523 } else { 529 524 argregex = ARGTYPES[group_num] 530 525 } 531 - // 4: parse args and { 532 - let start_line = false 526 + // 5: parse args and { 533 527 if (!argregex) { 534 - accept(REGEX.lastIndex) 528 + accept() 535 529 let body = 'NULL_ENV'===type //h 536 530 PROCESS(type, token_text, null, body, token_text) 537 531 if (body || 'NEWLINE'===type) 538 - start_line = true 532 + start_line() 539 533 } else { 540 534 // try to match arguments 541 535 argregex.lastIndex = REGEX.lastIndex ··· 549 543 let body = argmatch[2] // the {, or contents of raw tags 550 544 let word = argmatch[3] // only for syntax like \sub word 551 545 552 - if (ARGS_CODE!==argregex) { 546 + if (ARGS_CODE!==argregex) 553 547 args = parse_args(args) 554 - start_line = body 555 - } 556 - accept(REGEX.lastIndex = argregex.lastIndex) 548 + 549 + REGEX.lastIndex = argregex.lastIndex 550 + accept() 557 551 558 552 PROCESS(type, full_token, args, body, token_text) 559 553 // word 560 554 if (undefined!==word) { 561 555 TEXT(word.replace(/\\([^])/g, "$1")) 562 556 CLOSE() 563 - start_line = false 564 557 } 565 - } 566 - // 5: handle start-of-line 567 - if (start_line) { 568 - text = text.substring(last) 569 - last = REGEX.lastIndex = 0 570 - prev = -1 558 + // start new line at `{` 559 + if (body && ARGS_CODE!==argregex && undefined!==word) 560 + start_line() 571 561 } 572 562 } // end of main loop 573 563
+1 -1
render.js
··· 162 162 } 163 163 audio.onvolumechange = e=>{ 164 164 let volume = audio.volume 165 - vol.textContent = volume ? ["๐Ÿ”ˆ","๐Ÿ”‰","๐Ÿ”Š"][volume*2.99|0] : "๐Ÿ”‡" 165 + vol.textContent = volume ? ["๐Ÿ”ˆ", "๐Ÿ”‰", "๐Ÿ”Š"][volume*2.99|0] : "๐Ÿ”‡" 166 166 } 167 167 volume.value = audio.volume 168 168 audio.onvolumechange()