├── LICENSE ├── README.md ├── dist ├── casual-markdown-doc.css ├── casual-markdown-doc.js ├── casual-markdown-page.html ├── casual-markdown.css ├── casual-markdown.js ├── casual-markdown@0.82.css ├── casual-markdown@0.82.js ├── casual-markdown@0.85.css ├── casual-markdown@0.85.js ├── casual-markdown@0.90.css ├── casual-markdown@0.90.js ├── casual-markdown@0.92.css └── casual-markdown@0.92.js └── source ├── casual-markdown-doc.css ├── casual-markdown-doc.js ├── casual-markdown-page.html ├── casual-markdown.css └── casual-markdown.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 CK Hung 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## casual-markdown 2 | 3 | A lightweight RegExp-based markdown parser, with TOC, scrollspy and frontmatter support 4 | 5 | It revises from simple-markdown-parser of [Powerpage Markdown Document](https://github.com/casualwriter/powerpage-md-document) 6 | for the following features 7 | 8 | * simple, super lightweight (200 lines) 9 | * vanilla javascript, no dependence 10 | * all browsers supported (IE9+, Chrome, Firefox, Brave, etc..) 11 | * straight-forward coding style, hopefully readable. 12 | * support [basic+enhanced syntax](https://casualwriter.github.io/casual-markdown/casual-markdown-syntax.html) according [Basic Markdown Syntax (markdownguide.org)](https://www.markdownguide.org/basic-syntax/) 13 | * TOC and scrollspy support 14 | * highlight comments/keywords in code-block 15 | * frontmatter for simple YAML 16 | * extendable (by override md.before, md.after, md.formatCode, md.formatYAML) 17 | 18 | 19 | ### Usage Guide 20 | 21 | just simply include [casual-markdown.js](https://github.com/casualwriter/casual-markdown/blob/main/source/casual-markdown.js) from CDN or local. 22 | 23 | ~~~ 24 | 25 | 26 | ~~~ 27 | 28 | or github-page (may specify version no) 29 | 30 | ~~~ 31 | 32 | 33 | ~~~ 34 | 35 | or may download `casual-markdown.js, casual-markdown.css` and include them locally. 36 | 37 | 38 | ### Basic Usage 39 | 40 | Once include the javascript library, casual-markdown object `md` is available with below functions. 41 | 42 | * `md.html(mdString)` - convert markdown string into html 43 | * `md.toc( mdElement, tocElement, options)` - generate TOC html to tocElement 44 | * default TOC option := { title:'Table of Contents', css:'h1,h2,h3,h4', scrollspy:null } 45 | 46 | for example 47 | 48 | ~~~ 49 | // get markdown source from element 50 | var mdText = document.getElementById('source').innerHTML 51 | 52 | // parse markdown, and render content 53 | document.getElementById('content').innerHTML = md.html( mdText ) 54 | 55 | // render TOC, add scrollspy to document.body 56 | md.toc( 'content', 'toc', { css:'h2,h3,h4', title:'Table of Contents', scrollspy:'body' } ) 57 | 58 | // render TOC, title=Index, add scrollspy to
59 | md.toc( 'content', 'toc', { title:'Index', scrollspy:'content' } ) 60 | ~~~ 61 | 62 | 63 | ### Advanced Usage 64 | 65 | #### TOC from special elements 66 | 67 | TOC may retrieve heading from special TAG elements. ``e.g. tag
``. 68 | 69 | md.toc() retrieve content from selected tags, using tag name as className. So remember provide additional #toc class to show it nice in TOC dialog. 70 | 71 | ~~~ 72 | // render TOC from tag:section, and H3, H4 73 | md.toc( 'content', 'toc', { css: 'section,h3,h4' } ) 74 | 75 | // remember provide style class for TOC tag, normally set position for left margin 76 | var style = document.createElement('style'); 77 | style.innerHTML = '#toc .SECTION { margin-left:2em } 78 | document.head.appendChild(style); 79 | ~~~ 80 | 81 | #### Activate scrollspy feature 82 | 83 | To activate the scrollspy feature, just pass element-ID of scroll content as below. 84 | 85 | ~~~ 86 | // activate scrollspy. normally is same as md-content, but sometime is document body. 87 | md.toc( 'content', 'toc', { css: 'h2,h3,h4', scrollspy:'conent' } ) 88 | 89 | // sometimes scroll content is document body. 90 | md.toc( 'content', 'toc', { css: 'h3,h4,h5', scrollspy:'body' } ) 91 | ~~~ 92 | 93 | #### Code-block formatter 94 | 95 | Code-block keywords will be highlighted, they are hard-code in function md.formatCode(). 96 | If do want to highlight different keywords, please revise or override original function md.formatCode(). 97 | 98 | ~~~ 99 | //===== format code-block, highlight remarks/keywords for code/sql 100 | md.formatCode = function (match, title, block) { 101 | // convert tag <> to < > tab to 3 space, support marker using ^^^ 102 | block = block.replace(//g,'>') 103 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 104 | 105 | // highlight comment and keyword based on title := none | sql | code 106 | if (title.toLowerCase(title) == 'sql') { 107 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 108 | block = block.replace(/(\s?)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 109 | block = block.replace(/(\s?)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 110 | } else if ((title||'none')!=='none') { 111 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 112 | block = block.replace(/(\s?)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 113 | block = block.replace(/(\s?)(var|let|const|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 114 | } 115 | return '
'  + block + '
' 116 | } 117 | ~~~ 118 | 119 | #### Extend Parser for enhanced syntax 120 | 121 | to parse every markdown-block, dummy function md.before() and md.after() will be called 122 | (i.e. ``md.after( md.parser( md.before(mdText) ) )``). 123 | 124 | They can be re-defined for enhanced syntax. for example, 125 | 126 | ~~~ 127 | // original code in casual-markdown.js 128 | var md = { before: function (str) {return str}, after: function (str) {return str} } 129 | 130 | // re-define for extend syntax. e.g. $text$ => text 131 | md.before = function (str) { 132 | return mdstr = mdstr.replace(/\$(\w.*?[^\\])\$/gm, '$1') 133 | } 134 | 135 | // re-define for extend syntax. e.g. $$text$$ => text 136 | md.after = function (str) { 137 | return mdstr = mdstr.replace(/\$\$(\w.*?[^\\])\$\$/gm, '$1') 138 | } 139 | 140 | document.getElementById('content').innerHTML = md.html() 141 | ~~~ 142 | 143 | ### Frontmatter for simple YAML 144 | 145 | Support frontmatter for simple YAML, only support string value (with 2 level ) meanwhile. 146 | 147 | Frontmatter start with `---` (at least three) in first line of markdown document, for example 148 | 149 | ``` 150 | ----------------------------------------------------------------------------- 151 | title : Markdown-as-Page 152 | style : #header { background: RoyalBlue } 153 | menu : 154 | Home : index.md 155 | Supported Syntax: md-syntax.md 156 | md-as-Doc : md-as-doc.md 157 | md-as-Page : md-as-page.md 158 | md-as-Blog : md-as-blog.md 159 | [DarkMode] : javascript:darkmode() 160 | ----------------------------------------------------------------------------- 161 | 162 | ## {{ title }} 163 | 164 | .... 165 | ``` 166 | 167 | After called md.html(), js program may refer these values by `md.yaml[name]` (i.e. md.yaml = { title:'Markdown-as-Page', .... }) 168 | and html string with ``{{ name }}`` will be replaced with related values 169 | 170 | 171 | ### Applications 172 | 173 | * Markdown-as-Document: https://github.com/casualwriter/casual-markdown-doc 174 | * Markdown-as-WebPage: https://github.com/casualwriter/casual-markdown-page 175 | * Markdown-as-Blog: https://github.com/casualwriter/casual-markdown-blog (not ready yet!) 176 | 177 | 178 | ### Update History 179 | 180 | * 2022/07/19, v0.80, initial release. 181 | * 2022/07/21, v0.82, refine toc/scrollspy, add dummy function for extension 182 | * 2022/07/22, v0.85, frontmatter for simple YAML 183 | * 2022/07/31, v0.90, refine frontmatter. code casual-markdown-doc.js, casual-markdown-page.html 184 | * 2023/04/12, v0.92, add copy feature for code-block. minor fine-tune. 185 | 186 | -------------------------------------------------------------------------------- /dist/casual-markdown-doc.css: -------------------------------------------------------------------------------- 1 | /* ==== copy from casual-markdown.css ==== */ 2 | .markdown code { background:#f0f0f0; color:navy; border-radius:2px; padding:2px; } 3 | .markdown pre { background:#f0f0f0; margin:16px; border:1px solid #ddd; padding:8px; } 4 | .markdown blockquote { background:#f0f0f0; border-left:6px solid grey; padding:8px } 5 | .markdown table { margin:12px; border-collapse: collapse; } 6 | .markdown th { border:1px solid grey; background:lightgrey; padding:6px; } 7 | .markdown td { border:1px solid grey; padding:6px; word-wrap:break-word; } 8 | .markdown tr:nth-child(even) { background:#f6f6f6; } 9 | .markdown ins { color:#890604 } 10 | .markdown rem { color:#198964 } 11 | .toc ul { padding: 0 12px; } 12 | .toc h3 { color:#0057b7; border-bottom:1px dotted grey } 13 | .toc .H1 { list-style-type:none; font-weight:600; margin:4px; background:#eee } 14 | .toc .H2 { list-style-type:none; font-weight:600; margin:4px; } 15 | .toc .H3 { margin-left:2em } 16 | .toc .H4 { margin-left:4em } 17 | .toc .active { color:#0057b7 } 18 | .toc li:hover { background:#ffd700 } 19 | 20 | /* ==== style for casual-markdown-doc.css ==== */ 21 | body { font-family:Verdana,sans-serif; font-size:14px; line-height:1.5; margin:0; display:none } 22 | code, xmp { white-space : pre-wrap; font-size:90% } 23 | img { margin:auto; max-width:990px; padding: 2px 10px } 24 | h1, h2 { border-bottom: 1px solid grey; margin:20px 0px; color:#06c; padding:8px 1px; page-break-before:always; } 25 | h3, h4 { margin-top:24px 0px; padding:8px 1px; color:#66e; } 26 | a { text-decoration:none; } a:hover{ background:#ffd700 } 27 | 28 | header { font-size:32px; margin-bottom:24px; padding:16px; background:#0057b7; color:white } 29 | #tocbox { background: rgba(210,210,210,0.8); border:1px solid grey; } 30 | #tocbox { position:fixed; right:8px; top:8px; padding:4px; } 31 | 32 | @media print { 33 | header { margin-top:60px; color:black; border-bottom:2px solid grey;} 34 | #tocbox { position:relative; font-size:18px; margin:12px; line-height:160%; page-break-after: always; } 35 | #tocbox button { display:none } 36 | } 37 | -------------------------------------------------------------------------------- /dist/casual-markdown-doc.js: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * casual-markdown - a lightweight regexp-base markdown parser with TOC support 3 | * last updated on 2022/07/22, v0.85, code formatter, toc, scrollspy and front matter 4 | * 5 | * Copyright (c) 2022, Casualwriter (MIT Licensed) 6 | * https://github.com/casualwriter/casual-markdown 7 | *****************************************************************************/ 8 | ;(function(){ 9 | 10 | // define md object, and extent function (which is a dummy function for user extension) 11 | var md = { yaml:{}, before: function (str) {return str}, after: function (str) {return str} } 12 | 13 | // function for REGEXP to convert html tag. ie. => <TAG*gt; 14 | md.formatTag = function (html) { return html.replace(//g,'>'); } 15 | 16 | // front matter for simple YAML (support 1 level only) 17 | md.formatYAML = function (front, matter) { 18 | matter.replace( /^\s*([^:]+):(.*)$/gm, function(m,key,val) { md.yaml[key.trim()] = val.trim() } ); 19 | return '' 20 | } 21 | 22 | //===== format code-block, highlight remarks/keywords for code/sql 23 | md.formatCode = function (match, title, block) { 24 | // convert tag <> to < > tab to 3 space, support mark code using ^^^ 25 | block = block.replace(//g,'>') 26 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 27 | 28 | // highlight comment and keyword based on title := none | sql | code 29 | if (title.toLowerCase(title) == 'sql') { 30 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 31 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 32 | block = block.replace(/(\s)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 33 | } else if ((title||'none')!=='none') { 34 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 35 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 36 | block = block.replace(/(\s)(var|let|const|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 37 | } 38 | return '
'  + block + '
' 39 | } 40 | 41 | //===== parse markdown string into HTML string (exclude code-block) 42 | md.parser = function( mdstr ) { 43 | // apply yaml variables 44 | console.log( 'BEFORE==>', mdstr.substr(0,100) ) 45 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 46 | console.log( 'AFTER==>', mdstr.substr(0,100) ) 47 | // table syntax 48 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\|\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 49 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 50 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 51 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 52 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 53 | } ) 54 | 55 | // horizontal rule =>
56 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 57 | 58 | // header =>

..

59 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 60 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 61 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 62 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 63 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 64 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 65 | 66 | // inline code-block: `code-block` => code-block 67 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 68 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 69 | 70 | // blockquote, max 2 levels =>
{text}
71 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 72 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 73 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 74 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 75 | 76 | // image syntax: ![title](url) => title 77 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 78 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 79 | 80 | // links syntax: [title "title"](url) => text 81 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 82 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 83 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 84 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 85 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 86 | 87 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
88 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 89 | mdstr = mdstr.replace(/^\d[ .](.*)/gm, '
  1. $1
' ) 90 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 91 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 92 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 93 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 94 | 95 | // text decoration: bold, italic, underline, strikethrough, highlight 96 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 97 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 98 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 99 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 100 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 101 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 102 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 103 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 104 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 105 | 106 | // line break and paragraph =>

107 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 108 | 109 | // indent as code-block 110 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 111 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 112 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 113 | 114 | // Escaping Characters 115 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 116 | } 117 | 118 | //===== parse markdown string into HTML content (cater code-block) 119 | md.html = function (mdText) { 120 | // replace \r\n to \n, and handle front matter for simple YAML 121 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 122 | // handle code-block. 123 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 124 | 125 | // split by "", skip for code-block and process normal text 126 | var pos1=0, pos2=0, mdHTML = '' 127 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 128 | pos2 = mdText.indexOf('', pos1 ) 129 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 130 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 131 | mdText = mdText.substr( pos2 + 7 ) 132 | } 133 | 134 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 135 | } 136 | 137 | //===== TOC support 138 | md.toc = function (srcDiv, tocDiv, options ) { 139 | 140 | // select elements, set title 141 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 142 | var tocTitle = (options&&options.title) || 'Table of Contents' 143 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 144 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 145 | 146 | // loop for each element,add
  • element with class in TAG name. 147 | for (var i=0; i' 151 | html += toc[i].textContent + '
  • '; 152 | } 153 | 154 | document.getElementById(tocDiv).innerHTML = html + "
"; 155 | 156 | //===== scrollspy support (ps: add to document if element(scroll) not found) 157 | if ( options && options.scrollspy ) { 158 | 159 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 160 | 161 | // get TOC elements, and viewport position 162 | var list = document.getElementById(tocDiv).querySelectorAll('li') 163 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 164 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 165 | 166 | // loop for each TOC element, add/remove scrollspy class 167 | for (var i=0; i0 && pos tag into HTML document 193 | //============================================================================= 194 | window.onload = function () { 195 | 196 | var html = '
' 197 | html += '
' + (document.body.title||document.title) + '
' 198 | html += '\n
' 200 | html += '\n
' + md.html(document.body.innerHTML.replace(/\>/g,'>')) + '
'; 201 | 202 | // add shortcut for edit current page. 203 | html += 'TOC'; 204 | html += 'HTML'; 205 | 206 | document.body.innerHTML = html 207 | document.body.style.display = 'block'; 208 | md.toc( 'content', 'toc', { scrollspy:'body' } ) 209 | 210 | } 211 | 212 | // toggle TOC 213 | function tocToggle(show) { 214 | var disp = document.getElementById('tocbox').style.display 215 | document.getElementById('tocbox').style.display = show||(disp=='none')? 'block' : 'none' 216 | } 217 | 218 | // debug: show HTML 219 | function debug() { 220 | var html = document.getElementById('content').innerHTML 221 | if (html.substr(0,5)=='') { 222 | document.getElementById('content').innerHTML = html.substr(5, html.length-11) 223 | } else { 224 | document.getElementById('content').innerHTML = '<xmp>' + html.replace(/xmp\>/g,'|xmp>') + '' 225 | } 226 | } 227 | 228 | -------------------------------------------------------------------------------- /dist/casual-markdown-page.html: -------------------------------------------------------------------------------- 1 | 2 | md-as-Page 3 | 4 | 5 | 6 | 7 | 36 | 37 | 38 | 39 | 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 135 | -------------------------------------------------------------------------------- /dist/casual-markdown.css: -------------------------------------------------------------------------------- 1 | .markdown code { background:#f0f0f0; color:navy; border-radius:6px; padding:2px; } 2 | .markdown pre { background:#f0f0f0; margin:12px; border:1px solid #ddd; padding:20px 12px; border-radius:6px; } 3 | .markdown pre:hover button { display:block; } 4 | .markdown pre button { display:none; position:relative; float:right; top:-16px } 5 | .markdown blockquote { background:#f0f0f0; border-left:6px solid grey; padding:8px } 6 | .markdown table { margin:12px; border-collapse: collapse; } 7 | .markdown th { border:1px solid grey; background:lightgrey; padding:6px; } 8 | .markdown td { border:1px solid grey; padding:6px; } 9 | .markdown tr:nth-child(even) { background:#f0f0f0; } 10 | .markdown ins { color:#890604 } 11 | .markdown rem { color:#198964 } 12 | .toc ul { padding: 0 12px; } 13 | .toc h3 { color:#0057b7; border-bottom:1px dotted grey } 14 | .toc .H1 { list-style-type:none; font-weight:600; margin:4px; background:#eee } 15 | .toc .H2 { list-style-type:none; font-weight:600; margin:4px; } 16 | .toc .H3 { margin-left:2em } 17 | .toc .H4 { margin-left:4em } 18 | .toc .active { color:#0057b7 } 19 | .toc li:hover { background:#f0f0f0 } 20 | -------------------------------------------------------------------------------- /dist/casual-markdown.js: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * casual-markdown - a lightweight regexp-base markdown parser with TOC support 3 | * 2022/07/31, v0.90, refine frontmatter (simple yaml) 4 | * 2023/04/12, v0.92, addCopyButton for code-block 5 | * 6 | * Copyright (c) 2022-2023, Casualwriter (MIT Licensed) 7 | * https://github.com/casualwriter/casual-markdown 8 | *****************************************************************************/ 9 | ;(function(){ 10 | 11 | // define md object, and extent function (which is a dummy function) 12 | var md = { yaml:{}, before: function (str) {return str}, after: function (str) {return str} } 13 | 14 | // function for REGEXP to convert html tag. ie. => <TAG*gt; 15 | md.formatTag = function (html) { return html.replace(//g,'>'); } 16 | 17 | // frontmatter for simple YAML (support multi-level, but string value only) 18 | md.formatYAML = function (front, matter) { 19 | var level = {}, latest = md.yaml; 20 | matter.replace( /^\s*#(.*)$/gm, '' ).replace( /^( *)([^:^\n]+):(.*)$/gm, function(m, sp, key,val) { 21 | level[sp] = level[sp] || latest 22 | latest = level[sp][key.trim()] = val.trim() || {} 23 | for (e in level) if(e>sp) level[e]=null; 24 | } ); 25 | return '' 26 | } 27 | 28 | //===== format code-block, highlight remarks/keywords for code/sql 29 | md.formatCode = function (match, title, block) { 30 | // convert tag <> to < > tab to 3 space, support marker using ^^^ 31 | block = block.replace(//g,'>') 32 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 33 | 34 | // highlight comment and keyword based on title := none | sql | code 35 | if (title.toLowerCase(title) == 'sql') { 36 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 37 | block = block.replace(/(\s?)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 38 | block = block.replace(/(\s?)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 39 | } else if ((title||'none')!=='none') { 40 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 41 | block = block.replace(/(\s?)(function|procedure|return|exit|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 42 | block = block.replace(/(\s?)(var|let|const|=>|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 43 | } 44 | 45 | return '
'  + block + '
' 46 | } 47 | 48 | // copy to clipboard for code-block 49 | md.clipboard = function (e) { 50 | navigator.clipboard.writeText( e.parentNode.innerText.replace('copy\n','') ) 51 | e.innerText = 'copied' 52 | } 53 | 54 | //===== parse markdown string into HTML string (exclude code-block) 55 | md.parser = function( mdstr ) { 56 | 57 | // apply yaml variables 58 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 59 | 60 | // table syntax 61 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\s?\|\s?\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 62 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 63 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 64 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 65 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 66 | } ) 67 | 68 | // horizontal rule =>
69 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 70 | 71 | // header =>

..

72 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 73 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 74 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 75 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 76 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 77 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 78 | 79 | // inline code-block: `code-block` => code-block 80 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 81 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 82 | 83 | // blockquote, max 2 levels =>
{text}
84 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 85 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 86 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 87 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 88 | 89 | // image syntax: ![title](url) => title 90 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 91 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 92 | 93 | // links syntax: [title "title"](url) => text 94 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 95 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 96 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 97 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 98 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 99 | 100 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
101 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 102 | mdstr = mdstr.replace(/^\d\d?[ .](.*)/gm, '
  1. $1
' ) 103 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 104 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 105 | mdstr = mdstr.replace(/<\/[ou]l\>\n\n?<[ou]l\>/g, '\n' ) 106 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 107 | 108 | // text decoration: bold, italic, underline, strikethrough, highlight 109 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 110 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 111 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 112 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 113 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 114 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 115 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 116 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 117 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 118 | 119 | // line break and paragraph =>

120 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 121 | 122 | // indent as code-block 123 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 124 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 125 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 126 | 127 | // Escaping Characters 128 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 129 | } 130 | 131 | //===== parse markdown string into HTML content (cater code-block) 132 | md.html = function (mdText) { 133 | // replace \r\n to \n, and handle front matter for simple YAML 134 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 135 | // handle code-block. 136 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 137 | 138 | // split by "", skip for code-block and process normal text 139 | var pos1=0, pos2=0, mdHTML = '' 140 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 141 | pos2 = mdText.indexOf('', pos1 ) 142 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 143 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 144 | mdText = mdText.substr( pos2 + 7 ) 145 | } 146 | 147 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 148 | } 149 | 150 | //===== TOC support 151 | md.toc = function (srcDiv, tocDiv, options ) { 152 | 153 | // select elements, set title 154 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 155 | var tocTitle = (options&&options.title) || 'Table of Contents' 156 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 157 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 158 | 159 | // loop for each element,add
  • element with class in TAG name. 160 | for (var i=0; i' 164 | html += toc[i].textContent + '
  • '; 165 | } 166 | 167 | document.getElementById(tocDiv).innerHTML = html + "
"; 168 | 169 | //===== scrollspy support (ps: add to document.body if element(scrollspy) not found) 170 | if ( options && options.scrollspy ) { 171 | 172 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 173 | 174 | // get TOC elements, and viewport position 175 | var list = document.getElementById(tocDiv).querySelectorAll('li') 176 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 177 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 178 | 179 | // loop for each TOC element, add/remove scrollspy class 180 | for (var i=0; i0 && pos => <TAG*gt; 14 | md.formatTag = function (html) { return html.replace(//g,'>'); } 15 | 16 | //===== format code-block, highlight remarks/keywords for code/sql 17 | md.formatCode = function (match, title, block) { 18 | // convert tag <> to < > tab to 3 space, support mark code using ^^^ 19 | block = block.replace(//g,'>') 20 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 21 | 22 | // highlight comment and keyword based on title := none | sql | code 23 | if (title.toLowerCase(title) == 'sql') { 24 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 25 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 26 | block = block.replace(/(\s)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 27 | } else if ((title||'none')!=='none') { 28 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 29 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 30 | block = block.replace(/(\s)(var|let|const|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 31 | } 32 | return '
'  + block + '
' 33 | } 34 | 35 | //===== parse markdown string into HTML string (exclude code-block) 36 | md.parser = function( mdstr ) { 37 | // table syntax 38 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\|\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 39 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 40 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 41 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 42 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 43 | } ) 44 | 45 | // horizontal rule =>
46 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 47 | 48 | // header =>

..

49 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 50 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 51 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 52 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 53 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 54 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 55 | 56 | // inline code-block: `code-block` => code-block 57 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 58 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 59 | 60 | // blockquote, max 2 levels =>
{text}
61 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 62 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 63 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 64 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 65 | 66 | // image syntax: ![title](url) => title 67 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 68 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 69 | 70 | // links syntax: [title](url) => text 71 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 72 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 73 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 74 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 75 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 76 | 77 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
78 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 79 | mdstr = mdstr.replace(/^\d[ .](.*)/gm, '
  1. $1
' ) 80 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 81 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 82 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 83 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 84 | 85 | // text decoration: bold, italic, underline, strikethrough, highlight 86 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 87 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 88 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 89 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 90 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 91 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 92 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 93 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 94 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 95 | 96 | // line break and paragraph =>

97 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 98 | 99 | // indent as code-block 100 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 101 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 102 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 103 | 104 | // Escaping Characters 105 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 106 | } 107 | 108 | //===== parse markdown string into HTML content (cater code-block) 109 | md.html = function (mdText) { 110 | // first, handle syntax for code-block 111 | var pos1=0, pos2=0, mdHTML = '' 112 | mdText = mdText.replace(/\r\n/g, '\n').replace(/\n~~~/g,'\n```') 113 | mdText = mdText.replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 114 | 115 | // split by "", skip for code-block and process normal text 116 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 117 | pos2 = mdText.indexOf('', pos1 ) 118 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 119 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 120 | mdText = mdText.substr( pos2 + 7 ) 121 | } 122 | 123 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 124 | } 125 | 126 | //===== TOC support 127 | md.toc = function (srcDiv, tocDiv, options ) { 128 | 129 | // select elements, set title 130 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 131 | var tocTitle = (options&&options.title) || 'Table of Contents' 132 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 133 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 134 | 135 | // loop for each element,add
  • element with class in TAG name. 136 | for (var i=0; i' 140 | html += toc[i].textContent + '
  • '; 141 | } 142 | 143 | document.getElementById(tocDiv).innerHTML = html + "
"; 144 | 145 | //===== scrollspy support (ps: add to document if element(scroll) not found) 146 | if ( options && options.scrollspy ) { 147 | 148 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 149 | 150 | // get TOC elements, and viewport position 151 | var list = document.getElementById(tocDiv).querySelectorAll('li') 152 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 153 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 154 | 155 | // loop for each TOC element, add/remove scrollspy class 156 | for (var i=0; i0 && pos => <TAG*gt; 14 | md.formatTag = function (html) { return html.replace(//g,'>'); } 15 | 16 | // frontmatter for simple YAML (only support one level and string value) 17 | md.formatYAML = function (front, matter) { 18 | matter.replace( /^\s*([^:]+):(.*)$/gm, function(m,key,val) { md.yaml[key.trim()] = val.trim() } ); 19 | return '' 20 | } 21 | 22 | //===== format code-block, highlight remarks/keywords for code/sql 23 | md.formatCode = function (match, title, block) { 24 | // convert tag <> to < > tab to 3 space, support marker using ^^^ 25 | block = block.replace(//g,'>') 26 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 27 | 28 | // highlight comment and keyword based on title := none | sql | code 29 | if (title.toLowerCase(title) == 'sql') { 30 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 31 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 32 | block = block.replace(/(\s)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 33 | } else if ((title||'none')!=='none') { 34 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 35 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 36 | block = block.replace(/(\s)(var|let|const|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 37 | } 38 | return '
'  + block + '
' 39 | } 40 | 41 | //===== parse markdown string into HTML string (exclude code-block) 42 | md.parser = function( mdstr ) { 43 | 44 | // apply yaml variables 45 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 46 | 47 | // table syntax 48 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\|\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 49 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 50 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 51 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 52 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 53 | } ) 54 | 55 | // horizontal rule =>
56 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 57 | 58 | // header =>

..

59 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 60 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 61 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 62 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 63 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 64 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 65 | 66 | // inline code-block: `code-block` => code-block 67 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 68 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 69 | 70 | // blockquote, max 2 levels =>
{text}
71 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 72 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 73 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 74 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 75 | 76 | // image syntax: ![title](url) => title 77 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 78 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 79 | 80 | // links syntax: [title "title"](url) => text 81 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 82 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 83 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 84 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 85 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 86 | 87 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
88 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 89 | mdstr = mdstr.replace(/^\d[ .](.*)/gm, '
  1. $1
' ) 90 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 91 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 92 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 93 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 94 | 95 | // text decoration: bold, italic, underline, strikethrough, highlight 96 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 97 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 98 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 99 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 100 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 101 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 102 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 103 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 104 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 105 | 106 | // line break and paragraph =>

107 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 108 | 109 | // indent as code-block 110 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 111 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 112 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 113 | 114 | // Escaping Characters 115 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 116 | } 117 | 118 | //===== parse markdown string into HTML content (cater code-block) 119 | md.html = function (mdText) { 120 | // replace \r\n to \n, and handle front matter for simple YAML 121 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 122 | // handle code-block. 123 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 124 | 125 | // split by "", skip for code-block and process normal text 126 | var pos1=0, pos2=0, mdHTML = '' 127 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 128 | pos2 = mdText.indexOf('', pos1 ) 129 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 130 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 131 | mdText = mdText.substr( pos2 + 7 ) 132 | } 133 | 134 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 135 | } 136 | 137 | //===== TOC support 138 | md.toc = function (srcDiv, tocDiv, options ) { 139 | 140 | // select elements, set title 141 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 142 | var tocTitle = (options&&options.title) || 'Table of Contents' 143 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 144 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 145 | 146 | // loop for each element,add
  • element with class in TAG name. 147 | for (var i=0; i' 151 | html += toc[i].textContent + '
  • '; 152 | } 153 | 154 | document.getElementById(tocDiv).innerHTML = html + "
"; 155 | 156 | //===== scrollspy support (ps: add to document.body if element(scrollspy) not found) 157 | if ( options && options.scrollspy ) { 158 | 159 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 160 | 161 | // get TOC elements, and viewport position 162 | var list = document.getElementById(tocDiv).querySelectorAll('li') 163 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 164 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 165 | 166 | // loop for each TOC element, add/remove scrollspy class 167 | for (var i=0; i0 && pos => <TAG*gt; 14 | md.formatTag = function (html) { return html.replace(//g,'>'); } 15 | 16 | // frontmatter for simple YAML (support multi-level, but string value only) 17 | md.formatYAML = function (front, matter) { 18 | var level = {}, latest = md.yaml; 19 | matter.replace( /^\s*#(.*)$/gm, '' ).replace( /^( *)([^:^\n]+):(.*)$/gm, function(m, sp, key,val) { 20 | level[sp] = level[sp] || latest 21 | latest = level[sp][key.trim()] = val.trim() || {} 22 | for (e in level) if(e>sp) level[e]=null; 23 | } ); 24 | return '' 25 | } 26 | 27 | //===== format code-block, highlight remarks/keywords for code/sql 28 | md.formatCode = function (match, title, block) { 29 | // convert tag <> to < > tab to 3 space, support marker using ^^^ 30 | block = block.replace(//g,'>') 31 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 32 | 33 | // highlight comment and keyword based on title := none | sql | code 34 | if (title.toLowerCase(title) == 'sql') { 35 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 36 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 37 | block = block.replace(/(\s)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 38 | } else if ((title||'none')!=='none') { 39 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 40 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 41 | block = block.replace(/(\s)(var|let|const|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 42 | } 43 | return '
'  + block + '
' 44 | } 45 | 46 | //===== parse markdown string into HTML string (exclude code-block) 47 | md.parser = function( mdstr ) { 48 | 49 | // apply yaml variables 50 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 51 | 52 | // table syntax 53 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\|\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 54 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 55 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 56 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 57 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 58 | } ) 59 | 60 | // horizontal rule =>
61 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 62 | 63 | // header =>

..

64 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 65 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 66 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 67 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 68 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 69 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 70 | 71 | // inline code-block: `code-block` => code-block 72 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 73 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 74 | 75 | // blockquote, max 2 levels =>
{text}
76 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 77 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 78 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 79 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 80 | 81 | // image syntax: ![title](url) => title 82 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 83 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 84 | 85 | // links syntax: [title "title"](url) => text 86 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 87 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 88 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 89 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 90 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 91 | 92 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
93 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 94 | mdstr = mdstr.replace(/^\d[ .](.*)/gm, '
  1. $1
' ) 95 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 96 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 97 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 98 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 99 | 100 | // text decoration: bold, italic, underline, strikethrough, highlight 101 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 102 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 103 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 104 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 105 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 106 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 107 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 108 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 109 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 110 | 111 | // line break and paragraph =>

112 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 113 | 114 | // indent as code-block 115 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 116 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 117 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 118 | 119 | // Escaping Characters 120 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 121 | } 122 | 123 | //===== parse markdown string into HTML content (cater code-block) 124 | md.html = function (mdText) { 125 | // replace \r\n to \n, and handle front matter for simple YAML 126 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 127 | // handle code-block. 128 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 129 | 130 | // split by "", skip for code-block and process normal text 131 | var pos1=0, pos2=0, mdHTML = '' 132 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 133 | pos2 = mdText.indexOf('', pos1 ) 134 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 135 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 136 | mdText = mdText.substr( pos2 + 7 ) 137 | } 138 | 139 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 140 | } 141 | 142 | //===== TOC support 143 | md.toc = function (srcDiv, tocDiv, options ) { 144 | 145 | // select elements, set title 146 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 147 | var tocTitle = (options&&options.title) || 'Table of Contents' 148 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 149 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 150 | 151 | // loop for each element,add
  • element with class in TAG name. 152 | for (var i=0; i' 156 | html += toc[i].textContent + '
  • '; 157 | } 158 | 159 | document.getElementById(tocDiv).innerHTML = html + "
"; 160 | 161 | //===== scrollspy support (ps: add to document.body if element(scrollspy) not found) 162 | if ( options && options.scrollspy ) { 163 | 164 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 165 | 166 | // get TOC elements, and viewport position 167 | var list = document.getElementById(tocDiv).querySelectorAll('li') 168 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 169 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 170 | 171 | // loop for each TOC element, add/remove scrollspy class 172 | for (var i=0; i0 && pos => <TAG*gt; 15 | md.formatTag = function (html) { return html.replace(//g,'>'); } 16 | 17 | // frontmatter for simple YAML (support multi-level, but string value only) 18 | md.formatYAML = function (front, matter) { 19 | var level = {}, latest = md.yaml; 20 | matter.replace( /^\s*#(.*)$/gm, '' ).replace( /^( *)([^:^\n]+):(.*)$/gm, function(m, sp, key,val) { 21 | level[sp] = level[sp] || latest 22 | latest = level[sp][key.trim()] = val.trim() || {} 23 | for (e in level) if(e>sp) level[e]=null; 24 | } ); 25 | return '' 26 | } 27 | 28 | //===== format code-block, highlight remarks/keywords for code/sql 29 | md.formatCode = function (match, title, block) { 30 | // convert tag <> to < > tab to 3 space, support marker using ^^^ 31 | block = block.replace(//g,'>') 32 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 33 | 34 | // highlight comment and keyword based on title := none | sql | code 35 | if (title.toLowerCase(title) == 'sql') { 36 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 37 | block = block.replace(/(\s?)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 38 | block = block.replace(/(\s?)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 39 | } else if ((title||'none')!=='none') { 40 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 41 | block = block.replace(/(\s?)(function|procedure|return|exit|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 42 | block = block.replace(/(\s?)(var|let|const|=>|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 43 | } 44 | 45 | return '
'  + block + '
' 46 | } 47 | 48 | // copy to clipboard for code-block 49 | md.clipboard = function (e) { 50 | navigator.clipboard.writeText( e.parentNode.innerText.replace('copy\n','') ) 51 | e.innerText = 'copied' 52 | } 53 | 54 | //===== parse markdown string into HTML string (exclude code-block) 55 | md.parser = function( mdstr ) { 56 | 57 | // apply yaml variables 58 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 59 | 60 | // table syntax 61 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\s?\|\s?\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 62 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 63 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 64 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 65 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 66 | } ) 67 | 68 | // horizontal rule =>
69 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 70 | 71 | // header =>

..

72 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 73 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 74 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 75 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 76 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 77 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 78 | 79 | // inline code-block: `code-block` => code-block 80 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 81 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 82 | 83 | // blockquote, max 2 levels =>
{text}
84 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 85 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 86 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 87 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 88 | 89 | // image syntax: ![title](url) => title 90 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 91 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 92 | 93 | // links syntax: [title "title"](url) => text 94 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 95 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 96 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 97 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 98 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 99 | 100 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
101 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 102 | mdstr = mdstr.replace(/^\d\d?[ .](.*)/gm, '
  1. $1
' ) 103 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 104 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 105 | mdstr = mdstr.replace(/<\/[ou]l\>\n\n?<[ou]l\>/g, '\n' ) 106 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 107 | 108 | // text decoration: bold, italic, underline, strikethrough, highlight 109 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 110 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 111 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 112 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 113 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 114 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 115 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 116 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 117 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 118 | 119 | // line break and paragraph =>

120 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 121 | 122 | // indent as code-block 123 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 124 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 125 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 126 | 127 | // Escaping Characters 128 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 129 | } 130 | 131 | //===== parse markdown string into HTML content (cater code-block) 132 | md.html = function (mdText) { 133 | // replace \r\n to \n, and handle front matter for simple YAML 134 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 135 | // handle code-block. 136 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 137 | 138 | // split by "", skip for code-block and process normal text 139 | var pos1=0, pos2=0, mdHTML = '' 140 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 141 | pos2 = mdText.indexOf('', pos1 ) 142 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 143 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 144 | mdText = mdText.substr( pos2 + 7 ) 145 | } 146 | 147 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 148 | } 149 | 150 | //===== TOC support 151 | md.toc = function (srcDiv, tocDiv, options ) { 152 | 153 | // select elements, set title 154 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 155 | var tocTitle = (options&&options.title) || 'Table of Contents' 156 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 157 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 158 | 159 | // loop for each element,add
  • element with class in TAG name. 160 | for (var i=0; i' 164 | html += toc[i].textContent + '
  • '; 165 | } 166 | 167 | document.getElementById(tocDiv).innerHTML = html + "
"; 168 | 169 | //===== scrollspy support (ps: add to document.body if element(scrollspy) not found) 170 | if ( options && options.scrollspy ) { 171 | 172 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 173 | 174 | // get TOC elements, and viewport position 175 | var list = document.getElementById(tocDiv).querySelectorAll('li') 176 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 177 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 178 | 179 | // loop for each TOC element, add/remove scrollspy class 180 | for (var i=0; i0 && pos => <TAG*gt; 14 | md.formatTag = function (html) { return html.replace(//g,'>'); } 15 | 16 | // front matter for simple YAML (support 1 level only) 17 | md.formatYAML = function (front, matter) { 18 | matter.replace( /^\s*([^:]+):(.*)$/gm, function(m,key,val) { md.yaml[key.trim()] = val.trim() } ); 19 | return '' 20 | } 21 | 22 | //===== format code-block, highlight remarks/keywords for code/sql 23 | md.formatCode = function (match, title, block) { 24 | // convert tag <> to < > tab to 3 space, support mark code using ^^^ 25 | block = block.replace(//g,'>') 26 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 27 | 28 | // highlight comment and keyword based on title := none | sql | code 29 | if (title.toLowerCase(title) == 'sql') { 30 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 31 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 32 | block = block.replace(/(\s)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 33 | } else if ((title||'none')!=='none') { 34 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 35 | block = block.replace(/(\s)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 36 | block = block.replace(/(\s)(var|let|const|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 37 | } 38 | return '
'  + block + '
' 39 | } 40 | 41 | //===== parse markdown string into HTML string (exclude code-block) 42 | md.parser = function( mdstr ) { 43 | // apply yaml variables 44 | console.log( 'BEFORE==>', mdstr.substr(0,100) ) 45 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 46 | console.log( 'AFTER==>', mdstr.substr(0,100) ) 47 | // table syntax 48 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\|\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 49 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 50 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 51 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 52 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 53 | } ) 54 | 55 | // horizontal rule =>
56 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 57 | 58 | // header =>

..

59 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 60 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 61 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 62 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 63 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 64 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 65 | 66 | // inline code-block: `code-block` => code-block 67 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 68 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 69 | 70 | // blockquote, max 2 levels =>
{text}
71 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 72 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 73 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 74 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 75 | 76 | // image syntax: ![title](url) => title 77 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 78 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 79 | 80 | // links syntax: [title "title"](url) => text 81 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 82 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 83 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 84 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 85 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 86 | 87 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
88 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 89 | mdstr = mdstr.replace(/^\d[ .](.*)/gm, '
  1. $1
' ) 90 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 91 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 92 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 93 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 94 | 95 | // text decoration: bold, italic, underline, strikethrough, highlight 96 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 97 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 98 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 99 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 100 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 101 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 102 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 103 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 104 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 105 | 106 | // line break and paragraph =>

107 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 108 | 109 | // indent as code-block 110 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 111 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 112 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 113 | 114 | // Escaping Characters 115 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 116 | } 117 | 118 | //===== parse markdown string into HTML content (cater code-block) 119 | md.html = function (mdText) { 120 | // replace \r\n to \n, and handle front matter for simple YAML 121 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 122 | // handle code-block. 123 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 124 | 125 | // split by "", skip for code-block and process normal text 126 | var pos1=0, pos2=0, mdHTML = '' 127 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 128 | pos2 = mdText.indexOf('', pos1 ) 129 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 130 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 131 | mdText = mdText.substr( pos2 + 7 ) 132 | } 133 | 134 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 135 | } 136 | 137 | //===== TOC support 138 | md.toc = function (srcDiv, tocDiv, options ) { 139 | 140 | // select elements, set title 141 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 142 | var tocTitle = (options&&options.title) || 'Table of Contents' 143 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 144 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 145 | 146 | // loop for each element,add
  • element with class in TAG name. 147 | for (var i=0; i' 151 | html += toc[i].textContent + '
  • '; 152 | } 153 | 154 | document.getElementById(tocDiv).innerHTML = html + "
"; 155 | 156 | //===== scrollspy support (ps: add to document if element(scroll) not found) 157 | if ( options && options.scrollspy ) { 158 | 159 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 160 | 161 | // get TOC elements, and viewport position 162 | var list = document.getElementById(tocDiv).querySelectorAll('li') 163 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 164 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 165 | 166 | // loop for each TOC element, add/remove scrollspy class 167 | for (var i=0; i0 && pos tag into HTML document 193 | //============================================================================= 194 | window.onload = function () { 195 | 196 | var html = '
' 197 | html += '
' + (document.body.title||document.title) + '
' 198 | html += '\n
' 200 | html += '\n
' + md.html(document.body.innerHTML.replace(/\>/g,'>')) + '
'; 201 | 202 | // add shortcut for edit current page. 203 | html += 'TOC'; 204 | html += 'HTML'; 205 | 206 | document.body.innerHTML = html 207 | document.body.style.display = 'block'; 208 | md.toc( 'content', 'toc', { scrollspy:'body' } ) 209 | 210 | } 211 | 212 | // toggle TOC 213 | function tocToggle(show) { 214 | var disp = document.getElementById('tocbox').style.display 215 | document.getElementById('tocbox').style.display = show||(disp=='none')? 'block' : 'none' 216 | } 217 | 218 | // debug: show HTML 219 | function debug() { 220 | var html = document.getElementById('content').innerHTML 221 | if (html.substr(0,5)=='') { 222 | document.getElementById('content').innerHTML = html.substr(5, html.length-11) 223 | } else { 224 | document.getElementById('content').innerHTML = '<xmp>' + html.replace(/xmp\>/g,'|xmp>') + '' 225 | } 226 | } 227 | 228 | -------------------------------------------------------------------------------- /source/casual-markdown-page.html: -------------------------------------------------------------------------------- 1 | 2 | All-in-One 3 | 4 | 50 | 235 | 236 | 237 | 238 | 242 | 243 |
244 |
245 |
246 |
247 | 248 | 249 | 250 | 251 | 252 | 317 | -------------------------------------------------------------------------------- /source/casual-markdown.css: -------------------------------------------------------------------------------- 1 | .markdown code { background:#f0f0f0; color:navy; border-radius:6px; padding:2px; } 2 | .markdown pre { background:#f0f0f0; margin:12px; border:1px solid #ddd; padding:20px 12px; border-radius:6px; } 3 | .markdown pre:hover button { display:block; } 4 | .markdown pre button { display:none; position:relative; float:right; top:-16px } 5 | .markdown blockquote { background:#f0f0f0; border-left:6px solid grey; padding:8px } 6 | .markdown table { margin:12px; border-collapse: collapse; } 7 | .markdown th { border:1px solid grey; background:lightgrey; padding:6px; } 8 | .markdown td { border:1px solid grey; padding:6px; } 9 | .markdown tr:nth-child(even) { background:#f0f0f0; } 10 | .markdown ins { color:#890604 } 11 | .markdown rem { color:#198964 } 12 | .toc ul { padding: 0 12px; } 13 | .toc h3 { color:#0057b7; border-bottom:1px dotted grey } 14 | .toc .H1 { list-style-type:none; font-weight:600; margin:4px; background:#eee } 15 | .toc .H2 { list-style-type:none; font-weight:600; margin:4px; } 16 | .toc .H3 { margin-left:2em } 17 | .toc .H4 { margin-left:4em } 18 | .toc .active { color:#0057b7 } 19 | .toc li:hover { background:#f0f0f0 } 20 | -------------------------------------------------------------------------------- /source/casual-markdown.js: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * casual-markdown - a lightweight regexp-base markdown parser with TOC support 3 | * 2022/07/31, v0.90, refine frontmatter (simple yaml) 4 | * 2023/04/12, v0.92, addCopyButton for code-block 5 | * 6 | * Copyright (c) 2022-2023, Casualwriter (MIT Licensed) 7 | * https://github.com/casualwriter/casual-markdown 8 | *****************************************************************************/ 9 | ;(function(){ 10 | 11 | // define md object, and extent function (which is a dummy function) 12 | var md = { yaml:{}, before: function (str) {return str}, after: function (str) {return str} } 13 | 14 | // function for REGEXP to convert html tag. ie. => <TAG*gt; 15 | md.formatTag = function (html) { return html.replace(//g,'>'); } 16 | 17 | // frontmatter for simple YAML (support multi-level, but string value only) 18 | md.formatYAML = function (front, matter) { 19 | var level = {}, latest = md.yaml; 20 | matter.replace( /^\s*#(.*)$/gm, '' ).replace( /^( *)([^:^\n]+):(.*)$/gm, function(m, sp, key,val) { 21 | level[sp] = level[sp] || latest 22 | latest = level[sp][key.trim()] = val.trim() || {} 23 | for (e in level) if(e>sp) level[e]=null; 24 | } ); 25 | return '' 26 | } 27 | 28 | //===== format code-block, highlight remarks/keywords for code/sql 29 | md.formatCode = function (match, title, block) { 30 | // convert tag <> to < > tab to 3 space, support marker using ^^^ 31 | block = block.replace(//g,'>') 32 | block = block.replace(/\t/g,' ').replace(/\^\^\^(.+?)\^\^\^/g, '$1') 33 | 34 | // highlight comment and keyword based on title := none | sql | code 35 | if (title.toLowerCase(title) == 'sql') { 36 | block = block.replace(/^\-\-(.*)/gm,'--$1').replace(/\s\-\-(.*)/gm,' --$1') 37 | block = block.replace(/(\s?)(function|procedure|return|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 38 | block = block.replace(/(\s?)(select|update|delete|insert|create|from|where|group by|having|set)(\s)/gim,'$1$2$3') 39 | } else if ((title||'none')!=='none') { 40 | block = block.replace(/^\/\/(.*)/gm,'//$1').replace(/\s\/\/(.*)/gm,' //$1') 41 | block = block.replace(/(\s?)(function|procedure|return|exit|if|then|else|end|loop|while|or|and|case|when)(\s)/gim,'$1$2$3') 42 | block = block.replace(/(\s?)(var|let|const|=>|for|next|do|while|loop|continue|break|switch|try|catch|finally)(\s)/gim,'$1$2$3') 43 | } 44 | 45 | return '
'  + block + '
' 46 | } 47 | 48 | // copy to clipboard for code-block 49 | md.clipboard = function (e) { 50 | navigator.clipboard.writeText( e.parentNode.innerText.replace('copy\n','') ) 51 | e.innerText = 'copied' 52 | } 53 | 54 | //===== parse markdown string into HTML string (exclude code-block) 55 | md.parser = function( mdstr ) { 56 | 57 | // apply yaml variables 58 | for (var name in this.yaml) mdstr = mdstr.replace( new RegExp('\{\{\\s*'+name+'\\s*\}\}', 'gm'), this.yaml[name] ) 59 | 60 | // table syntax 61 | mdstr = mdstr.replace(/\n(.+?)\n.*?\-\-\s?\|\s?\-\-.*?\n([\s\S]*?)\n\s*?\n/g, function (m,p1,p2) { 62 | var thead = p1.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1').replace(/\|/g,'') 63 | var tbody = p2.replace(/^\|(.+)/gm,'$1').replace(/(.+)\|$/gm,'$1') 64 | tbody = tbody.replace(/(.+)/gm,'$1').replace(/\|/g,'') 65 | return '\n\n\n\n' + tbody + '\n
' + thead + '\n
\n\n' 66 | } ) 67 | 68 | // horizontal rule =>
69 | mdstr = mdstr.replace(/^-{3,}|^\_{3,}|^\*{3,}$/gm, '
').replace(/\n\n/g, '\n

') 70 | 71 | // header =>

..

72 | mdstr = mdstr.replace(/^##### (.*?)\s*#*$/gm, '
$1
') 73 | .replace(/^#### (.*?)\s*#*$/gm, '

$1

') 74 | .replace(/^### (.*?)\s*#*$/gm, '

$1

') 75 | .replace(/^## (.*?)\s*#*$/gm, '

$1

') 76 | .replace(/^# (.*?)\s*#*$/gm, '

$1

') 77 | .replace(/^(.*?)\s*{(.*)}\s*<\/h\d\>$/gm, '$2') 78 | 79 | // inline code-block: `code-block` => code-block 80 | mdstr = mdstr.replace(/``(.*?)``/gm, function(m,p){ return '' + md.formatTag(p).replace(/`/g,'`') + ''} ) 81 | mdstr = mdstr.replace(/`(.*?)`/gm, '$1' ) 82 | 83 | // blockquote, max 2 levels =>
{text}
84 | mdstr = mdstr.replace(/^\>\> (.*$)/gm, '
$1
') 85 | mdstr = mdstr.replace(/^\> (.*$)/gm, '
$1
') 86 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 87 | mdstr = mdstr.replace(/<\/blockquote\>\n/g, '\n
' ) 88 | 89 | // image syntax: ![title](url) => title 90 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 91 | mdstr = mdstr.replace(/!\[(.*?)\]\((.*?)\)/gm, '$1') 92 | 93 | // links syntax: [title "title"](url) => text 94 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "new"\)/gm, '$1') 95 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?) "(.*?)"\)/gm, '$1') 96 | mdstr = mdstr.replace(/([<\s])(https?\:\/\/.*?)([\s\>])/gm, '$1$2$3') 97 | mdstr = mdstr.replace(/\[(.*?)\]\(\)/gm, '$1') 98 | mdstr = mdstr.replace(/\[(.*?)\]\((.*?)\)/gm, '$1') 99 | 100 | // unordered/ordered list, max 2 levels =>
  • ..
,
  1. ..
101 | mdstr = mdstr.replace(/^[\*+-][ .](.*)/gm, '
  • $1
' ) 102 | mdstr = mdstr.replace(/^\d\d?[ .](.*)/gm, '
  1. $1
' ) 103 | mdstr = mdstr.replace(/^\s{2,6}[\*+-][ .](.*)/gm, '
    • $1
' ) 104 | mdstr = mdstr.replace(/^\s{2,6}\d[ .](.*)/gm, '
    1. $1
' ) 105 | mdstr = mdstr.replace(/<\/[ou]l\>\n\n?<[ou]l\>/g, '\n' ) 106 | mdstr = mdstr.replace(/<\/[ou]l\>\n<[ou]l\>/g, '\n' ) 107 | 108 | // text decoration: bold, italic, underline, strikethrough, highlight 109 | mdstr = mdstr.replace(/\*\*\*(\w.*?[^\\])\*\*\*/gm, '$1') 110 | mdstr = mdstr.replace(/\*\*(\w.*?[^\\])\*\*/gm, '$1') 111 | mdstr = mdstr.replace(/\*(\w.*?[^\\])\*/gm, '$1') 112 | mdstr = mdstr.replace(/___(\w.*?[^\\])___/gm, '$1') 113 | mdstr = mdstr.replace(/__(\w.*?[^\\])__/gm, '$1') 114 | // mdstr = mdstr.replace(/_(\w.*?[^\\])_/gm, '$1') // NOT support!! 115 | mdstr = mdstr.replace(/\^\^\^(.+?)\^\^\^/gm, '$1') 116 | mdstr = mdstr.replace(/\^\^(\w.*?)\^\^/gm, '$1') 117 | mdstr = mdstr.replace(/~~(\w.*?)~~/gm, '$1') 118 | 119 | // line break and paragraph =>

120 | mdstr = mdstr.replace(/ \n/g, '\n
').replace(/\n\s*\n/g, '\n

\n') 121 | 122 | // indent as code-block 123 | mdstr = mdstr.replace(/^ {4,10}(.*)/gm, function(m,p) { return '

' + md.formatTag(p) + '
'} ) 124 | mdstr = mdstr.replace(/^\t(.*)/gm, function(m,p) { return '
' + md.formatTag(p) + '
'} ) 125 | mdstr = mdstr.replace(/<\/code\><\/pre\>\n/g, '\n' ) 126 | 127 | // Escaping Characters 128 | return mdstr.replace(/\\([`_~\*\+\-\.\^\\\<\>\(\)\[\]])/gm, '$1' ) 129 | } 130 | 131 | //===== parse markdown string into HTML content (cater code-block) 132 | md.html = function (mdText) { 133 | // replace \r\n to \n, and handle front matter for simple YAML 134 | mdText = mdText.replace(/\r\n/g, '\n').replace( /^---+\s*\n([\s\S]*?)\n---+\s*\n/, md.formatYAML ) 135 | // handle code-block. 136 | mdText = mdText.replace(/\n~~~/g,'\n```').replace(/\n``` *(.*?)\n([\s\S]*?)\n``` *\n/g, md.formatCode) 137 | 138 | // split by "", skip for code-block and process normal text 139 | var pos1=0, pos2=0, mdHTML = '' 140 | while ( (pos1 = mdText.indexOf('')) >= 0 ) { 141 | pos2 = mdText.indexOf('', pos1 ) 142 | mdHTML += md.after( md.parser( md.before( mdText.substr(0,pos1) ) ) ) 143 | mdHTML += mdText.substr(pos1, (pos2>0? pos2-pos1+7 : mdtext.length) ) 144 | mdText = mdText.substr( pos2 + 7 ) 145 | } 146 | 147 | return '
' + mdHTML + md.after( md.parser( md.before(mdText) ) ) + '
' 148 | } 149 | 150 | //===== TOC support 151 | md.toc = function (srcDiv, tocDiv, options ) { 152 | 153 | // select elements, set title 154 | var tocSelector = (options&&options.css) || 'h1,h2,h3,h4' 155 | var tocTitle = (options&&options.title) || 'Table of Contents' 156 | var toc = document.getElementById(srcDiv).querySelectorAll( tocSelector ) 157 | var html = '
    ' + (tocTitle=='none'? '' : '

    ' + tocTitle + '

    '); 158 | 159 | // loop for each element,add
  • element with class in TAG name. 160 | for (var i=0; i' 164 | html += toc[i].textContent + '
  • '; 165 | } 166 | 167 | document.getElementById(tocDiv).innerHTML = html + "
"; 168 | 169 | //===== scrollspy support (ps: add to document.body if element(scrollspy) not found) 170 | if ( options && options.scrollspy ) { 171 | 172 | (document.getElementById(options.scrollspy)||document).onscroll = function () { 173 | 174 | // get TOC elements, and viewport position 175 | var list = document.getElementById(tocDiv).querySelectorAll('li') 176 | var divScroll = document.getElementById(options.scrollspy) || document.documentElement 177 | var divHeight = divScroll.clientHeight || divScroll.offsetHeight 178 | 179 | // loop for each TOC element, add/remove scrollspy class 180 | for (var i=0; i0 && pos