├── README.md ├── components ├── marked │ ├── index.js │ └── lib │ │ └── marked.js ├── mpvue-wxparse │ └── src │ │ ├── components │ │ ├── wxParseVideo.vue │ │ ├── wxParseAudio.vue │ │ ├── wxParseTemplate11.vue │ │ ├── wxParseImg.vue │ │ ├── wxParseTemplate1.vue │ │ ├── wxParseTemplate10.vue │ │ ├── wxParseTemplate2.vue │ │ ├── wxParseTemplate3.vue │ │ ├── wxParseTemplate4.vue │ │ ├── wxParseTemplate5.vue │ │ ├── wxParseTemplate6.vue │ │ ├── wxParseTemplate7.vue │ │ ├── wxParseTemplate8.vue │ │ ├── wxParseTemplate9.vue │ │ └── wxParseTemplate0.vue │ │ ├── wxParse.vue │ │ ├── wxParse.css │ │ └── libs │ │ ├── htmlparser.js │ │ ├── wxDiscode.js │ │ └── html2json.js └── ly-markdown.vue ├── main.js ├── pages.json ├── App.vue ├── pages └── index │ └── index.vue ├── manifest.json └── static └── markdown.css /README.md: -------------------------------------------------------------------------------- 1 | # uniapp-markdown 2 | uniapp的markdown编辑器插件 3 | -------------------------------------------------------------------------------- /components/marked/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/marked'); 2 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | 4 | Vue.config.productionTip = false 5 | 6 | App.mpType = 'app' 7 | 8 | const app = new Vue({ 9 | ...App 10 | }) 11 | app.$mount() 12 | -------------------------------------------------------------------------------- /pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ //pages数组中第一项表示应用启动页 3 | { 4 | "path": "pages/index/index", 5 | "style": { 6 | "navigationBarTitleText": "uni-app" 7 | } 8 | } 9 | ], 10 | "globalStyle": { 11 | "navigationBarTextStyle": "black", 12 | "navigationBarTitleText": "uni-app", 13 | "navigationBarBackgroundColor": "#F8F8F8", 14 | "backgroundColor": "#F8F8F8" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseVideo.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 26 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseAudio.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 74 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate11.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 80 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseImg.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 79 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate1.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate10.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 90 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate2.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate3.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate4.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate5.vue: -------------------------------------------------------------------------------- 1 | s 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate6.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate7.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate8.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate9.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 91 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "uni-markdown", 3 | "appid" : "__UNI__4AC9661", 4 | "description": "", 5 | "versionName": "1.0.0", 6 | "versionCode": "100", 7 | "app-plus": { /* 5+App特有相关 */ 8 | "modules": { /* 模块配置 */ 9 | 10 | }, 11 | "distribute": { /* 应用发布信息 */ 12 | "android": { /* android打包配置 */ 13 | "permissions": ["", 14 | "", 15 | "", 16 | "", 17 | "", 18 | "", 19 | "", 20 | "", 21 | "", 22 | "", 23 | "", 24 | "", 25 | "", 26 | "", 27 | "", 28 | "", 29 | "", 30 | "", 31 | "", 32 | "", 33 | "", 34 | "" 35 | ] 36 | }, 37 | "ios": { /* ios打包配置 */ 38 | 39 | }, 40 | "sdkConfigs": { /* SDK配置 */ 41 | 42 | } 43 | } 44 | }, 45 | "quickapp": { /* 快应用特有相关 */ 46 | 47 | }, 48 | "mp-weixin": { /* 小程序特有相关 */ 49 | "appid": "" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/wxParse.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 118 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/components/wxParseTemplate0.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 100 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/wxParse.css: -------------------------------------------------------------------------------- 1 | /** 2 | * author: Di (微信小程序开发工程师) 3 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 4 | * 垂直微信小程序开发交流社区 5 | * 6 | * github地址: https://github.com/icindy/wxParse 7 | * 8 | * for: 微信小程序富文本解析 9 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 10 | */ 11 | 12 | .wxParse { 13 | width: 100%; 14 | font-family: Helvetica, sans-serif; 15 | font-size: 30px; 16 | color: #666; 17 | line-height: 1.8; 18 | } 19 | 20 | .wxParse view { 21 | word-break: hyphenate; 22 | } 23 | 24 | .wxParse .inline { 25 | display: inline; 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | .wxParse .div { 31 | margin: 0; 32 | padding: 0; 33 | } 34 | 35 | .wxParse .h1 { 36 | font-size: 2em; 37 | margin: 0.67em 0; 38 | } 39 | .wxParse .h2 { 40 | font-size: 1.5em; 41 | margin: 0.83em 0; 42 | } 43 | .wxParse .h3 { 44 | font-size: 1.17em; 45 | margin: 1em 0; 46 | } 47 | .wxParse .h4 { 48 | margin: 1.33em 0; 49 | } 50 | .wxParse .h5 { 51 | font-size: 0.83em; 52 | margin: 1.67em 0; 53 | } 54 | .wxParse .h6 { 55 | font-size: 0.67em; 56 | margin: 2.33em 0; 57 | } 58 | 59 | .wxParse .h1, 60 | .wxParse .h2, 61 | .wxParse .h3, 62 | .wxParse .h4, 63 | .wxParse .h5, 64 | .wxParse .h6, 65 | .wxParse .b, 66 | .wxParse .strong { 67 | font-weight: bolder; 68 | } 69 | 70 | .wxParse .p { 71 | margin: 1em 0; 72 | } 73 | 74 | .wxParse .i, 75 | .wxParse .cite, 76 | .wxParse .em, 77 | .wxParse .var, 78 | .wxParse .address { 79 | font-style: italic; 80 | } 81 | 82 | .wxParse .pre, 83 | .wxParse .tt, 84 | .wxParse .code, 85 | .wxParse .kbd, 86 | .wxParse .samp { 87 | font-family: monospace; 88 | } 89 | .wxParse .pre { 90 | overflow: auto; 91 | background: #f5f5f5; 92 | padding: 16px; 93 | white-space: pre; 94 | margin: 1em 0px; 95 | } 96 | .wxParse .code { 97 | display: inline; 98 | background: #f5f5f5; 99 | } 100 | 101 | .wxParse .big { 102 | font-size: 1.17em; 103 | } 104 | 105 | .wxParse .small, 106 | .wxParse .sub, 107 | .wxParse .sup { 108 | font-size: 0.83em; 109 | } 110 | 111 | .wxParse .sub { 112 | vertical-align: sub; 113 | } 114 | .wxParse .sup { 115 | vertical-align: super; 116 | } 117 | 118 | .wxParse .s, 119 | .wxParse .strike, 120 | .wxParse .del { 121 | text-decoration: line-through; 122 | } 123 | 124 | .wxParse .strong, 125 | .wxParse .s { 126 | display: inline; 127 | } 128 | 129 | .wxParse .a { 130 | color: deepskyblue; 131 | } 132 | 133 | .wxParse .video { 134 | text-align: center; 135 | margin: 22px 0; 136 | } 137 | 138 | .wxParse .video-video { 139 | width: 100%; 140 | } 141 | 142 | .wxParse .img { 143 | display: inline-block; 144 | width: 0; 145 | height: 0; 146 | max-width: 100%; 147 | overflow: hidden; 148 | } 149 | 150 | .wxParse .blockquote { 151 | margin: 10px 0; 152 | padding: 22px 0 22px 22px; 153 | font-family: Courier, Calibri, "宋体"; 154 | background: #f5f5f5; 155 | border-left: 6px solid #dbdbdb; 156 | } 157 | .wxParse .blockquote .p { 158 | margin: 0; 159 | } 160 | 161 | .wxParse .ul, .wxParse .ol { 162 | display: block; 163 | margin: 1em 0; 164 | padding-left: 33px; 165 | } 166 | .wxParse .ol { 167 | list-style-type: disc; 168 | } 169 | .wxParse .ol { 170 | list-style-type: decimal; 171 | } 172 | .wxParse .li { 173 | display: list-item; 174 | align-items: baseline; 175 | text-align: match-parent; 176 | } 177 | .wxParse .ul .ul, .wxParse .ol .ul { 178 | list-style-type: circle; 179 | } 180 | .wxParse .ol .ol .ul, .wxParse .ol .ul .ul, .wxParse .ul .ol .ul, .wxParse .ul .ul .ul { 181 | list-style-type: square; 182 | } 183 | 184 | .wxParse .u { 185 | text-decoration: underline; 186 | } 187 | .wxParse .hide { 188 | display: none; 189 | } 190 | .wxParse .del { 191 | display: inline; 192 | } 193 | .wxParse .figure { 194 | overflow: hidden; 195 | } 196 | 197 | .wxParse .table { 198 | width: 100%; 199 | } 200 | .wxParse .thead, .wxParse .tfoot, .wxParse .tr { 201 | display: flex; 202 | flex-direction: row; 203 | } 204 | .wxParse .tr { 205 | width:100%; 206 | display: flex; 207 | border-right: 2px solid #e0e0e0; 208 | border-bottom: 2px solid #e0e0e0; 209 | } 210 | .wxParse .th, 211 | .wxParse .td { 212 | display: flex; 213 | width: 1276px; 214 | overflow: auto; 215 | flex: 1; 216 | padding: 11px; 217 | border-left: 2px solid #e0e0e0; 218 | } 219 | .wxParse .td:last { 220 | border-top: 2px solid #e0e0e0; 221 | } 222 | .wxParse .th { 223 | background: #f0f0f0; 224 | border-top: 2px solid #e0e0e0; 225 | } 226 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/libs/htmlparser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser 4 | * 5 | * author: Di (微信小程序开发工程师) 6 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 7 | * 垂直微信小程序开发交流社区 8 | * 9 | * github地址: https://github.com/icindy/wxParse 10 | * 11 | * for: 微信小程序富文本解析 12 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 13 | */ 14 | // Regular Expressions for parsing tags and attributes 15 | 16 | const startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z0-9_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; 17 | const endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/; 18 | const attr = /([a-zA-Z0-9_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; 19 | 20 | function makeMap(str) { 21 | const obj = {}; 22 | const items = str.split(','); 23 | for (let i = 0; i < items.length; i += 1) obj[items[i]] = true; 24 | return obj; 25 | } 26 | 27 | // Empty Elements - HTML 5 28 | const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); 29 | 30 | // Block Elements - HTML 5 31 | const block = makeMap('address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); 32 | 33 | // Inline Elements - HTML 5 34 | const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); 35 | 36 | // Elements that you can, intentionally, leave open 37 | // (and which close themselves) 38 | const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); 39 | 40 | // Attributes that have their values filled in disabled="disabled" 41 | const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); 42 | 43 | function HTMLParser(html, handler) { 44 | let index; 45 | let chars; 46 | let match; 47 | let last = html; 48 | const stack = []; 49 | 50 | stack.last = () => stack[stack.length - 1]; 51 | 52 | function parseEndTag(tag, tagName) { 53 | // If no tag name is provided, clean shop 54 | let pos; 55 | if (!tagName) { 56 | pos = 0; 57 | } else { 58 | // Find the closest opened tag of the same type 59 | tagName = tagName.toLowerCase(); 60 | for (pos = stack.length - 1; pos >= 0; pos -= 1) { 61 | if (stack[pos] === tagName) break; 62 | } 63 | } 64 | if (pos >= 0) { 65 | // Close all the open elements, up the stack 66 | for (let i = stack.length - 1; i >= pos; i -= 1) { 67 | if (handler.end) handler.end(stack[i]); 68 | } 69 | 70 | // Remove the open elements from the stack 71 | stack.length = pos; 72 | } 73 | } 74 | 75 | function parseStartTag(tag, tagName, rest, unary) { 76 | tagName = tagName.toLowerCase(); 77 | 78 | if (block[tagName]) { 79 | while (stack.last() && inline[stack.last()]) { 80 | parseEndTag('', stack.last()); 81 | } 82 | } 83 | 84 | if (closeSelf[tagName] && stack.last() === tagName) { 85 | parseEndTag('', tagName); 86 | } 87 | 88 | unary = empty[tagName] || !!unary; 89 | 90 | if (!unary) stack.push(tagName); 91 | 92 | if (handler.start) { 93 | const attrs = []; 94 | 95 | rest.replace(attr, function genAttr(matches, name) { 96 | const value = arguments[2] || arguments[3] || arguments[4] || (fillAttrs[name] ? name : ''); 97 | 98 | attrs.push({ 99 | name, 100 | value, 101 | escaped: value.replace(/(^|[^\\])"/g, '$1\\"'), // " 102 | }); 103 | }); 104 | 105 | if (handler.start) { 106 | handler.start(tagName, attrs, unary); 107 | } 108 | } 109 | } 110 | 111 | while (html) { 112 | chars = true; 113 | 114 | if (html.indexOf(''); 111 | str = str.replace(/•/g, '•'); 112 | 113 | return str; 114 | } 115 | 116 | // HTML 支持的其他实体 117 | function strOtherDiscode(str) { 118 | str = str.replace(/Œ/g, 'Œ'); 119 | str = str.replace(/œ/g, 'œ'); 120 | str = str.replace(/Š/g, 'Š'); 121 | str = str.replace(/š/g, 'š'); 122 | str = str.replace(/Ÿ/g, 'Ÿ'); 123 | str = str.replace(/ƒ/g, 'ƒ'); 124 | str = str.replace(/ˆ/g, 'ˆ'); 125 | str = str.replace(/˜/g, '˜'); 126 | str = str.replace(/ /g, ''); 127 | str = str.replace(/ /g, ''); 128 | str = str.replace(/ /g, ''); 129 | str = str.replace(/‌/g, ''); 130 | str = str.replace(/‍/g, ''); 131 | str = str.replace(/‎/g, ''); 132 | str = str.replace(/‏/g, ''); 133 | str = str.replace(/–/g, '–'); 134 | str = str.replace(/—/g, '—'); 135 | str = str.replace(/‘/g, '‘'); 136 | str = str.replace(/’/g, '’'); 137 | str = str.replace(/‚/g, '‚'); 138 | str = str.replace(/“/g, '“'); 139 | str = str.replace(/”/g, '”'); 140 | str = str.replace(/„/g, '„'); 141 | str = str.replace(/†/g, '†'); 142 | str = str.replace(/‡/g, '‡'); 143 | str = str.replace(/•/g, '•'); 144 | str = str.replace(/…/g, '…'); 145 | str = str.replace(/‰/g, '‰'); 146 | str = str.replace(/′/g, '′'); 147 | str = str.replace(/″/g, '″'); 148 | str = str.replace(/‹/g, '‹'); 149 | str = str.replace(/›/g, '›'); 150 | str = str.replace(/‾/g, '‾'); 151 | str = str.replace(/€/g, '€'); 152 | str = str.replace(/™/g, '™'); 153 | 154 | str = str.replace(/←/g, '←'); 155 | str = str.replace(/↑/g, '↑'); 156 | str = str.replace(/→/g, '→'); 157 | str = str.replace(/↓/g, '↓'); 158 | str = str.replace(/↔/g, '↔'); 159 | str = str.replace(/↵/g, '↵'); 160 | str = str.replace(/⌈/g, '⌈'); 161 | str = str.replace(/⌉/g, '⌉'); 162 | 163 | str = str.replace(/⌊/g, '⌊'); 164 | str = str.replace(/⌋/g, '⌋'); 165 | str = str.replace(/◊/g, '◊'); 166 | str = str.replace(/♠/g, '♠'); 167 | str = str.replace(/♣/g, '♣'); 168 | str = str.replace(/♥/g, '♥'); 169 | 170 | str = str.replace(/♦/g, '♦'); 171 | str = str.replace(/'/g, "'"); 172 | return str; 173 | } 174 | 175 | function strDiscode(str) { 176 | str = strNumDiscode(str); 177 | str = strGreeceDiscode(str); 178 | str = strcharacterDiscode(str); 179 | str = strOtherDiscode(str); 180 | return str; 181 | } 182 | 183 | function urlToHttpUrl(url, domain) { 184 | if (/^\/\//.test(url)) { 185 | return `https:${url}`; 186 | } else if (/^\//.test(url)) { 187 | return `https://${domain}${url}`; 188 | } 189 | return url; 190 | } 191 | 192 | export default { 193 | strDiscode, 194 | urlToHttpUrl, 195 | }; 196 | -------------------------------------------------------------------------------- /components/ly-markdown.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 193 | 194 | 242 | -------------------------------------------------------------------------------- /components/mpvue-wxparse/src/libs/html2json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * html2Json 改造来自: https://github.com/Jxck/html2json 3 | * 4 | * 5 | * author: Di (微信小程序开发工程师) 6 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 7 | * 垂直微信小程序开发交流社区 8 | * 9 | * github地址: https://github.com/icindy/wxParse 10 | * 11 | * for: 微信小程序富文本解析 12 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 13 | */ 14 | 15 | import wxDiscode from './wxDiscode'; 16 | import HTMLParser from './htmlparser'; 17 | 18 | function makeMap(str) { 19 | const obj = {}; 20 | const items = str.split(','); 21 | for (let i = 0; i < items.length; i += 1) obj[items[i]] = true; 22 | return obj; 23 | } 24 | 25 | // Block Elements - HTML 5 26 | const block = makeMap('br,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); 27 | 28 | // Inline Elements - HTML 5 29 | const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); 30 | 31 | // Elements that you can, intentionally, leave open 32 | // (and which close themselves) 33 | const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); 34 | 35 | function removeDOCTYPE(html) { 36 | const isDocument = /([^]*)<\/body>/.test(html); 37 | return isDocument ? RegExp.$1 : html; 38 | } 39 | 40 | function trimHtml(html) { 41 | return html 42 | .replace(//gi, '') 43 | .replace(/\/\*.*?\*\//gi, '') 44 | .replace(/[ ]+/gi, '') 46 | .replace(//gi, ''); 47 | } 48 | 49 | function getScreenInfo() { 50 | const screen = {}; 51 | wx.getSystemInfo({ 52 | success: (res) => { 53 | screen.width = res.windowWidth; 54 | screen.height = res.windowHeight; 55 | }, 56 | }); 57 | return screen; 58 | } 59 | 60 | function html2json(html, customHandler, imageProp, host) { 61 | // 处理字符串 62 | html = removeDOCTYPE(html); 63 | html = trimHtml(html); 64 | html = wxDiscode.strDiscode(html); 65 | // 生成node节点 66 | const bufArray = []; 67 | const results = { 68 | nodes: [], 69 | imageUrls: [], 70 | }; 71 | 72 | function Node(tag) { 73 | this.node = 'element'; 74 | this.tag = tag; 75 | } 76 | Node.prototype.$screen = getScreenInfo(); 77 | Node.prototype.$host = host; 78 | 79 | HTMLParser(html, { 80 | start(tag, attrs, unary) { 81 | // node for this element 82 | const node = new Node(tag); 83 | 84 | if (bufArray.length !== 0) { 85 | const parent = bufArray[0]; 86 | if (parent.nodes === undefined) { 87 | parent.nodes = []; 88 | } 89 | } 90 | 91 | if (block[tag]) { 92 | node.tagType = 'block'; 93 | } else if (inline[tag]) { 94 | node.tagType = 'inline'; 95 | } else if (closeSelf[tag]) { 96 | node.tagType = 'closeSelf'; 97 | } 98 | 99 | node.attr = attrs.reduce((pre, attr) => { 100 | const { name } = attr; 101 | let { value } = attr; 102 | if (name === 'class') { 103 | node.classStr = value; 104 | } 105 | // has multi attibutes 106 | // make it array of attribute 107 | if (name === 'style') { 108 | node.styleStr = value; 109 | } 110 | if (value.match(/ /)) { 111 | value = value.split(' '); 112 | } 113 | 114 | // if attr already exists 115 | // merge it 116 | if (pre[name]) { 117 | if (Array.isArray(pre[name])) { 118 | // already array, push to last 119 | pre[name].push(value); 120 | } else { 121 | // single value, make it array 122 | pre[name] = [pre[name], value]; 123 | } 124 | } else { 125 | // not exist, put it 126 | pre[name] = value; 127 | } 128 | 129 | return pre; 130 | }, {}); 131 | 132 | // 优化样式相关属性 133 | if (node.classStr) { 134 | node.classStr += ` ${node.tag}`; 135 | } else { 136 | node.classStr = node.tag; 137 | } 138 | if (node.tagType === 'inline') { 139 | node.classStr += ' inline'; 140 | } 141 | 142 | // 对img添加额外数据 143 | if (node.tag === 'img') { 144 | let imgUrl = node.attr.src; 145 | imgUrl = wxDiscode.urlToHttpUrl(imgUrl, imageProp.domain); 146 | Object.assign(node.attr, imageProp, { 147 | src: imgUrl || '', 148 | }); 149 | if (imgUrl) { 150 | results.imageUrls.push(imgUrl); 151 | } 152 | } 153 | 154 | // 处理a标签属性 155 | if (node.tag === 'a') { 156 | node.attr.href = node.attr.href || ''; 157 | } 158 | 159 | // 处理font标签样式属性 160 | if (node.tag === 'font') { 161 | const fontSize = [ 162 | 'x-small', 163 | 'small', 164 | 'medium', 165 | 'large', 166 | 'x-large', 167 | 'xx-large', 168 | '-webkit-xxx-large', 169 | ]; 170 | const styleAttrs = { 171 | color: 'color', 172 | face: 'font-family', 173 | size: 'font-size', 174 | }; 175 | if (!node.styleStr) node.styleStr = ''; 176 | Object.keys(styleAttrs).forEach((key) => { 177 | if (node.attr[key]) { 178 | const value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key]; 179 | node.styleStr += `${styleAttrs[key]}: ${value};`; 180 | } 181 | }); 182 | } 183 | 184 | // 临时记录source资源 185 | if (node.tag === 'source') { 186 | results.source = node.attr.src; 187 | } 188 | 189 | if (customHandler.start) { 190 | customHandler.start(node, results); 191 | } 192 | 193 | if (unary) { 194 | // if this tag doesn't have end tag 195 | // like 196 | // add to parents 197 | const parent = bufArray[0] || results; 198 | if (parent.nodes === undefined) { 199 | parent.nodes = []; 200 | } 201 | parent.nodes.push(node); 202 | } else { 203 | bufArray.unshift(node); 204 | } 205 | }, 206 | end(tag) { 207 | // merge into parent tag 208 | const node = bufArray.shift(); 209 | if (node.tag !== tag) { 210 | console.error('invalid state: mismatch end tag'); 211 | } 212 | 213 | // 当有缓存source资源时于于video补上src资源 214 | if (node.tag === 'video' && results.source) { 215 | node.attr.src = results.source; 216 | delete results.source; 217 | } 218 | 219 | if (customHandler.end) { 220 | customHandler.end(node, results); 221 | } 222 | 223 | if (bufArray.length === 0) { 224 | results.nodes.push(node); 225 | } else { 226 | const parent = bufArray[0]; 227 | if (!parent.nodes) { 228 | parent.nodes = []; 229 | } 230 | parent.nodes.push(node); 231 | } 232 | }, 233 | chars(text) { 234 | if (!text.trim()) return; 235 | 236 | const node = { 237 | node: 'text', 238 | text, 239 | }; 240 | 241 | if (customHandler.chars) { 242 | customHandler.chars(node, results); 243 | } 244 | 245 | if (bufArray.length === 0) { 246 | results.nodes.push(node); 247 | } else { 248 | const parent = bufArray[0]; 249 | if (parent.nodes === undefined) { 250 | parent.nodes = []; 251 | } 252 | parent.nodes.push(node); 253 | } 254 | }, 255 | }); 256 | 257 | return results; 258 | } 259 | 260 | export default html2json; 261 | -------------------------------------------------------------------------------- /static/markdown.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'iconfont'; 3 | src: url(data:application/font-woff2;charset=utf-8;base64,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) format('woff2'), 4 | url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); 5 | font-weight: normal; 6 | font-style: normal; 7 | } 8 | 9 | .iconfont { 10 | font-family:"iconfont" !important; 11 | font-size:16px; 12 | font-style:normal; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | .icon-aligncenter:before { content: "\e600"; } 18 | 19 | .icon-alignjustify:before { content: "\e601"; } 20 | 21 | .icon-alignleft:before { content: "\e602"; } 22 | 23 | .icon-aligncenter1:before { content: "\e603"; } 24 | 25 | .icon-alignjustify1:before { content: "\e604"; } 26 | 27 | .icon-alignleft1:before { content: "\e605"; } 28 | 29 | .icon-alignright:before { content: "\e606"; } 30 | 31 | .icon-anchor:before { content: "\e607"; } 32 | 33 | .icon-blockquote:before { content: "\e608"; } 34 | 35 | .icon-bold:before { content: "\e609"; } 36 | 37 | .icon-char:before { content: "\e60a"; } 38 | 39 | .icon-clearformat:before { content: "\e60b"; } 40 | 41 | .icon-code:before { content: "\e60c"; } 42 | 43 | .icon-configure:before { content: "\e60d"; } 44 | 45 | .icon-copy:before { content: "\e60e"; } 46 | 47 | .icon-corner:before { content: "\e60f"; } 48 | 49 | .icon-cut:before { content: "\e610"; } 50 | 51 | .icon-datetime:before { content: "\e611"; } 52 | 53 | .icon-explore:before { content: "\e612"; } 54 | 55 | .icon-find:before { content: "\e613"; } 56 | 57 | .icon-fullscreen:before { content: "\e614"; } 58 | 59 | .icon-help:before { content: "\e615"; } 60 | 61 | .icon-hr:before { content: "\e616"; } 62 | 63 | .icon-indent:before { content: "\e618"; } 64 | 65 | .icon-italic:before { content: "\e619"; } 66 | 67 | .icon-link:before { content: "\e61a"; } 68 | 69 | .icon-ltr:before { content: "\e61b"; } 70 | 71 | .icon-nbsp:before { content: "\e61c"; } 72 | 73 | .icon-new:before { content: "\e61d"; } 74 | 75 | .icon-ok:before { content: "\e61e"; } 76 | 77 | .icon-orderedlist:before { content: "\e61f"; } 78 | 79 | .icon-outdent:before { content: "\e620"; } 80 | 81 | .icon-pagebreak:before { content: "\e621"; } 82 | 83 | .icon-paragraph:before { content: "\e622"; } 84 | 85 | .icon-paste:before { content: "\e623"; } 86 | 87 | .icon-pasteastext:before { content: "\e624"; } 88 | 89 | .icon-preview:before { content: "\e625"; } 90 | 91 | .icon-print:before { content: "\e626"; } 92 | 93 | .icon-redo:before { content: "\e627"; } 94 | 95 | .icon-rtl:before { content: "\e628"; } 96 | 97 | .icon-save:before { content: "\e629"; } 98 | 99 | .icon-smiley:before { content: "\e62a"; } 100 | 101 | .icon-specialchar:before { content: "\e62b"; } 102 | 103 | .icon-spellcheck:before { content: "\e62c"; } 104 | 105 | .icon-strike:before { content: "\e62d"; } 106 | 107 | .icon-sub:before { content: "\e62e"; } 108 | 109 | .icon-sup:before { content: "\e62f"; } 110 | 111 | .icon-table:before { content: "\e630"; } 112 | 113 | .icon-template:before { content: "\e631"; } 114 | 115 | .icon-underline:before { content: "\e632"; } 116 | 117 | .icon-undo:before { content: "\e633"; } 118 | 119 | .icon-unlink:before { content: "\e634"; } 120 | 121 | .icon-unorderedlist:before { content: "\e635"; } 122 | 123 | .icon-video:before { content: "\e636"; } 124 | 125 | .icon-mark:before { content: "\e63a"; } 126 | 127 | .icon-image:before { content: "\e63e"; } 128 | 129 | .icon-xiahuaxian1:before { content: "\e7d1"; } 130 | 131 | .icon-qingkong:before { content: "\e6a3"; } 132 | 133 | -------------------------------------------------------------------------------- /components/marked/lib/marked.js: -------------------------------------------------------------------------------- 1 | /** 2 | * marked - a markdown parser 3 | * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) 4 | * https://github.com/markedjs/marked 5 | */ 6 | 7 | ;(function(root) { 8 | 'use strict'; 9 | 10 | /** 11 | * Block-Level Grammar 12 | */ 13 | 14 | var block = { 15 | newline: /^\n+/, 16 | code: /^( {4}[^\n]+\n*)+/, 17 | fences: noop, 18 | hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, 19 | heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, 20 | nptable: noop, 21 | blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, 22 | list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, 23 | html: '^ {0,3}(?:' // optional indentation 24 | + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) 25 | + '|comment[^\\n]*(\\n+|$)' // (2) 26 | + '|<\\?[\\s\\S]*?\\?>\\n*' // (3) 27 | + '|\\n*' // (4) 28 | + '|\\n*' // (5) 29 | + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) 30 | + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag 31 | + '|(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag 32 | + ')', 33 | def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, 34 | table: noop, 35 | lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, 36 | paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/, 37 | text: /^[^\n]+/ 38 | }; 39 | 40 | block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; 41 | block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; 42 | block.def = edit(block.def) 43 | .replace('label', block._label) 44 | .replace('title', block._title) 45 | .getRegex(); 46 | 47 | block.bullet = /(?:[*+-]|\d+\.)/; 48 | block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; 49 | block.item = edit(block.item, 'gm') 50 | .replace(/bull/g, block.bullet) 51 | .getRegex(); 52 | 53 | block.list = edit(block.list) 54 | .replace(/bull/g, block.bullet) 55 | .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') 56 | .replace('def', '\\n+(?=' + block.def.source + ')') 57 | .getRegex(); 58 | 59 | block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' 60 | + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' 61 | + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' 62 | + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' 63 | + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' 64 | + '|track|ul'; 65 | block._comment = //; 66 | block.html = edit(block.html, 'i') 67 | .replace('comment', block._comment) 68 | .replace('tag', block._tag) 69 | .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) 70 | .getRegex(); 71 | 72 | block.paragraph = edit(block.paragraph) 73 | .replace('hr', block.hr) 74 | .replace('heading', block.heading) 75 | .replace('lheading', block.lheading) 76 | .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks 77 | .getRegex(); 78 | 79 | block.blockquote = edit(block.blockquote) 80 | .replace('paragraph', block.paragraph) 81 | .getRegex(); 82 | 83 | /** 84 | * Normal Block Grammar 85 | */ 86 | 87 | block.normal = merge({}, block); 88 | 89 | /** 90 | * GFM Block Grammar 91 | */ 92 | 93 | block.gfm = merge({}, block.normal, { 94 | fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/, 95 | paragraph: /^/, 96 | heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ 97 | }); 98 | 99 | block.gfm.paragraph = edit(block.paragraph) 100 | .replace('(?!', '(?!' 101 | + block.gfm.fences.source.replace('\\1', '\\2') + '|' 102 | + block.list.source.replace('\\1', '\\3') + '|') 103 | .getRegex(); 104 | 105 | /** 106 | * GFM + Tables Block Grammar 107 | */ 108 | 109 | block.tables = merge({}, block.gfm, { 110 | nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/, 111 | table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/ 112 | }); 113 | 114 | /** 115 | * Pedantic grammar 116 | */ 117 | 118 | block.pedantic = merge({}, block.normal, { 119 | html: edit( 120 | '^ *(?:comment *(?:\\n|\\s*$)' 121 | + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag 122 | + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') 123 | .replace('comment', block._comment) 124 | .replace(/tag/g, '(?!(?:' 125 | + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' 126 | + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' 127 | + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b') 128 | .getRegex(), 129 | def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/ 130 | }); 131 | 132 | /** 133 | * Block Lexer 134 | */ 135 | 136 | function Lexer(options) { 137 | this.tokens = []; 138 | this.tokens.links = Object.create(null); 139 | this.options = options || marked.defaults; 140 | this.rules = block.normal; 141 | 142 | if (this.options.pedantic) { 143 | this.rules = block.pedantic; 144 | } else if (this.options.gfm) { 145 | if (this.options.tables) { 146 | this.rules = block.tables; 147 | } else { 148 | this.rules = block.gfm; 149 | } 150 | } 151 | } 152 | 153 | /** 154 | * Expose Block Rules 155 | */ 156 | 157 | Lexer.rules = block; 158 | 159 | /** 160 | * Static Lex Method 161 | */ 162 | 163 | Lexer.lex = function(src, options) { 164 | var lexer = new Lexer(options); 165 | return lexer.lex(src); 166 | }; 167 | 168 | /** 169 | * Preprocessing 170 | */ 171 | 172 | Lexer.prototype.lex = function(src) { 173 | src = src 174 | .replace(/\r\n|\r/g, '\n') 175 | .replace(/\t/g, ' ') 176 | .replace(/\u00a0/g, ' ') 177 | .replace(/\u2424/g, '\n'); 178 | 179 | return this.token(src, true); 180 | }; 181 | 182 | /** 183 | * Lexing 184 | */ 185 | 186 | Lexer.prototype.token = function(src, top) { 187 | src = src.replace(/^ +$/gm, ''); 188 | var next, 189 | loose, 190 | cap, 191 | bull, 192 | b, 193 | item, 194 | listStart, 195 | listItems, 196 | t, 197 | space, 198 | i, 199 | tag, 200 | l, 201 | isordered, 202 | istask, 203 | ischecked; 204 | 205 | while (src) { 206 | // newline 207 | if (cap = this.rules.newline.exec(src)) { 208 | src = src.substring(cap[0].length); 209 | if (cap[0].length > 1) { 210 | this.tokens.push({ 211 | type: 'space' 212 | }); 213 | } 214 | } 215 | 216 | // code 217 | if (cap = this.rules.code.exec(src)) { 218 | src = src.substring(cap[0].length); 219 | cap = cap[0].replace(/^ {4}/gm, ''); 220 | this.tokens.push({ 221 | type: 'code', 222 | text: !this.options.pedantic 223 | ? rtrim(cap, '\n') 224 | : cap 225 | }); 226 | continue; 227 | } 228 | 229 | // fences (gfm) 230 | if (cap = this.rules.fences.exec(src)) { 231 | src = src.substring(cap[0].length); 232 | this.tokens.push({ 233 | type: 'code', 234 | lang: cap[2], 235 | text: cap[3] || '' 236 | }); 237 | continue; 238 | } 239 | 240 | // heading 241 | if (cap = this.rules.heading.exec(src)) { 242 | src = src.substring(cap[0].length); 243 | this.tokens.push({ 244 | type: 'heading', 245 | depth: cap[1].length, 246 | text: cap[2] 247 | }); 248 | continue; 249 | } 250 | 251 | // table no leading pipe (gfm) 252 | if (top && (cap = this.rules.nptable.exec(src))) { 253 | item = { 254 | type: 'table', 255 | header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), 256 | align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), 257 | cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] 258 | }; 259 | 260 | if (item.header.length === item.align.length) { 261 | src = src.substring(cap[0].length); 262 | 263 | for (i = 0; i < item.align.length; i++) { 264 | if (/^ *-+: *$/.test(item.align[i])) { 265 | item.align[i] = 'right'; 266 | } else if (/^ *:-+: *$/.test(item.align[i])) { 267 | item.align[i] = 'center'; 268 | } else if (/^ *:-+ *$/.test(item.align[i])) { 269 | item.align[i] = 'left'; 270 | } else { 271 | item.align[i] = null; 272 | } 273 | } 274 | 275 | for (i = 0; i < item.cells.length; i++) { 276 | item.cells[i] = splitCells(item.cells[i], item.header.length); 277 | } 278 | 279 | this.tokens.push(item); 280 | 281 | continue; 282 | } 283 | } 284 | 285 | // hr 286 | if (cap = this.rules.hr.exec(src)) { 287 | src = src.substring(cap[0].length); 288 | this.tokens.push({ 289 | type: 'hr' 290 | }); 291 | continue; 292 | } 293 | 294 | // blockquote 295 | if (cap = this.rules.blockquote.exec(src)) { 296 | src = src.substring(cap[0].length); 297 | 298 | this.tokens.push({ 299 | type: 'blockquote_start' 300 | }); 301 | 302 | cap = cap[0].replace(/^ *> ?/gm, ''); 303 | 304 | // Pass `top` to keep the current 305 | // "toplevel" state. This is exactly 306 | // how markdown.pl works. 307 | this.token(cap, top); 308 | 309 | this.tokens.push({ 310 | type: 'blockquote_end' 311 | }); 312 | 313 | continue; 314 | } 315 | 316 | // list 317 | if (cap = this.rules.list.exec(src)) { 318 | src = src.substring(cap[0].length); 319 | bull = cap[2]; 320 | isordered = bull.length > 1; 321 | 322 | listStart = { 323 | type: 'list_start', 324 | ordered: isordered, 325 | start: isordered ? +bull : '', 326 | loose: false 327 | }; 328 | 329 | this.tokens.push(listStart); 330 | 331 | // Get each top-level item. 332 | cap = cap[0].match(this.rules.item); 333 | 334 | listItems = []; 335 | next = false; 336 | l = cap.length; 337 | i = 0; 338 | 339 | for (; i < l; i++) { 340 | item = cap[i]; 341 | 342 | // Remove the list item's bullet 343 | // so it is seen as the next token. 344 | space = item.length; 345 | item = item.replace(/^ *([*+-]|\d+\.) +/, ''); 346 | 347 | // Outdent whatever the 348 | // list item contains. Hacky. 349 | if (~item.indexOf('\n ')) { 350 | space -= item.length; 351 | item = !this.options.pedantic 352 | ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') 353 | : item.replace(/^ {1,4}/gm, ''); 354 | } 355 | 356 | // Determine whether the next list item belongs here. 357 | // Backpedal if it does not belong in this list. 358 | if (this.options.smartLists && i !== l - 1) { 359 | b = block.bullet.exec(cap[i + 1])[0]; 360 | if (bull !== b && !(bull.length > 1 && b.length > 1)) { 361 | src = cap.slice(i + 1).join('\n') + src; 362 | i = l - 1; 363 | } 364 | } 365 | 366 | // Determine whether item is loose or not. 367 | // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ 368 | // for discount behavior. 369 | loose = next || /\n\n(?!\s*$)/.test(item); 370 | if (i !== l - 1) { 371 | next = item.charAt(item.length - 1) === '\n'; 372 | if (!loose) loose = next; 373 | } 374 | 375 | if (loose) { 376 | listStart.loose = true; 377 | } 378 | 379 | // Check for task list items 380 | istask = /^\[[ xX]\] /.test(item); 381 | ischecked = undefined; 382 | if (istask) { 383 | ischecked = item[1] !== ' '; 384 | item = item.replace(/^\[[ xX]\] +/, ''); 385 | } 386 | 387 | t = { 388 | type: 'list_item_start', 389 | task: istask, 390 | checked: ischecked, 391 | loose: loose 392 | }; 393 | 394 | listItems.push(t); 395 | this.tokens.push(t); 396 | 397 | // Recurse. 398 | this.token(item, false); 399 | 400 | this.tokens.push({ 401 | type: 'list_item_end' 402 | }); 403 | } 404 | 405 | if (listStart.loose) { 406 | l = listItems.length; 407 | i = 0; 408 | for (; i < l; i++) { 409 | listItems[i].loose = true; 410 | } 411 | } 412 | 413 | this.tokens.push({ 414 | type: 'list_end' 415 | }); 416 | 417 | continue; 418 | } 419 | 420 | // html 421 | if (cap = this.rules.html.exec(src)) { 422 | src = src.substring(cap[0].length); 423 | this.tokens.push({ 424 | type: this.options.sanitize 425 | ? 'paragraph' 426 | : 'html', 427 | pre: !this.options.sanitizer 428 | && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), 429 | text: cap[0] 430 | }); 431 | continue; 432 | } 433 | 434 | // def 435 | if (top && (cap = this.rules.def.exec(src))) { 436 | src = src.substring(cap[0].length); 437 | if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); 438 | tag = cap[1].toLowerCase().replace(/\s+/g, ' '); 439 | if (!this.tokens.links[tag]) { 440 | this.tokens.links[tag] = { 441 | href: cap[2], 442 | title: cap[3] 443 | }; 444 | } 445 | continue; 446 | } 447 | 448 | // table (gfm) 449 | if (top && (cap = this.rules.table.exec(src))) { 450 | item = { 451 | type: 'table', 452 | header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), 453 | align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), 454 | cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : [] 455 | }; 456 | 457 | if (item.header.length === item.align.length) { 458 | src = src.substring(cap[0].length); 459 | 460 | for (i = 0; i < item.align.length; i++) { 461 | if (/^ *-+: *$/.test(item.align[i])) { 462 | item.align[i] = 'right'; 463 | } else if (/^ *:-+: *$/.test(item.align[i])) { 464 | item.align[i] = 'center'; 465 | } else if (/^ *:-+ *$/.test(item.align[i])) { 466 | item.align[i] = 'left'; 467 | } else { 468 | item.align[i] = null; 469 | } 470 | } 471 | 472 | for (i = 0; i < item.cells.length; i++) { 473 | item.cells[i] = splitCells( 474 | item.cells[i].replace(/^ *\| *| *\| *$/g, ''), 475 | item.header.length); 476 | } 477 | 478 | this.tokens.push(item); 479 | 480 | continue; 481 | } 482 | } 483 | 484 | // lheading 485 | if (cap = this.rules.lheading.exec(src)) { 486 | src = src.substring(cap[0].length); 487 | this.tokens.push({ 488 | type: 'heading', 489 | depth: cap[2] === '=' ? 1 : 2, 490 | text: cap[1] 491 | }); 492 | continue; 493 | } 494 | 495 | // top-level paragraph 496 | if (top && (cap = this.rules.paragraph.exec(src))) { 497 | src = src.substring(cap[0].length); 498 | this.tokens.push({ 499 | type: 'paragraph', 500 | text: cap[1].charAt(cap[1].length - 1) === '\n' 501 | ? cap[1].slice(0, -1) 502 | : cap[1] 503 | }); 504 | continue; 505 | } 506 | 507 | // text 508 | if (cap = this.rules.text.exec(src)) { 509 | // Top-level should never reach here. 510 | src = src.substring(cap[0].length); 511 | this.tokens.push({ 512 | type: 'text', 513 | text: cap[0] 514 | }); 515 | continue; 516 | } 517 | 518 | if (src) { 519 | throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); 520 | } 521 | } 522 | 523 | return this.tokens; 524 | }; 525 | 526 | /** 527 | * Inline-Level Grammar 528 | */ 529 | 530 | var inline = { 531 | escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, 532 | autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, 533 | url: noop, 534 | tag: '^comment' 535 | + '|^' // self-closing tag 536 | + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag 537 | + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. 538 | + '|^' // declaration, e.g. 539 | + '|^', // CDATA section 540 | link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/, 541 | reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, 542 | nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, 543 | strong: /^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/, 544 | em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/, 545 | code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/, 546 | br: /^( {2,}|\\)\n(?!\s*$)/, 547 | del: noop, 548 | text: /^[\s\S]+?(?=[\\?@\[\]\\^_`{|}~])/g; 552 | 553 | inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; 554 | inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/; 555 | inline.autolink = edit(inline.autolink) 556 | .replace('scheme', inline._scheme) 557 | .replace('email', inline._email) 558 | .getRegex(); 559 | 560 | inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; 561 | 562 | inline.tag = edit(inline.tag) 563 | .replace('comment', block._comment) 564 | .replace('attribute', inline._attribute) 565 | .getRegex(); 566 | 567 | inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/; 568 | inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/; 569 | inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; 570 | 571 | inline.link = edit(inline.link) 572 | .replace('label', inline._label) 573 | .replace('href', inline._href) 574 | .replace('title', inline._title) 575 | .getRegex(); 576 | 577 | inline.reflink = edit(inline.reflink) 578 | .replace('label', inline._label) 579 | .getRegex(); 580 | 581 | /** 582 | * Normal Inline Grammar 583 | */ 584 | 585 | inline.normal = merge({}, inline); 586 | 587 | /** 588 | * Pedantic Inline Grammar 589 | */ 590 | 591 | inline.pedantic = merge({}, inline.normal, { 592 | strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, 593 | em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/, 594 | link: edit(/^!?\[(label)\]\((.*?)\)/) 595 | .replace('label', inline._label) 596 | .getRegex(), 597 | reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/) 598 | .replace('label', inline._label) 599 | .getRegex() 600 | }); 601 | 602 | /** 603 | * GFM Inline Grammar 604 | */ 605 | 606 | inline.gfm = merge({}, inline.normal, { 607 | escape: edit(inline.escape).replace('])', '~|])').getRegex(), 608 | url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/) 609 | .replace('email', inline._email) 610 | .getRegex(), 611 | _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, 612 | del: /^~+(?=\S)([\s\S]*?\S)~+/, 613 | text: edit(inline.text) 614 | .replace(']|', '~]|') 615 | .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|') 616 | .getRegex() 617 | }); 618 | 619 | /** 620 | * GFM + Line Breaks Inline Grammar 621 | */ 622 | 623 | inline.breaks = merge({}, inline.gfm, { 624 | br: edit(inline.br).replace('{2,}', '*').getRegex(), 625 | text: edit(inline.gfm.text).replace('{2,}', '*').getRegex() 626 | }); 627 | 628 | /** 629 | * Inline Lexer & Compiler 630 | */ 631 | 632 | function InlineLexer(links, options) { 633 | this.options = options || marked.defaults; 634 | this.links = links; 635 | this.rules = inline.normal; 636 | this.renderer = this.options.renderer || new Renderer(); 637 | this.renderer.options = this.options; 638 | 639 | if (!this.links) { 640 | throw new Error('Tokens array requires a `links` property.'); 641 | } 642 | 643 | if (this.options.pedantic) { 644 | this.rules = inline.pedantic; 645 | } else if (this.options.gfm) { 646 | if (this.options.breaks) { 647 | this.rules = inline.breaks; 648 | } else { 649 | this.rules = inline.gfm; 650 | } 651 | } 652 | } 653 | 654 | /** 655 | * Expose Inline Rules 656 | */ 657 | 658 | InlineLexer.rules = inline; 659 | 660 | /** 661 | * Static Lexing/Compiling Method 662 | */ 663 | 664 | InlineLexer.output = function(src, links, options) { 665 | var inline = new InlineLexer(links, options); 666 | return inline.output(src); 667 | }; 668 | 669 | /** 670 | * Lexing/Compiling 671 | */ 672 | 673 | InlineLexer.prototype.output = function(src) { 674 | var out = '', 675 | link, 676 | text, 677 | href, 678 | title, 679 | cap, 680 | prevCapZero; 681 | 682 | while (src) { 683 | // escape 684 | if (cap = this.rules.escape.exec(src)) { 685 | src = src.substring(cap[0].length); 686 | out += cap[1]; 687 | continue; 688 | } 689 | 690 | // autolink 691 | if (cap = this.rules.autolink.exec(src)) { 692 | src = src.substring(cap[0].length); 693 | if (cap[2] === '@') { 694 | text = escape(this.mangle(cap[1])); 695 | href = 'mailto:' + text; 696 | } else { 697 | text = escape(cap[1]); 698 | href = text; 699 | } 700 | out += this.renderer.link(href, null, text); 701 | continue; 702 | } 703 | 704 | // url (gfm) 705 | if (!this.inLink && (cap = this.rules.url.exec(src))) { 706 | do { 707 | prevCapZero = cap[0]; 708 | cap[0] = this.rules._backpedal.exec(cap[0])[0]; 709 | } while (prevCapZero !== cap[0]); 710 | src = src.substring(cap[0].length); 711 | if (cap[2] === '@') { 712 | text = escape(cap[0]); 713 | href = 'mailto:' + text; 714 | } else { 715 | text = escape(cap[0]); 716 | if (cap[1] === 'www.') { 717 | href = 'http://' + text; 718 | } else { 719 | href = text; 720 | } 721 | } 722 | out += this.renderer.link(href, null, text); 723 | continue; 724 | } 725 | 726 | // tag 727 | if (cap = this.rules.tag.exec(src)) { 728 | if (!this.inLink && /^/i.test(cap[0])) { 731 | this.inLink = false; 732 | } 733 | src = src.substring(cap[0].length); 734 | out += this.options.sanitize 735 | ? this.options.sanitizer 736 | ? this.options.sanitizer(cap[0]) 737 | : escape(cap[0]) 738 | : cap[0] 739 | continue; 740 | } 741 | 742 | // link 743 | if (cap = this.rules.link.exec(src)) { 744 | src = src.substring(cap[0].length); 745 | this.inLink = true; 746 | href = cap[2]; 747 | if (this.options.pedantic) { 748 | link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); 749 | 750 | if (link) { 751 | href = link[1]; 752 | title = link[3]; 753 | } else { 754 | title = ''; 755 | } 756 | } else { 757 | title = cap[3] ? cap[3].slice(1, -1) : ''; 758 | } 759 | href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); 760 | out += this.outputLink(cap, { 761 | href: InlineLexer.escapes(href), 762 | title: InlineLexer.escapes(title) 763 | }); 764 | this.inLink = false; 765 | continue; 766 | } 767 | 768 | // reflink, nolink 769 | if ((cap = this.rules.reflink.exec(src)) 770 | || (cap = this.rules.nolink.exec(src))) { 771 | src = src.substring(cap[0].length); 772 | link = (cap[2] || cap[1]).replace(/\s+/g, ' '); 773 | link = this.links[link.toLowerCase()]; 774 | if (!link || !link.href) { 775 | out += cap[0].charAt(0); 776 | src = cap[0].substring(1) + src; 777 | continue; 778 | } 779 | this.inLink = true; 780 | out += this.outputLink(cap, link); 781 | this.inLink = false; 782 | continue; 783 | } 784 | 785 | // strong 786 | if (cap = this.rules.strong.exec(src)) { 787 | src = src.substring(cap[0].length); 788 | out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1])); 789 | continue; 790 | } 791 | 792 | // em 793 | if (cap = this.rules.em.exec(src)) { 794 | src = src.substring(cap[0].length); 795 | out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1])); 796 | continue; 797 | } 798 | 799 | // code 800 | if (cap = this.rules.code.exec(src)) { 801 | src = src.substring(cap[0].length); 802 | out += this.renderer.codespan(escape(cap[2].trim(), true)); 803 | continue; 804 | } 805 | 806 | // br 807 | if (cap = this.rules.br.exec(src)) { 808 | src = src.substring(cap[0].length); 809 | out += this.renderer.br(); 810 | continue; 811 | } 812 | 813 | // del (gfm) 814 | if (cap = this.rules.del.exec(src)) { 815 | src = src.substring(cap[0].length); 816 | out += this.renderer.del(this.output(cap[1])); 817 | continue; 818 | } 819 | 820 | // text 821 | if (cap = this.rules.text.exec(src)) { 822 | src = src.substring(cap[0].length); 823 | out += this.renderer.text(escape(this.smartypants(cap[0]))); 824 | continue; 825 | } 826 | 827 | if (src) { 828 | throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); 829 | } 830 | } 831 | 832 | return out; 833 | }; 834 | 835 | InlineLexer.escapes = function(text) { 836 | return text ? text.replace(InlineLexer.rules._escapes, '$1') : text; 837 | } 838 | 839 | /** 840 | * Compile Link 841 | */ 842 | 843 | InlineLexer.prototype.outputLink = function(cap, link) { 844 | var href = link.href, 845 | title = link.title ? escape(link.title) : null; 846 | 847 | return cap[0].charAt(0) !== '!' 848 | ? this.renderer.link(href, title, this.output(cap[1])) 849 | : this.renderer.image(href, title, escape(cap[1])); 850 | }; 851 | 852 | /** 853 | * Smartypants Transformations 854 | */ 855 | 856 | InlineLexer.prototype.smartypants = function(text) { 857 | if (!this.options.smartypants) return text; 858 | return text 859 | // em-dashes 860 | .replace(/---/g, '\u2014') 861 | // en-dashes 862 | .replace(/--/g, '\u2013') 863 | // opening singles 864 | .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') 865 | // closing singles & apostrophes 866 | .replace(/'/g, '\u2019') 867 | // opening doubles 868 | .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') 869 | // closing doubles 870 | .replace(/"/g, '\u201d') 871 | // ellipses 872 | .replace(/\.{3}/g, '\u2026'); 873 | }; 874 | 875 | /** 876 | * Mangle Links 877 | */ 878 | 879 | InlineLexer.prototype.mangle = function(text) { 880 | if (!this.options.mangle) return text; 881 | var out = '', 882 | l = text.length, 883 | i = 0, 884 | ch; 885 | 886 | for (; i < l; i++) { 887 | ch = text.charCodeAt(i); 888 | if (Math.random() > 0.5) { 889 | ch = 'x' + ch.toString(16); 890 | } 891 | out += '&#' + ch + ';'; 892 | } 893 | 894 | return out; 895 | }; 896 | 897 | /** 898 | * Renderer 899 | */ 900 | 901 | function Renderer(options) { 902 | this.options = options || marked.defaults; 903 | } 904 | 905 | Renderer.prototype.code = function(code, lang, escaped) { 906 | if (this.options.highlight) { 907 | var out = this.options.highlight(code, lang); 908 | if (out != null && out !== code) { 909 | escaped = true; 910 | code = out; 911 | } 912 | } 913 | 914 | if (!lang) { 915 | return '
'
 916 |       + (escaped ? code : escape(code, true))
 917 |       + '
'; 918 | } 919 | 920 | return '
'
 924 |     + (escaped ? code : escape(code, true))
 925 |     + '
\n'; 926 | }; 927 | 928 | Renderer.prototype.blockquote = function(quote) { 929 | return '
\n' + quote + '
\n'; 930 | }; 931 | 932 | Renderer.prototype.html = function(html) { 933 | return html; 934 | }; 935 | 936 | Renderer.prototype.heading = function(text, level, raw) { 937 | if (this.options.headerIds) { 938 | return '' 944 | + text 945 | + '\n'; 948 | } 949 | // ignore IDs 950 | return '' + text + '\n'; 951 | }; 952 | 953 | Renderer.prototype.hr = function() { 954 | return this.options.xhtml ? '
\n' : '
\n'; 955 | }; 956 | 957 | Renderer.prototype.list = function(body, ordered, start) { 958 | var type = ordered ? 'ol' : 'ul', 959 | startatt = (ordered && start !== 1) ? (' start="' + start + '"') : ''; 960 | return '<' + type + startatt + '>\n' + body + '\n'; 961 | }; 962 | 963 | Renderer.prototype.listitem = function(text) { 964 | return '
  • ' + text + '
  • \n'; 965 | }; 966 | 967 | Renderer.prototype.checkbox = function(checked) { 968 | return ' '; 973 | } 974 | 975 | Renderer.prototype.paragraph = function(text) { 976 | return '

    ' + text + '

    \n'; 977 | }; 978 | 979 | Renderer.prototype.table = function(header, body) { 980 | if (body) body = '' + body + ''; 981 | 982 | return '\n' 983 | + '\n' 984 | + header 985 | + '\n' 986 | + body 987 | + '
    \n'; 988 | }; 989 | 990 | Renderer.prototype.tablerow = function(content) { 991 | return '\n' + content + '\n'; 992 | }; 993 | 994 | Renderer.prototype.tablecell = function(content, flags) { 995 | var type = flags.header ? 'th' : 'td'; 996 | var tag = flags.align 997 | ? '<' + type + ' align="' + flags.align + '">' 998 | : '<' + type + '>'; 999 | return tag + content + '\n'; 1000 | }; 1001 | 1002 | // span level renderer 1003 | Renderer.prototype.strong = function(text) { 1004 | return '' + text + ''; 1005 | }; 1006 | 1007 | Renderer.prototype.em = function(text) { 1008 | return '' + text + ''; 1009 | }; 1010 | 1011 | Renderer.prototype.codespan = function(text) { 1012 | return '' + text + ''; 1013 | }; 1014 | 1015 | Renderer.prototype.br = function() { 1016 | return this.options.xhtml ? '
    ' : '
    '; 1017 | }; 1018 | 1019 | Renderer.prototype.del = function(text) { 1020 | return '' + text + ''; 1021 | }; 1022 | 1023 | Renderer.prototype.link = function(href, title, text) { 1024 | if (this.options.sanitize) { 1025 | try { 1026 | var prot = decodeURIComponent(unescape(href)) 1027 | .replace(/[^\w:]/g, '') 1028 | .toLowerCase(); 1029 | } catch (e) { 1030 | return text; 1031 | } 1032 | if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { 1033 | return text; 1034 | } 1035 | } 1036 | if (this.options.baseUrl && !originIndependentUrl.test(href)) { 1037 | href = resolveUrl(this.options.baseUrl, href); 1038 | } 1039 | try { 1040 | href = encodeURI(href).replace(/%25/g, '%'); 1041 | } catch (e) { 1042 | return text; 1043 | } 1044 | var out = '
    '; 1049 | return out; 1050 | }; 1051 | 1052 | Renderer.prototype.image = function(href, title, text) { 1053 | if (this.options.baseUrl && !originIndependentUrl.test(href)) { 1054 | href = resolveUrl(this.options.baseUrl, href); 1055 | } 1056 | var out = '' + text + '' : '>'; 1061 | return out; 1062 | }; 1063 | 1064 | Renderer.prototype.text = function(text) { 1065 | return text; 1066 | }; 1067 | 1068 | /** 1069 | * TextRenderer 1070 | * returns only the textual part of the token 1071 | */ 1072 | 1073 | function TextRenderer() {} 1074 | 1075 | // no need for block level renderers 1076 | 1077 | TextRenderer.prototype.strong = 1078 | TextRenderer.prototype.em = 1079 | TextRenderer.prototype.codespan = 1080 | TextRenderer.prototype.del = 1081 | TextRenderer.prototype.text = function (text) { 1082 | return text; 1083 | } 1084 | 1085 | TextRenderer.prototype.link = 1086 | TextRenderer.prototype.image = function(href, title, text) { 1087 | return '' + text; 1088 | } 1089 | 1090 | TextRenderer.prototype.br = function() { 1091 | return ''; 1092 | } 1093 | 1094 | /** 1095 | * Parsing & Compiling 1096 | */ 1097 | 1098 | function Parser(options) { 1099 | this.tokens = []; 1100 | this.token = null; 1101 | this.options = options || marked.defaults; 1102 | this.options.renderer = this.options.renderer || new Renderer(); 1103 | this.renderer = this.options.renderer; 1104 | this.renderer.options = this.options; 1105 | } 1106 | 1107 | /** 1108 | * Static Parse Method 1109 | */ 1110 | 1111 | Parser.parse = function(src, options) { 1112 | var parser = new Parser(options); 1113 | return parser.parse(src); 1114 | }; 1115 | 1116 | /** 1117 | * Parse Loop 1118 | */ 1119 | 1120 | Parser.prototype.parse = function(src) { 1121 | this.inline = new InlineLexer(src.links, this.options); 1122 | // use an InlineLexer with a TextRenderer to extract pure text 1123 | this.inlineText = new InlineLexer( 1124 | src.links, 1125 | merge({}, this.options, {renderer: new TextRenderer()}) 1126 | ); 1127 | this.tokens = src.reverse(); 1128 | 1129 | var out = ''; 1130 | while (this.next()) { 1131 | out += this.tok(); 1132 | } 1133 | 1134 | return out; 1135 | }; 1136 | 1137 | /** 1138 | * Next Token 1139 | */ 1140 | 1141 | Parser.prototype.next = function() { 1142 | return this.token = this.tokens.pop(); 1143 | }; 1144 | 1145 | /** 1146 | * Preview Next Token 1147 | */ 1148 | 1149 | Parser.prototype.peek = function() { 1150 | return this.tokens[this.tokens.length - 1] || 0; 1151 | }; 1152 | 1153 | /** 1154 | * Parse Text Tokens 1155 | */ 1156 | 1157 | Parser.prototype.parseText = function() { 1158 | var body = this.token.text; 1159 | 1160 | while (this.peek().type === 'text') { 1161 | body += '\n' + this.next().text; 1162 | } 1163 | 1164 | return this.inline.output(body); 1165 | }; 1166 | 1167 | /** 1168 | * Parse Current Token 1169 | */ 1170 | 1171 | Parser.prototype.tok = function() { 1172 | switch (this.token.type) { 1173 | case 'space': { 1174 | return ''; 1175 | } 1176 | case 'hr': { 1177 | return this.renderer.hr(); 1178 | } 1179 | case 'heading': { 1180 | return this.renderer.heading( 1181 | this.inline.output(this.token.text), 1182 | this.token.depth, 1183 | unescape(this.inlineText.output(this.token.text))); 1184 | } 1185 | case 'code': { 1186 | return this.renderer.code(this.token.text, 1187 | this.token.lang, 1188 | this.token.escaped); 1189 | } 1190 | case 'table': { 1191 | var header = '', 1192 | body = '', 1193 | i, 1194 | row, 1195 | cell, 1196 | j; 1197 | 1198 | // header 1199 | cell = ''; 1200 | for (i = 0; i < this.token.header.length; i++) { 1201 | cell += this.renderer.tablecell( 1202 | this.inline.output(this.token.header[i]), 1203 | { header: true, align: this.token.align[i] } 1204 | ); 1205 | } 1206 | header += this.renderer.tablerow(cell); 1207 | 1208 | for (i = 0; i < this.token.cells.length; i++) { 1209 | row = this.token.cells[i]; 1210 | 1211 | cell = ''; 1212 | for (j = 0; j < row.length; j++) { 1213 | cell += this.renderer.tablecell( 1214 | this.inline.output(row[j]), 1215 | { header: false, align: this.token.align[j] } 1216 | ); 1217 | } 1218 | 1219 | body += this.renderer.tablerow(cell); 1220 | } 1221 | return this.renderer.table(header, body); 1222 | } 1223 | case 'blockquote_start': { 1224 | body = ''; 1225 | 1226 | while (this.next().type !== 'blockquote_end') { 1227 | body += this.tok(); 1228 | } 1229 | 1230 | return this.renderer.blockquote(body); 1231 | } 1232 | case 'list_start': { 1233 | body = ''; 1234 | var ordered = this.token.ordered, 1235 | start = this.token.start; 1236 | 1237 | while (this.next().type !== 'list_end') { 1238 | body += this.tok(); 1239 | } 1240 | 1241 | return this.renderer.list(body, ordered, start); 1242 | } 1243 | case 'list_item_start': { 1244 | body = ''; 1245 | var loose = this.token.loose; 1246 | 1247 | if (this.token.task) { 1248 | body += this.renderer.checkbox(this.token.checked); 1249 | } 1250 | 1251 | while (this.next().type !== 'list_item_end') { 1252 | body += !loose && this.token.type === 'text' 1253 | ? this.parseText() 1254 | : this.tok(); 1255 | } 1256 | 1257 | return this.renderer.listitem(body); 1258 | } 1259 | case 'html': { 1260 | // TODO parse inline content if parameter markdown=1 1261 | return this.renderer.html(this.token.text); 1262 | } 1263 | case 'paragraph': { 1264 | return this.renderer.paragraph(this.inline.output(this.token.text)); 1265 | } 1266 | case 'text': { 1267 | return this.renderer.paragraph(this.parseText()); 1268 | } 1269 | } 1270 | }; 1271 | 1272 | /** 1273 | * Helpers 1274 | */ 1275 | 1276 | function escape(html, encode) { 1277 | return html 1278 | .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') 1279 | .replace(//g, '>') 1281 | .replace(/"/g, '"') 1282 | .replace(/'/g, '''); 1283 | } 1284 | 1285 | function unescape(html) { 1286 | // explicitly match decimal, hex, and named HTML entities 1287 | return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) { 1288 | n = n.toLowerCase(); 1289 | if (n === 'colon') return ':'; 1290 | if (n.charAt(0) === '#') { 1291 | return n.charAt(1) === 'x' 1292 | ? String.fromCharCode(parseInt(n.substring(2), 16)) 1293 | : String.fromCharCode(+n.substring(1)); 1294 | } 1295 | return ''; 1296 | }); 1297 | } 1298 | 1299 | function edit(regex, opt) { 1300 | regex = regex.source || regex; 1301 | opt = opt || ''; 1302 | return { 1303 | replace: function(name, val) { 1304 | val = val.source || val; 1305 | val = val.replace(/(^|[^\[])\^/g, '$1'); 1306 | regex = regex.replace(name, val); 1307 | return this; 1308 | }, 1309 | getRegex: function() { 1310 | return new RegExp(regex, opt); 1311 | } 1312 | }; 1313 | } 1314 | 1315 | function resolveUrl(base, href) { 1316 | if (!baseUrls[' ' + base]) { 1317 | // we can ignore everything in base after the last slash of its path component, 1318 | // but we might need to add _that_ 1319 | // https://tools.ietf.org/html/rfc3986#section-3 1320 | if (/^[^:]+:\/*[^/]*$/.test(base)) { 1321 | baseUrls[' ' + base] = base + '/'; 1322 | } else { 1323 | baseUrls[' ' + base] = rtrim(base, '/', true); 1324 | } 1325 | } 1326 | base = baseUrls[' ' + base]; 1327 | 1328 | if (href.slice(0, 2) === '//') { 1329 | return base.replace(/:[\s\S]*/, ':') + href; 1330 | } else if (href.charAt(0) === '/') { 1331 | return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href; 1332 | } else { 1333 | return base + href; 1334 | } 1335 | } 1336 | var baseUrls = {}; 1337 | var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; 1338 | 1339 | function noop() {} 1340 | noop.exec = noop; 1341 | 1342 | function merge(obj) { 1343 | var i = 1, 1344 | target, 1345 | key; 1346 | 1347 | for (; i < arguments.length; i++) { 1348 | target = arguments[i]; 1349 | for (key in target) { 1350 | if (Object.prototype.hasOwnProperty.call(target, key)) { 1351 | obj[key] = target[key]; 1352 | } 1353 | } 1354 | } 1355 | 1356 | return obj; 1357 | } 1358 | 1359 | function splitCells(tableRow, count) { 1360 | // ensure that every cell-delimiting pipe has a space 1361 | // before it to distinguish it from an escaped pipe 1362 | var row = tableRow.replace(/\|/g, function (match, offset, str) { 1363 | var escaped = false, 1364 | curr = offset; 1365 | while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; 1366 | if (escaped) { 1367 | // odd number of slashes means | is escaped 1368 | // so we leave it alone 1369 | return '|'; 1370 | } else { 1371 | // add space before unescaped | 1372 | return ' |'; 1373 | } 1374 | }), 1375 | cells = row.split(/ \|/), 1376 | i = 0; 1377 | 1378 | if (cells.length > count) { 1379 | cells.splice(count); 1380 | } else { 1381 | while (cells.length < count) cells.push(''); 1382 | } 1383 | 1384 | for (; i < cells.length; i++) { 1385 | // leading or trailing whitespace is ignored per the gfm spec 1386 | cells[i] = cells[i].trim().replace(/\\\|/g, '|'); 1387 | } 1388 | return cells; 1389 | } 1390 | 1391 | // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). 1392 | // /c*$/ is vulnerable to REDOS. 1393 | // invert: Remove suffix of non-c chars instead. Default falsey. 1394 | function rtrim(str, c, invert) { 1395 | if (str.length === 0) { 1396 | return ''; 1397 | } 1398 | 1399 | // Length of suffix matching the invert condition. 1400 | var suffLen = 0; 1401 | 1402 | // Step left until we fail to match the invert condition. 1403 | while (suffLen < str.length) { 1404 | var currChar = str.charAt(str.length - suffLen - 1); 1405 | if (currChar === c && !invert) { 1406 | suffLen++; 1407 | } else if (currChar !== c && invert) { 1408 | suffLen++; 1409 | } else { 1410 | break; 1411 | } 1412 | } 1413 | 1414 | return str.substr(0, str.length - suffLen); 1415 | } 1416 | 1417 | /** 1418 | * Marked 1419 | */ 1420 | 1421 | function marked(src, opt, callback) { 1422 | // throw error in case of non string input 1423 | if (typeof src === 'undefined' || src === null) { 1424 | throw new Error('marked(): input parameter is undefined or null'); 1425 | } 1426 | if (typeof src !== 'string') { 1427 | throw new Error('marked(): input parameter is of type ' 1428 | + Object.prototype.toString.call(src) + ', string expected'); 1429 | } 1430 | 1431 | if (callback || typeof opt === 'function') { 1432 | if (!callback) { 1433 | callback = opt; 1434 | opt = null; 1435 | } 1436 | 1437 | opt = merge({}, marked.defaults, opt || {}); 1438 | 1439 | var highlight = opt.highlight, 1440 | tokens, 1441 | pending, 1442 | i = 0; 1443 | 1444 | try { 1445 | tokens = Lexer.lex(src, opt) 1446 | } catch (e) { 1447 | return callback(e); 1448 | } 1449 | 1450 | pending = tokens.length; 1451 | 1452 | var done = function(err) { 1453 | if (err) { 1454 | opt.highlight = highlight; 1455 | return callback(err); 1456 | } 1457 | 1458 | var out; 1459 | 1460 | try { 1461 | out = Parser.parse(tokens, opt); 1462 | } catch (e) { 1463 | err = e; 1464 | } 1465 | 1466 | opt.highlight = highlight; 1467 | 1468 | return err 1469 | ? callback(err) 1470 | : callback(null, out); 1471 | }; 1472 | 1473 | if (!highlight || highlight.length < 3) { 1474 | return done(); 1475 | } 1476 | 1477 | delete opt.highlight; 1478 | 1479 | if (!pending) return done(); 1480 | 1481 | for (; i < tokens.length; i++) { 1482 | (function(token) { 1483 | if (token.type !== 'code') { 1484 | return --pending || done(); 1485 | } 1486 | return highlight(token.text, token.lang, function(err, code) { 1487 | if (err) return done(err); 1488 | if (code == null || code === token.text) { 1489 | return --pending || done(); 1490 | } 1491 | token.text = code; 1492 | token.escaped = true; 1493 | --pending || done(); 1494 | }); 1495 | })(tokens[i]); 1496 | } 1497 | 1498 | return; 1499 | } 1500 | try { 1501 | if (opt) opt = merge({}, marked.defaults, opt); 1502 | return Parser.parse(Lexer.lex(src, opt), opt); 1503 | } catch (e) { 1504 | e.message += '\nPlease report this to https://github.com/markedjs/marked.'; 1505 | if ((opt || marked.defaults).silent) { 1506 | return '

    An error occurred:

    '
    1507 |         + escape(e.message + '', true)
    1508 |         + '
    '; 1509 | } 1510 | throw e; 1511 | } 1512 | } 1513 | 1514 | /** 1515 | * Options 1516 | */ 1517 | 1518 | marked.options = 1519 | marked.setOptions = function(opt) { 1520 | merge(marked.defaults, opt); 1521 | return marked; 1522 | }; 1523 | 1524 | marked.getDefaults = function () { 1525 | return { 1526 | baseUrl: null, 1527 | breaks: false, 1528 | gfm: true, 1529 | headerIds: true, 1530 | headerPrefix: '', 1531 | highlight: null, 1532 | langPrefix: 'language-', 1533 | mangle: true, 1534 | pedantic: false, 1535 | renderer: new Renderer(), 1536 | sanitize: false, 1537 | sanitizer: null, 1538 | silent: false, 1539 | smartLists: false, 1540 | smartypants: false, 1541 | tables: true, 1542 | xhtml: false 1543 | }; 1544 | } 1545 | 1546 | marked.defaults = marked.getDefaults(); 1547 | 1548 | /** 1549 | * Expose 1550 | */ 1551 | 1552 | marked.Parser = Parser; 1553 | marked.parser = Parser.parse; 1554 | 1555 | marked.Renderer = Renderer; 1556 | marked.TextRenderer = TextRenderer; 1557 | 1558 | marked.Lexer = Lexer; 1559 | marked.lexer = Lexer.lex; 1560 | 1561 | marked.InlineLexer = InlineLexer; 1562 | marked.inlineLexer = InlineLexer.output; 1563 | 1564 | marked.parse = marked; 1565 | 1566 | if (typeof module !== 'undefined' && typeof exports === 'object') { 1567 | module.exports = marked; 1568 | } else if (typeof define === 'function' && define.amd) { 1569 | define(function() { return marked; }); 1570 | } else { 1571 | root.marked = marked; 1572 | } 1573 | })(this || (typeof window !== 'undefined' ? window : global)); 1574 | --------------------------------------------------------------------------------