this repo has no description

change codeblock parsing, also major updates to demo page

12Me21 c0dd4e2c 7f815641

+268 -290
+80 -105
index.html
··· 1 - <!DOCTYPE html> 2 - <html lang=en-QS> 3 - <meta id=ݽ charset=utf-8> 1 + <!doctype html><html lang=en-QS><meta id=ݽ charset=utf-8> 2 + <meta name=viewport content="width=device-width, height=device-height, initial-scale=1"> 3 + 4 4 <title>Markup2 Demo</title> 5 5 6 6 <script src=langs.js></script> ··· 9 9 <script src=render.js></script> 10 10 <script src=runtime.js></script> 11 11 <script src=helpers.js></script> 12 + <link rel=stylesheet href=markup.css> 12 13 14 + <script src=testing/textarea.js></script> 15 + <link rel=stylesheet href=testing/common.css> 13 16 <script src=testing/tree.js></script> 17 + <link rel=stylesheet href=testing/tree.css> 14 18 15 - <meta name=viewport content="width=device-width, initial-scale=1, height=device-height"> 16 - <link rel=stylesheet href=markup.css> 17 19 <style> 18 - html { word-break: break-word; } 19 - 20 - html, body { 21 - position:fixed; top:0;left:0;right:0;bottom:0; 22 - background: var(--T-bg, white); 20 + table.times { 21 + width: 100%; 23 22 } 24 - 25 - html, select { font: var(--T-font); } 26 - 27 23 table.times, table.times td, table.times th { 24 + color: black; 28 25 border: 1px solid currentColor; 29 26 border-spacing: 0; 30 27 } ··· 32 29 padding: 2px 5px; 33 30 word-break: normal; overflow-wrap: break-word; 34 31 } 32 + table.times td { 33 + color: black; 34 + background: white; 35 + } 35 36 table.times th { 36 37 font-weight: bold; 37 38 color: white; ··· 39 40 } 40 41 table-overflow { 41 42 overflow-x: auto; 43 + background: #222; 44 + color: #FFF; 42 45 } 43 46 44 47 #\$output { 45 48 border: 3px solid black; 46 49 padding: 2px; 47 50 overflow-y: auto; 51 + } 52 + #\$inputs { 53 + flex-wrap: wrap; 54 + gap: .25em; 55 + background: #222; 56 + color: #FFF; 57 + padding: 2px; 48 58 } 49 59 50 - body { 51 - display: flex; /* grid. rowheights: min-content auto, colwidths: 1fr 1fr on mobile, uh stack them liiike.. just do a normal block scrolling layout (should textarea/output ever scroll?) or maybe display one at a time? just have an options dropdown?*/ 60 + label { 61 + align-self: center; 62 + font-weight: bold; 52 63 } 53 - body > * { 54 - width: 50%; 55 - } 56 - .Col { display: flex; flex-direction: column; } 57 - .Col > * { flex-shrink: 0; } 58 - .Col > .limit { flex-shrink: 1; min-height: 0; } 59 64 </style> 60 65 61 - <input-pane class='Col'> 62 - <select id=$lang> 63 - <option> 12y2 64 - <option> 12y 65 - <option> bbcode 66 - <option> plaintext 67 - </select> 66 + <body class='Col'> 67 + <script src=testing/nav.js></script> 68 + 69 + <main class='fill Split'> 70 + <input-pane class='Col'> 71 + <div class='Row' id=$inputs> 72 + <select id=$lang value=12y2> 73 + <option> 12y2 74 + <option> 12y 75 + <option> bbcode 76 + <option value=plaintext> plain 77 + </select> 78 + <span class='fill'></span> 79 + <select id=$display value=render> 80 + <option> render 81 + <option> nodes 82 + <option> json 83 + </select> 84 + </div> 85 + 86 + <textarea-container class='limit'> 87 + <textarea id=$input></textarea> 88 + </textarea-container> 89 + </input-pane> 68 90 69 - <textarea-container class='limit'> 70 - <textarea id=$input></textarea> 71 - </textarea-container> 72 - </input-pane> 91 + <output-pane class='Col'> 92 + <table-overflow><table class='times'> 93 + <tr> 94 + <th> <th> parse <th> render <th> layout <th> total 95 + <tr> 96 + <th> ms 97 + <td> <time id=$time1 datetime=Z></time> 98 + <td> <time id=$time2 datetime=Z></time> 99 + <td> <time id=$time3 datetime=Z></time> 100 + <td> <time id=$time4 datetime=Z></time> 101 + </table></table-overflow> 102 + <span id=$count></span> 103 + <div id=$output class='Markup limit'></div> 104 + </output-pane> 105 + </main> 73 106 74 - <output-pane class='Col'> 75 - <table-overflow><table class=times> 76 - <tr> 77 - <th> <th> parse <th> render <th> layout <th> total 78 - <tr> 79 - <th> time (ms) 80 - <td> <time id=$time1 datetime=Z></time> 81 - <td> <time id=$time2 datetime=Z></time> 82 - <td> <time id=$time3 datetime=Z></time> 83 - <td> <time id=$time4 datetime=Z></time> 84 - </table></table-overflow> 85 - <span id=$count></span> 86 - <div id=$output class='Markup limit'></div> 87 - </output-pane> 88 - 89 107 <script> 90 - let batch = (cb,w=0)=>e=>w++||requestAnimationFrame(_=>cb(e,w=0)) 91 - 92 108 function show_time(elem, ms) { 93 109 elem.dateTime = (ms/1000).toFixed(4)+' s' 94 110 elem.textContent = ms.toFixed(1) ··· 96 112 97 113 function update() { 98 114 //Markup.convert_lang($input.value, $lang.value, $output) 99 - let t0 = performance.now() 115 + let t0, t1, t2, t3 116 + t0 = performance.now() 100 117 let tree = Markup.langs.parse($input.value, $lang.value) 101 - window.tree = tree 102 - let t1 = performance.now() 103 - $output.replaceChildren(Markup.renderer.render(tree)) 104 - let t2 = performance.now() 118 + t1 = performance.now() 119 + if ('render'==$display.value) { 120 + $output.replaceChildren(Markup.renderer.render(tree)) 121 + } else if ('nodes'==$display.value) { 122 + let e = draw_tree(tree) 123 + $output.replaceChildren(e) 124 + } else if ('json'==$display.value) { 125 + $output.textContent = JSON.stringify(tree) 126 + } 127 + t2 = performance.now() 105 128 $output.scrollHeight 106 - let t3 = performance.now() 129 + t3 = performance.now() 107 130 show_time($time1, t1-t0) 108 131 show_time($time2, t2-t1) 109 132 show_time($time3, t3-t2) 110 133 show_time($time4, t3-t0) 111 134 } 112 135 113 - $input.addEventListener('input', batch(update), {passive:true}) 136 + setup_textarea($input, update) 114 137 115 - //$input.oninput = update 116 138 $lang.onchange = update 139 + $display.onchange = update 117 140 update() 118 141 </script> 119 - 120 - 121 - <!-- textarea --> 122 - 123 - <style> 124 - textarea-container, textarea-container > textarea { 125 - display: block; 126 - box-sizing: content-box; 127 - min-height: 5em; 128 - height: 0; 129 - font: 1em monospace; 130 - } 131 - 132 - textarea-container { 133 - padding: 2px; 134 - border: 2px solid #00C8B4; 135 - border-radius: 2px; 136 - } 137 - 138 - textarea-container > textarea { 139 - resize: none; 140 - overflow-y: scroll; 141 - margin: 0; 142 - border: none; 143 - padding: 0; 144 - width: 100%; 145 - 146 - appearance: none; 147 - outline-offset: 2px; 148 - } 149 - </style> 150 - 151 - <script> 152 - { 153 - let resize = (t)=>{ 154 - t.style.height = "0" 155 - t.parentNode.style.height = `${t.scrollHeight+1}px` 156 - t.style.height = "100%" 157 - } 158 - document.addEventListener('input', function(e) { 159 - let t = e.target 160 - if (t instanceof HTMLTextAreaElement && t.parentNode.tagName=='TEXTAREA-CONTAINER') 161 - resize(t) 162 - }, {passive: true}) 163 - for (let t of document.querySelectorAll("textarea-container > textarea")) 164 - resize(t) 165 - } 166 - </script>
+30
markup.css
··· 336 336 .M-media-volume { 337 337 width: 50px; 338 338 } 339 + 340 + /**************/ 341 + /** Headings **/ 342 + /**************/ 343 + 344 + .Markup h2, .Markup h3, .Markup h4, .Markup h5 { 345 + margin: 0.1em 0; 346 + border-bottom: 1px dotted var(--T-border-color); 347 + } 348 + 349 + .Markup h2 { 350 + font-size: 2em; 351 + font-weight: 500; 352 + background: var(--T-gray-bg); 353 + padding: 0 0.1em; 354 + border-bottom: 1px solid var(--T-border-color); 355 + border-top: 1px solid var(--T-border-color); 356 + } 357 + 358 + .Markup h3 { 359 + background: var(--T-box-bg); 360 + padding: 0 0.1em; 361 + font-size: 1.6875em; 362 + font-weight: 500; 363 + } 364 + 365 + .Markup h4 { 366 + font-size: 1.3125em; 367 + font-weight: bold; 368 + }
+8 -11
parse.js
··· 68 68 const ARGS_TABLE = // /[...]? */ 69 69 /(?:\[([^\]\n]*)\])? */y 70 70 71 - const ARGS_CODE = // /uhhh 72 - /(?: *([-\w.+#$ ]+?)? *(?:\n|$))?([^]*?)(?:```|$)/y 73 - ARGS_CODE._raw = true 71 + const ARGS_CODE = // ... ``` 72 + /(?: *([-\w.+#$ ]+?) *(?:\n|$))?([^]*?)(?:```|$)/y 74 73 75 74 // problem with improving style parsing: 76 75 // sometimes a style tag might be valid as both a start and end tag? ··· 84 83 PAT`[\\][{][\n]?${{ NULL_ENV: 0}}` 85 84 PAT`[\\]{ANY}${{ ESCAPED: 0}}` 86 85 PAT`{BOL}[>]${{ QUOTE: ARGS_HEADING}}` 87 - PAT`{BOL}[\`]{3}${{ CODE_BLOCK: ARGS_CODE}}` 86 + PAT`{BOL}[\`]{3}(?=[^\n\`]*?{EOL})${{ CODE_BLOCK: ARGS_CODE}}` 88 87 PAT`[\`][^\`\n]*([\`]{2}[^\`\n]*)*[\`]?${{ INLINE_CODE: 0}}` 89 88 PAT`([!]${{ EMBED: ARGS_BODYLESS}})?\b(https?://|sbs:){URL_CHARS}({URL_FINAL}|[(]{URL_CHARS}[)]({URL_CHARS}{URL_FINAL})?)${{ LINK: ARGS_NORMAL}}` 90 89 PAT`{BOL} *[|]${{ TABLE_START: ARGS_TABLE}}` ··· 180 179 OPEN('quote', token, {cite: rargs[0]}, body) 181 180 } break; case 'CODE_BLOCK': { 182 181 let lang = rargs 183 - // idea: strip leading indent from code? 184 182 BLOCK('code', {text: body, lang}) 185 183 } break; case 'INLINE_CODE': { 186 184 BLOCK('icode', {text: token.replace(/`(`)?/g, "$1")}) ··· 500 498 type = 'INVALID_TAG' 501 499 argregex = ARGS_NORMAL 502 500 } 501 + } else if ('TABLE_CELL'===type && !in_table()) { 502 + REGEX.lastIndex = match.index+1 503 + last = match.index 504 + continue 503 505 } else { 504 - if ('TABLE_CELL'===type && !in_table()) { 505 - REGEX.lastIndex = match.index+1 506 - last = match.index 507 - continue 508 - } 509 506 argregex = ARGTYPES[group_num] 510 507 } 511 508 ··· 531 528 let body = argmatch[2] // the {, or contents of raw tags 532 529 let word = argmatch[3] // only for syntax like \sub word 533 530 534 - if (!argregex._raw) { 531 + if (ARGS_CODE!==argregex) { 535 532 args = parse_args(args) 536 533 start_line = body 537 534 }
+48
testing/common.css
··· 1 + html { word-break: break-word; } 2 + 3 + html, body { 4 + position: fixed; 5 + top:0; left:0; right:0; bottom:0; 6 + background: var(--T-bg, white); 7 + } 8 + 9 + html, select { 10 + font: var(--T-font); 11 + } 12 + 13 + .Row, .Col { 14 + display: flex; 15 + } 16 + .Col { 17 + flex-direction: column; 18 + } 19 + 20 + .Col > *, .Row > * { 21 + flex-shrink: 0; 22 + } 23 + .Col > .limit, .Row > .limit { 24 + flex-shrink: 1; 25 + min-height: 0; 26 + } 27 + .Col > .fill, .Row > .fill { 28 + flex-grow: 1; 29 + flex-shrink: 1; 30 + min-height: 0; 31 + } 32 + 33 + .Split { 34 + display: flex; 35 + } 36 + .Split > * { 37 + width: 50%; 38 + } 39 + 40 + @media all and (max-width: 550px) { 41 + .Split { 42 + flex-direction: column-reverse; 43 + justify-content: left; 44 + } 45 + .Split > * { 46 + width: unset; 47 + } 48 + }
+3
testing/index.html
··· 9 9 <script src=draw.js></script> 10 10 <link rel=stylesheet href=style.css> 11 11 12 + <body> 13 + <script src=nav.js></script> 14 + 12 15 <button onclick="run()">run tests</button> 13 16 <hr> 14 17 <div id=$output></div>
+39
testing/nav.js
··· 1 + //let base = new URL("./", document.baseURI) 2 + let pages = [ 3 + ["Demo", 'index.html'], 4 + ["Tests", 'testing/index.html'], 5 + ] 6 + function make_link([title, url]) { 7 + let a = document.createElement('a') 8 + a.href = new URL("../"+url, document.currentScript.src) 9 + a.textContent = title 10 + if (a.href == window.location) 11 + a.classList.add('current') 12 + return a 13 + } 14 + let nav = document.createElement('nav') 15 + nav.append(...pages.map(make_link)) 16 + document.currentScript.replaceWith(nav) 17 + 18 + let style = document.createElement('style') 19 + style.textContent = ` 20 + nav { 21 + display: flex; 22 + gap: 0.5em; 23 + font: 1em sans-serif; 24 + align-items: start; 25 + margin-bottom: 8px; 26 + } 27 + nav > a { 28 + border: 3px solid; 29 + border-color: currentColor transparent; 30 + text-decoration: none; 31 + padding: 0 0.5em; 32 + font-weight: bold; 33 + } 34 + nav > a.current { 35 + color: gray; 36 + border: 3px dotted gray; 37 + } 38 + ` 39 + document.head.append(style)
+53
testing/textarea.js
··· 1 + { 2 + let style = document.createElement('style') 3 + style.textContent = ` 4 + textarea-container, textarea-container > textarea { 5 + display: block; 6 + box-sizing: content-box; 7 + min-height: 5em; 8 + height: 0; 9 + font: 1em monospace; 10 + } 11 + 12 + textarea-container { 13 + padding: 2px; 14 + border: 2px solid #00C8B4; 15 + border-radius: 2px; 16 + } 17 + 18 + textarea-container > textarea { 19 + resize: none; 20 + overflow-y: scroll; 21 + margin: 0; 22 + border: none; 23 + padding: 0; 24 + width: 100%; 25 + 26 + appearance: none; 27 + outline-offset: 2px; 28 + } 29 + ` 30 + document.head.append(style) 31 + 32 + let resize = (t)=>{ 33 + t.style.height = "0" 34 + t.parentNode.style.height = `${t.scrollHeight+1}px` 35 + t.style.height = "100%" 36 + } 37 + 38 + function setup_textarea(textarea, callback) { 39 + resize(textarea) 40 + 41 + let lock = false 42 + textarea.addEventListener('input', e=>{ 43 + resize(textarea) 44 + if (lock) 45 + return 46 + lock = true 47 + window.setTimeout(()=>{ 48 + lock = false 49 + callback(e) 50 + }) 51 + }, {passive: true, capture: true}) 52 + } 53 + }
+5 -4
testing/tree.css
··· 6 6 background: lavender; 7 7 border: 2px solid purple; 8 8 border-radius: 10px 10px 0 0; 9 - padding: 1px 3px; 9 + padding: 1px 5px; 10 10 vertical-align: top; 11 11 border-collapse: collapse; 12 12 border-left-color: black; ··· 46 46 width: auto; 47 47 } 48 48 49 + 49 50 tree-node.one > node-content { 50 51 display: flex; 51 52 flex-direction: row; ··· 79 80 80 81 tree-node.text { 81 82 display: inline-block; 82 - background: lavender; 83 + background: beige; 83 84 border: 2px solid purple; 84 85 border-radius: 10px; 85 86 padding: 1px 3px; 86 87 font-family: serif; 87 88 } 88 89 89 - tree-node:not(tree-node *) > node-content { 90 + /*tree-node:not(tree-node *) > node-content { 90 91 background: none; 91 - } 92 + }*/
-168
testing/tree.html
··· 1 - <!doctype html><html lang=en-QS><meta charset=utf-8> 2 - <meta name=viewport content="width=device-width, initial-scale=1"> 3 - <title>Markup2 Tree Viewer</title> 4 - 5 - <base href=..> 6 - <script src=langs.js></script> 7 - <script src=parse.js></script> 8 - <script src=legacy.js></script> 9 - <script src=render.js></script> 10 - <script src=runtime.js></script> 11 - <script src=helpers.js></script> 12 - 13 - <link rel=stylesheet href=markup.css> 14 - 15 - <script src=testing/tree.js></script> 16 - <link rel=stylesheet href=testing/tree.css> 17 - 18 - <style> 19 - html { word-break: break-word; } 20 - 21 - html, body { 22 - position:fixed; top:0;left:0;right:0;bottom:0; 23 - background: var(--T-bg, white); 24 - } 25 - 26 - html, select { font: var(--T-font); } 27 - 28 - table.times, table.times td, table.times th { 29 - border: 1px solid currentColor; 30 - border-spacing: 0; 31 - } 32 - table.times td, table.times th { 33 - padding: 2px 5px; 34 - word-break: normal; overflow-wrap: break-word; 35 - } 36 - table.times th { 37 - font-weight: bold; 38 - color: white; 39 - background: black; 40 - } 41 - table-overflow { 42 - overflow-x: auto; 43 - } 44 - 45 - #\$output { 46 - border: 3px solid black; 47 - padding: 2px; 48 - overflow-y: auto; 49 - } 50 - 51 - body { 52 - display: flex; /* grid. rowheights: min-content auto, colwidths: 1fr 1fr on mobile, uh stack them liiike.. just do a normal block scrolling layout (should textarea/output ever scroll?) or maybe display one at a time? just have an options dropdown?*/ 53 - } 54 - body > * { 55 - width: 50%; 56 - } 57 - .Col { display: flex; flex-direction: column; } 58 - .Col > * { flex-shrink: 0; } 59 - .Col > .limit { flex-shrink: 1; min-height: 0; } 60 - </style> 61 - 62 - <input-pane class='Col'> 63 - <select id=$lang> 64 - <option> 12y2 65 - <option> 12y 66 - <option> bbcode 67 - <option> plaintext 68 - </select> 69 - 70 - <textarea-container class='limit'> 71 - <textarea id=$input></textarea> 72 - </textarea-container> 73 - </input-pane> 74 - 75 - <output-pane class='Col'> 76 - <table-overflow><table class=times> 77 - <tr> 78 - <th> <th> parse <th> render <th> layout <th> total 79 - <tr> 80 - <th> time (ms) 81 - <td> <time id=$time1 datetime=Z></time> 82 - <td> <time id=$time2 datetime=Z></time> 83 - <td> <time id=$time3 datetime=Z></time> 84 - <td> <time id=$time4 datetime=Z></time> 85 - </table></table-overflow> 86 - <span id=$count></span> 87 - <div id=$output class='Markup limit'></div> 88 - </output-pane> 89 - 90 - <script> 91 - let batch = (cb,w=0)=>e=>w++||requestAnimationFrame(_=>cb(e,w=0)) 92 - 93 - function show_time(elem, ms) { 94 - elem.dateTime = (ms/1000).toFixed(4)+' s' 95 - elem.textContent = ms.toFixed(1) 96 - } 97 - 98 - function update() { 99 - //Markup.convert_lang($input.value, $lang.value, $output) 100 - let t0 = performance.now() 101 - let tree = Markup.langs.parse($input.value, $lang.value) 102 - window.tree = tree 103 - let t1 = performance.now() 104 - $output.replaceChildren(draw_node(tree)) 105 - //$output.replaceChildren(Markup.renderer.render(tree)) 106 - let t2 = performance.now() 107 - $output.scrollHeight 108 - let t3 = performance.now() 109 - show_time($time1, t1-t0) 110 - show_time($time2, t2-t1) 111 - show_time($time3, t3-t2) 112 - show_time($time4, t3-t0) 113 - } 114 - 115 - $input.addEventListener('input', batch(update), {passive:true}) 116 - 117 - //$input.oninput = update 118 - $lang.onchange = update 119 - update() 120 - </script> 121 - 122 - 123 - <!-- textarea --> 124 - 125 - <style> 126 - textarea-container, textarea-container > textarea { 127 - display: block; 128 - box-sizing: content-box; 129 - min-height: 5em; 130 - height: 0; 131 - font: 1em monospace; 132 - } 133 - 134 - textarea-container { 135 - padding: 2px; 136 - border: 2px solid #00C8B4; 137 - border-radius: 2px; 138 - } 139 - 140 - textarea-container > textarea { 141 - resize: none; 142 - overflow-y: scroll; 143 - margin: 0; 144 - border: none; 145 - padding: 0; 146 - width: 100%; 147 - 148 - appearance: none; 149 - outline-offset: 2px; 150 - } 151 - </style> 152 - 153 - <script> 154 - { 155 - let resize = (t)=>{ 156 - t.style.height = "0" 157 - t.parentNode.style.height = `${t.scrollHeight+1}px` 158 - t.style.height = "100%" 159 - } 160 - document.addEventListener('input', function(e) { 161 - let t = e.target 162 - if (t instanceof HTMLTextAreaElement && t.parentNode.tagName=='TEXTAREA-CONTAINER') 163 - resize(t) 164 - }, {passive: true}) 165 - for (let t of document.querySelectorAll("textarea-container > textarea")) 166 - resize(t) 167 - } 168 - </script>
+2 -2
testing/tree.js
··· 57 57 }.bind(𐀶`<tree-node class='text'>`), 58 58 } 59 59 60 - function draw_node(node) { 60 + function draw_tree(node) { 61 61 let elem = CREATE.node(node) 62 62 if (node.content) 63 63 for (let n of node.content) 64 - elem.append(draw_node(n)) 64 + elem.append(draw_tree(n)) 65 65 return elem.getRootNode() 66 66 }