正在显示
7 个修改的文件
包含
4567 行增加
和
0 行删除
wxParse/html2json.js
0 → 100644
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 | +var __placeImgeUrlHttps = "https"; | ||
16 | +var __emojisReg = ''; | ||
17 | +var __emojisBaseSrc = ''; | ||
18 | +var __emojis = {}; | ||
19 | +var wxDiscode = require('./wxDiscode.js'); | ||
20 | +var HTMLParser = require('./htmlparser.js'); | ||
21 | +// Empty Elements - HTML 5 | ||
22 | +var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr"); | ||
23 | +// Block Elements - HTML 5 | ||
24 | +var block = makeMap("br,a,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"); | ||
25 | + | ||
26 | +// Inline Elements - HTML 5 | ||
27 | +var inline = makeMap("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"); | ||
28 | + | ||
29 | +// Elements that you can, intentionally, leave open | ||
30 | +// (and which close themselves) | ||
31 | +var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); | ||
32 | + | ||
33 | +// Attributes that have their values filled in disabled="disabled" | ||
34 | +var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); | ||
35 | + | ||
36 | +// Special Elements (can contain anything) | ||
37 | +var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block"); | ||
38 | +function makeMap(str) { | ||
39 | + var obj = {}, items = str.split(","); | ||
40 | + for (var i = 0; i < items.length; i++) | ||
41 | + obj[items[i]] = true; | ||
42 | + return obj; | ||
43 | +} | ||
44 | + | ||
45 | +function q(v) { | ||
46 | + return '"' + v + '"'; | ||
47 | +} | ||
48 | + | ||
49 | +function removeDOCTYPE(html) { | ||
50 | + return html | ||
51 | + .replace(/<\?xml.*\?>\n/, '') | ||
52 | + .replace(/<.*!doctype.*\>\n/, '') | ||
53 | + .replace(/<.*!DOCTYPE.*\>\n/, ''); | ||
54 | +} | ||
55 | + | ||
56 | +function trimHtml(html) { | ||
57 | + return html | ||
58 | + // .replace(/\r?\n+/g, '') | ||
59 | + .replace(/<!--.*?-->/ig, '') | ||
60 | + .replace(/\/\*.*?\*\//ig, '') | ||
61 | + .replace(/[ ]+</ig, '<') | ||
62 | +} | ||
63 | + | ||
64 | + | ||
65 | +function html2json(html, bindName) { | ||
66 | + //处理字符串 | ||
67 | + html = removeDOCTYPE(html); | ||
68 | + // html = trimHtml(html); | ||
69 | + // html = wxDiscode.strDiscode(html); | ||
70 | + //生成node节点 | ||
71 | + var bufArray = []; | ||
72 | + var results = { | ||
73 | + node: bindName, | ||
74 | + nodes: [], | ||
75 | + images:[], | ||
76 | + imageUrls:[] | ||
77 | + }; | ||
78 | + var index = 0; | ||
79 | + HTMLParser(html, { | ||
80 | + start: function (tag, attrs, unary) { | ||
81 | + //debug(tag, attrs, unary); | ||
82 | + // node for this element | ||
83 | + var node = { | ||
84 | + node: 'element', | ||
85 | + tag: tag, | ||
86 | + }; | ||
87 | + | ||
88 | + if (bufArray.length === 0) { | ||
89 | + node.index = index.toString() | ||
90 | + index += 1 | ||
91 | + } else { | ||
92 | + var parent = bufArray[0]; | ||
93 | + if (parent.nodes === undefined) { | ||
94 | + parent.nodes = []; | ||
95 | + } | ||
96 | + node.index = parent.index + '.' + parent.nodes.length | ||
97 | + } | ||
98 | + | ||
99 | + if (block[tag]) { | ||
100 | + node.tagType = "block"; | ||
101 | + } else if (inline[tag]) { | ||
102 | + node.tagType = "inline"; | ||
103 | + } else if (closeSelf[tag]) { | ||
104 | + node.tagType = "closeSelf"; | ||
105 | + } | ||
106 | + | ||
107 | + if (attrs.length !== 0) { | ||
108 | + node.attr = attrs.reduce(function (pre, attr) { | ||
109 | + var name = attr.name; | ||
110 | + var value = attr.value; | ||
111 | + if (name == 'class') { | ||
112 | + // console.dir(value); | ||
113 | + // value = value.join("") | ||
114 | + node.classStr = value; | ||
115 | + } | ||
116 | + // has multi attibutes | ||
117 | + // make it array of attribute | ||
118 | + if (name == 'style') { | ||
119 | + // console.dir(value); | ||
120 | + // value = value.join("") | ||
121 | + node.styleStr = value; | ||
122 | + } | ||
123 | + if (value.match(/ /)) { | ||
124 | + value = value.split(' '); | ||
125 | + } | ||
126 | + | ||
127 | + | ||
128 | + // if attr already exists | ||
129 | + // merge it | ||
130 | + if (pre[name]) { | ||
131 | + if (Array.isArray(pre[name])) { | ||
132 | + // already array, push to last | ||
133 | + pre[name].push(value); | ||
134 | + } else { | ||
135 | + // single value, make it array | ||
136 | + pre[name] = [pre[name], value]; | ||
137 | + } | ||
138 | + } else { | ||
139 | + // not exist, put it | ||
140 | + pre[name] = value; | ||
141 | + } | ||
142 | + | ||
143 | + return pre; | ||
144 | + }, {}); | ||
145 | + } | ||
146 | + | ||
147 | + //对img添加额外数据 | ||
148 | + if (node.tag === 'img') { | ||
149 | + node.imgIndex = results.images.length; | ||
150 | + var imgUrl = node.attr.src; | ||
151 | + if (imgUrl[0] == '') { | ||
152 | + imgUrl.splice(0, 1); | ||
153 | + } | ||
154 | + imgUrl = wxDiscode.urlToHttpUrl(imgUrl, __placeImgeUrlHttps); | ||
155 | + node.attr.src = imgUrl; | ||
156 | + node.from = bindName; | ||
157 | + results.images.push(node); | ||
158 | + results.imageUrls.push(imgUrl); | ||
159 | + } | ||
160 | + | ||
161 | + // 处理font标签样式属性 | ||
162 | + if (node.tag === 'font') { | ||
163 | + var fontSize = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large']; | ||
164 | + var styleAttrs = { | ||
165 | + 'color': 'color', | ||
166 | + 'face': 'font-family', | ||
167 | + 'size': 'font-size' | ||
168 | + }; | ||
169 | + if (!node.attr.style) node.attr.style = []; | ||
170 | + if (!node.styleStr) node.styleStr = ''; | ||
171 | + for (var key in styleAttrs) { | ||
172 | + if (node.attr[key]) { | ||
173 | + var value = key === 'size' ? fontSize[node.attr[key]-1] : node.attr[key]; | ||
174 | + node.attr.style.push(styleAttrs[key]); | ||
175 | + node.attr.style.push(value); | ||
176 | + node.styleStr += styleAttrs[key] + ': ' + value + ';'; | ||
177 | + } | ||
178 | + } | ||
179 | + } | ||
180 | + | ||
181 | + //临时记录source资源 | ||
182 | + if(node.tag === 'source'){ | ||
183 | + results.source = node.attr.src; | ||
184 | + } | ||
185 | + | ||
186 | + if (unary) { | ||
187 | + // if this tag doesn't have end tag | ||
188 | + // like <img src="hoge.png"/> | ||
189 | + // add to parents | ||
190 | + var parent = bufArray[0] || results; | ||
191 | + if (parent.nodes === undefined) { | ||
192 | + parent.nodes = []; | ||
193 | + } | ||
194 | + parent.nodes.push(node); | ||
195 | + } else { | ||
196 | + bufArray.unshift(node); | ||
197 | + } | ||
198 | + }, | ||
199 | + end: function (tag) { | ||
200 | + //debug(tag); | ||
201 | + // merge into parent tag | ||
202 | + var node = bufArray.shift(); | ||
203 | + if (node.tag !== tag) console.error('invalid state: mismatch end tag'); | ||
204 | + | ||
205 | + //当有缓存source资源时于于video补上src资源 | ||
206 | + if(node.tag === 'video' && results.source){ | ||
207 | + node.attr.src = results.source; | ||
208 | + delete results.source; | ||
209 | + } | ||
210 | + | ||
211 | + if (bufArray.length === 0) { | ||
212 | + results.nodes.push(node); | ||
213 | + } else { | ||
214 | + var parent = bufArray[0]; | ||
215 | + if (parent.nodes === undefined) { | ||
216 | + parent.nodes = []; | ||
217 | + } | ||
218 | + parent.nodes.push(node); | ||
219 | + } | ||
220 | + }, | ||
221 | + chars: function (text) { | ||
222 | + //debug(text); | ||
223 | + var node = { | ||
224 | + node: 'text', | ||
225 | + text: text, | ||
226 | + textArray:transEmojiStr(text) | ||
227 | + }; | ||
228 | + | ||
229 | + if (bufArray.length === 0) { | ||
230 | + node.index = index.toString() | ||
231 | + index += 1 | ||
232 | + results.nodes.push(node); | ||
233 | + } else { | ||
234 | + var parent = bufArray[0]; | ||
235 | + if (parent.nodes === undefined) { | ||
236 | + parent.nodes = []; | ||
237 | + } | ||
238 | + node.index = parent.index + '.' + parent.nodes.length | ||
239 | + parent.nodes.push(node); | ||
240 | + } | ||
241 | + }, | ||
242 | + comment: function (text) { | ||
243 | + //debug(text); | ||
244 | + // var node = { | ||
245 | + // node: 'comment', | ||
246 | + // text: text, | ||
247 | + // }; | ||
248 | + // var parent = bufArray[0]; | ||
249 | + // if (parent.nodes === undefined) { | ||
250 | + // parent.nodes = []; | ||
251 | + // } | ||
252 | + // parent.nodes.push(node); | ||
253 | + }, | ||
254 | + }); | ||
255 | + return results; | ||
256 | +}; | ||
257 | + | ||
258 | +function transEmojiStr(str){ | ||
259 | + // var eReg = new RegExp("["+__reg+' '+"]"); | ||
260 | +// str = str.replace(/\[([^\[\]]+)\]/g,':$1:') | ||
261 | + | ||
262 | + var emojiObjs = []; | ||
263 | + //如果正则表达式为空 | ||
264 | + if(__emojisReg.length == 0 || !__emojis){ | ||
265 | + var emojiObj = {} | ||
266 | + emojiObj.node = "text"; | ||
267 | + emojiObj.text = str; | ||
268 | + array = [emojiObj]; | ||
269 | + return array; | ||
270 | + } | ||
271 | + //这个地方需要调整 | ||
272 | + str = str.replace(/\[([^\[\]]+)\]/g,':$1:') | ||
273 | + var eReg = new RegExp("[:]"); | ||
274 | + var array = str.split(eReg); | ||
275 | + for(var i = 0; i < array.length; i++){ | ||
276 | + var ele = array[i]; | ||
277 | + var emojiObj = {}; | ||
278 | + if(__emojis[ele]){ | ||
279 | + emojiObj.node = "element"; | ||
280 | + emojiObj.tag = "emoji"; | ||
281 | + emojiObj.text = __emojis[ele]; | ||
282 | + emojiObj.baseSrc= __emojisBaseSrc; | ||
283 | + }else{ | ||
284 | + emojiObj.node = "text"; | ||
285 | + emojiObj.text = ele; | ||
286 | + } | ||
287 | + emojiObjs.push(emojiObj); | ||
288 | + } | ||
289 | + | ||
290 | + return emojiObjs; | ||
291 | +} | ||
292 | + | ||
293 | +function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){ | ||
294 | + __emojisReg = reg; | ||
295 | + __emojisBaseSrc=baseSrc; | ||
296 | + __emojis=emojis; | ||
297 | +} | ||
298 | + | ||
299 | +module.exports = { | ||
300 | + html2json: html2json, | ||
301 | + emojisInit:emojisInit | ||
302 | +}; | ||
303 | + |
wxParse/htmlparser.js
0 → 100644
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 | +var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, | ||
16 | + endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/, | ||
17 | + attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; | ||
18 | + | ||
19 | +// Empty Elements - HTML 5 | ||
20 | +var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr"); | ||
21 | + | ||
22 | +// Block Elements - HTML 5 | ||
23 | +var block = makeMap("a,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"); | ||
24 | + | ||
25 | +// Inline Elements - HTML 5 | ||
26 | +var inline = makeMap("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"); | ||
27 | + | ||
28 | +// Elements that you can, intentionally, leave open | ||
29 | +// (and which close themselves) | ||
30 | +var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); | ||
31 | + | ||
32 | +// Attributes that have their values filled in disabled="disabled" | ||
33 | +var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); | ||
34 | + | ||
35 | +// Special Elements (can contain anything) | ||
36 | +var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block"); | ||
37 | + | ||
38 | +function HTMLParser(html, handler) { | ||
39 | + var index, chars, match, stack = [], last = html; | ||
40 | + stack.last = function () { | ||
41 | + return this[this.length - 1]; | ||
42 | + }; | ||
43 | + | ||
44 | + while (html) { | ||
45 | + chars = true; | ||
46 | + | ||
47 | + // Make sure we're not in a script or style element | ||
48 | + if (!stack.last() || !special[stack.last()]) { | ||
49 | + | ||
50 | + // Comment | ||
51 | + if (html.indexOf("<!--") == 0) { | ||
52 | + index = html.indexOf("-->"); | ||
53 | + | ||
54 | + if (index >= 0) { | ||
55 | + if (handler.comment) | ||
56 | + handler.comment(html.substring(4, index)); | ||
57 | + html = html.substring(index + 3); | ||
58 | + chars = false; | ||
59 | + } | ||
60 | + | ||
61 | + // end tag | ||
62 | + } else if (html.indexOf("</") == 0) { | ||
63 | + match = html.match(endTag); | ||
64 | + | ||
65 | + if (match) { | ||
66 | + html = html.substring(match[0].length); | ||
67 | + match[0].replace(endTag, parseEndTag); | ||
68 | + chars = false; | ||
69 | + } | ||
70 | + | ||
71 | + // start tag | ||
72 | + } else if (html.indexOf("<") == 0) { | ||
73 | + match = html.match(startTag); | ||
74 | + | ||
75 | + if (match) { | ||
76 | + html = html.substring(match[0].length); | ||
77 | + match[0].replace(startTag, parseStartTag); | ||
78 | + chars = false; | ||
79 | + } | ||
80 | + } | ||
81 | + | ||
82 | + if (chars) { | ||
83 | + index = html.indexOf("<"); | ||
84 | + var text = '' | ||
85 | + while (index === 0) { | ||
86 | + text += "<"; | ||
87 | + html = html.substring(1); | ||
88 | + index = html.indexOf("<"); | ||
89 | + } | ||
90 | + text += index < 0 ? html : html.substring(0, index); | ||
91 | + html = index < 0 ? "" : html.substring(index); | ||
92 | + | ||
93 | + if (handler.chars) | ||
94 | + handler.chars(text); | ||
95 | + } | ||
96 | + | ||
97 | + } else { | ||
98 | + | ||
99 | + html = html.replace(new RegExp("([\\s\\S]*?)<\/" + stack.last() + "[^>]*>"), function (all, text) { | ||
100 | + text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, "$1$2"); | ||
101 | + if (handler.chars) | ||
102 | + handler.chars(text); | ||
103 | + | ||
104 | + return ""; | ||
105 | + }); | ||
106 | + | ||
107 | + | ||
108 | + parseEndTag("", stack.last()); | ||
109 | + } | ||
110 | + | ||
111 | + if (html == last) | ||
112 | + throw "Parse Error: " + html; | ||
113 | + last = html; | ||
114 | + } | ||
115 | + | ||
116 | + // Clean up any remaining tags | ||
117 | + parseEndTag(); | ||
118 | + | ||
119 | + function parseStartTag(tag, tagName, rest, unary) { | ||
120 | + tagName = tagName.toLowerCase(); | ||
121 | + | ||
122 | + if (block[tagName]) { | ||
123 | + while (stack.last() && inline[stack.last()]) { | ||
124 | + parseEndTag("", stack.last()); | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + if (closeSelf[tagName] && stack.last() == tagName) { | ||
129 | + parseEndTag("", tagName); | ||
130 | + } | ||
131 | + | ||
132 | + unary = empty[tagName] || !!unary; | ||
133 | + | ||
134 | + if (!unary) | ||
135 | + stack.push(tagName); | ||
136 | + | ||
137 | + if (handler.start) { | ||
138 | + var attrs = []; | ||
139 | + | ||
140 | + rest.replace(attr, function (match, name) { | ||
141 | + var value = arguments[2] ? arguments[2] : | ||
142 | + arguments[3] ? arguments[3] : | ||
143 | + arguments[4] ? arguments[4] : | ||
144 | + fillAttrs[name] ? name : ""; | ||
145 | + | ||
146 | + attrs.push({ | ||
147 | + name: name, | ||
148 | + value: value, | ||
149 | + escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //" | ||
150 | + }); | ||
151 | + }); | ||
152 | + | ||
153 | + if (handler.start) { | ||
154 | + handler.start(tagName, attrs, unary); | ||
155 | + } | ||
156 | + | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + function parseEndTag(tag, tagName) { | ||
161 | + // If no tag name is provided, clean shop | ||
162 | + if (!tagName) | ||
163 | + var pos = 0; | ||
164 | + | ||
165 | + // Find the closest opened tag of the same type | ||
166 | + else { | ||
167 | + tagName = tagName.toLowerCase(); | ||
168 | + for (var pos = stack.length - 1; pos >= 0; pos--) | ||
169 | + if (stack[pos] == tagName) | ||
170 | + break; | ||
171 | + } | ||
172 | + if (pos >= 0) { | ||
173 | + // Close all the open elements, up the stack | ||
174 | + for (var i = stack.length - 1; i >= pos; i--) | ||
175 | + if (handler.end) | ||
176 | + handler.end(stack[i]); | ||
177 | + | ||
178 | + // Remove the open elements from the stack | ||
179 | + stack.length = pos; | ||
180 | + } | ||
181 | + } | ||
182 | +}; | ||
183 | + | ||
184 | + | ||
185 | +function makeMap(str) { | ||
186 | + var obj = {}, items = str.split(","); | ||
187 | + for (var i = 0; i < items.length; i++) | ||
188 | + obj[items[i]] = true; | ||
189 | + return obj; | ||
190 | +} | ||
191 | + | ||
192 | +module.exports = HTMLParser; |
wxParse/showdown.js
0 → 100644
1 | +/** | ||
2 | + * | ||
3 | + * showdown: https://github.com/showdownjs/showdown | ||
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 | +function getDefaultOpts(simple) { | ||
16 | + 'use strict'; | ||
17 | + | ||
18 | + var defaultOptions = { | ||
19 | + omitExtraWLInCodeBlocks: { | ||
20 | + defaultValue: false, | ||
21 | + describe: 'Omit the default extra whiteline added to code blocks', | ||
22 | + type: 'boolean' | ||
23 | + }, | ||
24 | + noHeaderId: { | ||
25 | + defaultValue: false, | ||
26 | + describe: 'Turn on/off generated header id', | ||
27 | + type: 'boolean' | ||
28 | + }, | ||
29 | + prefixHeaderId: { | ||
30 | + defaultValue: false, | ||
31 | + describe: 'Specify a prefix to generated header ids', | ||
32 | + type: 'string' | ||
33 | + }, | ||
34 | + headerLevelStart: { | ||
35 | + defaultValue: false, | ||
36 | + describe: 'The header blocks level start', | ||
37 | + type: 'integer' | ||
38 | + }, | ||
39 | + parseImgDimensions: { | ||
40 | + defaultValue: false, | ||
41 | + describe: 'Turn on/off image dimension parsing', | ||
42 | + type: 'boolean' | ||
43 | + }, | ||
44 | + simplifiedAutoLink: { | ||
45 | + defaultValue: false, | ||
46 | + describe: 'Turn on/off GFM autolink style', | ||
47 | + type: 'boolean' | ||
48 | + }, | ||
49 | + literalMidWordUnderscores: { | ||
50 | + defaultValue: false, | ||
51 | + describe: 'Parse midword underscores as literal underscores', | ||
52 | + type: 'boolean' | ||
53 | + }, | ||
54 | + strikethrough: { | ||
55 | + defaultValue: false, | ||
56 | + describe: 'Turn on/off strikethrough support', | ||
57 | + type: 'boolean' | ||
58 | + }, | ||
59 | + tables: { | ||
60 | + defaultValue: false, | ||
61 | + describe: 'Turn on/off tables support', | ||
62 | + type: 'boolean' | ||
63 | + }, | ||
64 | + tablesHeaderId: { | ||
65 | + defaultValue: false, | ||
66 | + describe: 'Add an id to table headers', | ||
67 | + type: 'boolean' | ||
68 | + }, | ||
69 | + ghCodeBlocks: { | ||
70 | + defaultValue: true, | ||
71 | + describe: 'Turn on/off GFM fenced code blocks support', | ||
72 | + type: 'boolean' | ||
73 | + }, | ||
74 | + tasklists: { | ||
75 | + defaultValue: false, | ||
76 | + describe: 'Turn on/off GFM tasklist support', | ||
77 | + type: 'boolean' | ||
78 | + }, | ||
79 | + smoothLivePreview: { | ||
80 | + defaultValue: false, | ||
81 | + describe: 'Prevents weird effects in live previews due to incomplete input', | ||
82 | + type: 'boolean' | ||
83 | + }, | ||
84 | + smartIndentationFix: { | ||
85 | + defaultValue: false, | ||
86 | + description: 'Tries to smartly fix identation in es6 strings', | ||
87 | + type: 'boolean' | ||
88 | + } | ||
89 | + }; | ||
90 | + if (simple === false) { | ||
91 | + return JSON.parse(JSON.stringify(defaultOptions)); | ||
92 | + } | ||
93 | + var ret = {}; | ||
94 | + for (var opt in defaultOptions) { | ||
95 | + if (defaultOptions.hasOwnProperty(opt)) { | ||
96 | + ret[opt] = defaultOptions[opt].defaultValue; | ||
97 | + } | ||
98 | + } | ||
99 | + return ret; | ||
100 | +} | ||
101 | + | ||
102 | +/** | ||
103 | + * Created by Tivie on 06-01-2015. | ||
104 | + */ | ||
105 | + | ||
106 | +// Private properties | ||
107 | +var showdown = {}, | ||
108 | + parsers = {}, | ||
109 | + extensions = {}, | ||
110 | + globalOptions = getDefaultOpts(true), | ||
111 | + flavor = { | ||
112 | + github: { | ||
113 | + omitExtraWLInCodeBlocks: true, | ||
114 | + prefixHeaderId: 'user-content-', | ||
115 | + simplifiedAutoLink: true, | ||
116 | + literalMidWordUnderscores: true, | ||
117 | + strikethrough: true, | ||
118 | + tables: true, | ||
119 | + tablesHeaderId: true, | ||
120 | + ghCodeBlocks: true, | ||
121 | + tasklists: true | ||
122 | + }, | ||
123 | + vanilla: getDefaultOpts(true) | ||
124 | + }; | ||
125 | + | ||
126 | +/** | ||
127 | + * helper namespace | ||
128 | + * @type {{}} | ||
129 | + */ | ||
130 | +showdown.helper = {}; | ||
131 | + | ||
132 | +/** | ||
133 | + * TODO LEGACY SUPPORT CODE | ||
134 | + * @type {{}} | ||
135 | + */ | ||
136 | +showdown.extensions = {}; | ||
137 | + | ||
138 | +/** | ||
139 | + * Set a global option | ||
140 | + * @static | ||
141 | + * @param {string} key | ||
142 | + * @param {*} value | ||
143 | + * @returns {showdown} | ||
144 | + */ | ||
145 | +showdown.setOption = function (key, value) { | ||
146 | + 'use strict'; | ||
147 | + globalOptions[key] = value; | ||
148 | + return this; | ||
149 | +}; | ||
150 | + | ||
151 | +/** | ||
152 | + * Get a global option | ||
153 | + * @static | ||
154 | + * @param {string} key | ||
155 | + * @returns {*} | ||
156 | + */ | ||
157 | +showdown.getOption = function (key) { | ||
158 | + 'use strict'; | ||
159 | + return globalOptions[key]; | ||
160 | +}; | ||
161 | + | ||
162 | +/** | ||
163 | + * Get the global options | ||
164 | + * @static | ||
165 | + * @returns {{}} | ||
166 | + */ | ||
167 | +showdown.getOptions = function () { | ||
168 | + 'use strict'; | ||
169 | + return globalOptions; | ||
170 | +}; | ||
171 | + | ||
172 | +/** | ||
173 | + * Reset global options to the default values | ||
174 | + * @static | ||
175 | + */ | ||
176 | +showdown.resetOptions = function () { | ||
177 | + 'use strict'; | ||
178 | + globalOptions = getDefaultOpts(true); | ||
179 | +}; | ||
180 | + | ||
181 | +/** | ||
182 | + * Set the flavor showdown should use as default | ||
183 | + * @param {string} name | ||
184 | + */ | ||
185 | +showdown.setFlavor = function (name) { | ||
186 | + 'use strict'; | ||
187 | + if (flavor.hasOwnProperty(name)) { | ||
188 | + var preset = flavor[name]; | ||
189 | + for (var option in preset) { | ||
190 | + if (preset.hasOwnProperty(option)) { | ||
191 | + globalOptions[option] = preset[option]; | ||
192 | + } | ||
193 | + } | ||
194 | + } | ||
195 | +}; | ||
196 | + | ||
197 | +/** | ||
198 | + * Get the default options | ||
199 | + * @static | ||
200 | + * @param {boolean} [simple=true] | ||
201 | + * @returns {{}} | ||
202 | + */ | ||
203 | +showdown.getDefaultOptions = function (simple) { | ||
204 | + 'use strict'; | ||
205 | + return getDefaultOpts(simple); | ||
206 | +}; | ||
207 | + | ||
208 | +/** | ||
209 | + * Get or set a subParser | ||
210 | + * | ||
211 | + * subParser(name) - Get a registered subParser | ||
212 | + * subParser(name, func) - Register a subParser | ||
213 | + * @static | ||
214 | + * @param {string} name | ||
215 | + * @param {function} [func] | ||
216 | + * @returns {*} | ||
217 | + */ | ||
218 | +showdown.subParser = function (name, func) { | ||
219 | + 'use strict'; | ||
220 | + if (showdown.helper.isString(name)) { | ||
221 | + if (typeof func !== 'undefined') { | ||
222 | + parsers[name] = func; | ||
223 | + } else { | ||
224 | + if (parsers.hasOwnProperty(name)) { | ||
225 | + return parsers[name]; | ||
226 | + } else { | ||
227 | + throw Error('SubParser named ' + name + ' not registered!'); | ||
228 | + } | ||
229 | + } | ||
230 | + } | ||
231 | +}; | ||
232 | + | ||
233 | +/** | ||
234 | + * Gets or registers an extension | ||
235 | + * @static | ||
236 | + * @param {string} name | ||
237 | + * @param {object|function=} ext | ||
238 | + * @returns {*} | ||
239 | + */ | ||
240 | +showdown.extension = function (name, ext) { | ||
241 | + 'use strict'; | ||
242 | + | ||
243 | + if (!showdown.helper.isString(name)) { | ||
244 | + throw Error('Extension \'name\' must be a string'); | ||
245 | + } | ||
246 | + | ||
247 | + name = showdown.helper.stdExtName(name); | ||
248 | + | ||
249 | + // Getter | ||
250 | + if (showdown.helper.isUndefined(ext)) { | ||
251 | + if (!extensions.hasOwnProperty(name)) { | ||
252 | + throw Error('Extension named ' + name + ' is not registered!'); | ||
253 | + } | ||
254 | + return extensions[name]; | ||
255 | + | ||
256 | + // Setter | ||
257 | + } else { | ||
258 | + // Expand extension if it's wrapped in a function | ||
259 | + if (typeof ext === 'function') { | ||
260 | + ext = ext(); | ||
261 | + } | ||
262 | + | ||
263 | + // Ensure extension is an array | ||
264 | + if (!showdown.helper.isArray(ext)) { | ||
265 | + ext = [ext]; | ||
266 | + } | ||
267 | + | ||
268 | + var validExtension = validate(ext, name); | ||
269 | + | ||
270 | + if (validExtension.valid) { | ||
271 | + extensions[name] = ext; | ||
272 | + } else { | ||
273 | + throw Error(validExtension.error); | ||
274 | + } | ||
275 | + } | ||
276 | +}; | ||
277 | + | ||
278 | +/** | ||
279 | + * Gets all extensions registered | ||
280 | + * @returns {{}} | ||
281 | + */ | ||
282 | +showdown.getAllExtensions = function () { | ||
283 | + 'use strict'; | ||
284 | + return extensions; | ||
285 | +}; | ||
286 | + | ||
287 | +/** | ||
288 | + * Remove an extension | ||
289 | + * @param {string} name | ||
290 | + */ | ||
291 | +showdown.removeExtension = function (name) { | ||
292 | + 'use strict'; | ||
293 | + delete extensions[name]; | ||
294 | +}; | ||
295 | + | ||
296 | +/** | ||
297 | + * Removes all extensions | ||
298 | + */ | ||
299 | +showdown.resetExtensions = function () { | ||
300 | + 'use strict'; | ||
301 | + extensions = {}; | ||
302 | +}; | ||
303 | + | ||
304 | +/** | ||
305 | + * Validate extension | ||
306 | + * @param {array} extension | ||
307 | + * @param {string} name | ||
308 | + * @returns {{valid: boolean, error: string}} | ||
309 | + */ | ||
310 | +function validate(extension, name) { | ||
311 | + 'use strict'; | ||
312 | + | ||
313 | + var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', | ||
314 | + ret = { | ||
315 | + valid: true, | ||
316 | + error: '' | ||
317 | + }; | ||
318 | + | ||
319 | + if (!showdown.helper.isArray(extension)) { | ||
320 | + extension = [extension]; | ||
321 | + } | ||
322 | + | ||
323 | + for (var i = 0; i < extension.length; ++i) { | ||
324 | + var baseMsg = errMsg + ' sub-extension ' + i + ': ', | ||
325 | + ext = extension[i]; | ||
326 | + if (typeof ext !== 'object') { | ||
327 | + ret.valid = false; | ||
328 | + ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; | ||
329 | + return ret; | ||
330 | + } | ||
331 | + | ||
332 | + if (!showdown.helper.isString(ext.type)) { | ||
333 | + ret.valid = false; | ||
334 | + ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; | ||
335 | + return ret; | ||
336 | + } | ||
337 | + | ||
338 | + var type = ext.type = ext.type.toLowerCase(); | ||
339 | + | ||
340 | + // normalize extension type | ||
341 | + if (type === 'language') { | ||
342 | + type = ext.type = 'lang'; | ||
343 | + } | ||
344 | + | ||
345 | + if (type === 'html') { | ||
346 | + type = ext.type = 'output'; | ||
347 | + } | ||
348 | + | ||
349 | + if (type !== 'lang' && type !== 'output' && type !== 'listener') { | ||
350 | + ret.valid = false; | ||
351 | + ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"'; | ||
352 | + return ret; | ||
353 | + } | ||
354 | + | ||
355 | + if (type === 'listener') { | ||
356 | + if (showdown.helper.isUndefined(ext.listeners)) { | ||
357 | + ret.valid = false; | ||
358 | + ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"'; | ||
359 | + return ret; | ||
360 | + } | ||
361 | + } else { | ||
362 | + if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { | ||
363 | + ret.valid = false; | ||
364 | + ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method'; | ||
365 | + return ret; | ||
366 | + } | ||
367 | + } | ||
368 | + | ||
369 | + if (ext.listeners) { | ||
370 | + if (typeof ext.listeners !== 'object') { | ||
371 | + ret.valid = false; | ||
372 | + ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given'; | ||
373 | + return ret; | ||
374 | + } | ||
375 | + for (var ln in ext.listeners) { | ||
376 | + if (ext.listeners.hasOwnProperty(ln)) { | ||
377 | + if (typeof ext.listeners[ln] !== 'function') { | ||
378 | + ret.valid = false; | ||
379 | + ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln + | ||
380 | + ' must be a function but ' + typeof ext.listeners[ln] + ' given'; | ||
381 | + return ret; | ||
382 | + } | ||
383 | + } | ||
384 | + } | ||
385 | + } | ||
386 | + | ||
387 | + if (ext.filter) { | ||
388 | + if (typeof ext.filter !== 'function') { | ||
389 | + ret.valid = false; | ||
390 | + ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; | ||
391 | + return ret; | ||
392 | + } | ||
393 | + } else if (ext.regex) { | ||
394 | + if (showdown.helper.isString(ext.regex)) { | ||
395 | + ext.regex = new RegExp(ext.regex, 'g'); | ||
396 | + } | ||
397 | + if (!ext.regex instanceof RegExp) { | ||
398 | + ret.valid = false; | ||
399 | + ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given'; | ||
400 | + return ret; | ||
401 | + } | ||
402 | + if (showdown.helper.isUndefined(ext.replace)) { | ||
403 | + ret.valid = false; | ||
404 | + ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; | ||
405 | + return ret; | ||
406 | + } | ||
407 | + } | ||
408 | + } | ||
409 | + return ret; | ||
410 | +} | ||
411 | + | ||
412 | +/** | ||
413 | + * Validate extension | ||
414 | + * @param {object} ext | ||
415 | + * @returns {boolean} | ||
416 | + */ | ||
417 | +showdown.validateExtension = function (ext) { | ||
418 | + 'use strict'; | ||
419 | + | ||
420 | + var validateExtension = validate(ext, null); | ||
421 | + if (!validateExtension.valid) { | ||
422 | + console.warn(validateExtension.error); | ||
423 | + return false; | ||
424 | + } | ||
425 | + return true; | ||
426 | +}; | ||
427 | + | ||
428 | +/** | ||
429 | + * showdownjs helper functions | ||
430 | + */ | ||
431 | + | ||
432 | +if (!showdown.hasOwnProperty('helper')) { | ||
433 | + showdown.helper = {}; | ||
434 | +} | ||
435 | + | ||
436 | +/** | ||
437 | + * Check if var is string | ||
438 | + * @static | ||
439 | + * @param {string} a | ||
440 | + * @returns {boolean} | ||
441 | + */ | ||
442 | +showdown.helper.isString = function isString(a) { | ||
443 | + 'use strict'; | ||
444 | + return (typeof a === 'string' || a instanceof String); | ||
445 | +}; | ||
446 | + | ||
447 | +/** | ||
448 | + * Check if var is a function | ||
449 | + * @static | ||
450 | + * @param {string} a | ||
451 | + * @returns {boolean} | ||
452 | + */ | ||
453 | +showdown.helper.isFunction = function isFunction(a) { | ||
454 | + 'use strict'; | ||
455 | + var getType = {}; | ||
456 | + return a && getType.toString.call(a) === '[object Function]'; | ||
457 | +}; | ||
458 | + | ||
459 | +/** | ||
460 | + * ForEach helper function | ||
461 | + * @static | ||
462 | + * @param {*} obj | ||
463 | + * @param {function} callback | ||
464 | + */ | ||
465 | +showdown.helper.forEach = function forEach(obj, callback) { | ||
466 | + 'use strict'; | ||
467 | + if (typeof obj.forEach === 'function') { | ||
468 | + obj.forEach(callback); | ||
469 | + } else { | ||
470 | + for (var i = 0; i < obj.length; i++) { | ||
471 | + callback(obj[i], i, obj); | ||
472 | + } | ||
473 | + } | ||
474 | +}; | ||
475 | + | ||
476 | +/** | ||
477 | + * isArray helper function | ||
478 | + * @static | ||
479 | + * @param {*} a | ||
480 | + * @returns {boolean} | ||
481 | + */ | ||
482 | +showdown.helper.isArray = function isArray(a) { | ||
483 | + 'use strict'; | ||
484 | + return a.constructor === Array; | ||
485 | +}; | ||
486 | + | ||
487 | +/** | ||
488 | + * Check if value is undefined | ||
489 | + * @static | ||
490 | + * @param {*} value The value to check. | ||
491 | + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. | ||
492 | + */ | ||
493 | +showdown.helper.isUndefined = function isUndefined(value) { | ||
494 | + 'use strict'; | ||
495 | + return typeof value === 'undefined'; | ||
496 | +}; | ||
497 | + | ||
498 | +/** | ||
499 | + * Standardidize extension name | ||
500 | + * @static | ||
501 | + * @param {string} s extension name | ||
502 | + * @returns {string} | ||
503 | + */ | ||
504 | +showdown.helper.stdExtName = function (s) { | ||
505 | + 'use strict'; | ||
506 | + return s.replace(/[_-]||\s/g, '').toLowerCase(); | ||
507 | +}; | ||
508 | + | ||
509 | +function escapeCharactersCallback(wholeMatch, m1) { | ||
510 | + 'use strict'; | ||
511 | + var charCodeToEscape = m1.charCodeAt(0); | ||
512 | + return '~E' + charCodeToEscape + 'E'; | ||
513 | +} | ||
514 | + | ||
515 | +/** | ||
516 | + * Callback used to escape characters when passing through String.replace | ||
517 | + * @static | ||
518 | + * @param {string} wholeMatch | ||
519 | + * @param {string} m1 | ||
520 | + * @returns {string} | ||
521 | + */ | ||
522 | +showdown.helper.escapeCharactersCallback = escapeCharactersCallback; | ||
523 | + | ||
524 | +/** | ||
525 | + * Escape characters in a string | ||
526 | + * @static | ||
527 | + * @param {string} text | ||
528 | + * @param {string} charsToEscape | ||
529 | + * @param {boolean} afterBackslash | ||
530 | + * @returns {XML|string|void|*} | ||
531 | + */ | ||
532 | +showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) { | ||
533 | + 'use strict'; | ||
534 | + // First we have to escape the escape characters so that | ||
535 | + // we can build a character class out of them | ||
536 | + var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])'; | ||
537 | + | ||
538 | + if (afterBackslash) { | ||
539 | + regexString = '\\\\' + regexString; | ||
540 | + } | ||
541 | + | ||
542 | + var regex = new RegExp(regexString, 'g'); | ||
543 | + text = text.replace(regex, escapeCharactersCallback); | ||
544 | + | ||
545 | + return text; | ||
546 | +}; | ||
547 | + | ||
548 | +var rgxFindMatchPos = function (str, left, right, flags) { | ||
549 | + 'use strict'; | ||
550 | + var f = flags || '', | ||
551 | + g = f.indexOf('g') > -1, | ||
552 | + x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')), | ||
553 | + l = new RegExp(left, f.replace(/g/g, '')), | ||
554 | + pos = [], | ||
555 | + t, s, m, start, end; | ||
556 | + | ||
557 | + do { | ||
558 | + t = 0; | ||
559 | + while ((m = x.exec(str))) { | ||
560 | + if (l.test(m[0])) { | ||
561 | + if (!(t++)) { | ||
562 | + s = x.lastIndex; | ||
563 | + start = s - m[0].length; | ||
564 | + } | ||
565 | + } else if (t) { | ||
566 | + if (!--t) { | ||
567 | + end = m.index + m[0].length; | ||
568 | + var obj = { | ||
569 | + left: {start: start, end: s}, | ||
570 | + match: {start: s, end: m.index}, | ||
571 | + right: {start: m.index, end: end}, | ||
572 | + wholeMatch: {start: start, end: end} | ||
573 | + }; | ||
574 | + pos.push(obj); | ||
575 | + if (!g) { | ||
576 | + return pos; | ||
577 | + } | ||
578 | + } | ||
579 | + } | ||
580 | + } | ||
581 | + } while (t && (x.lastIndex = s)); | ||
582 | + | ||
583 | + return pos; | ||
584 | +}; | ||
585 | + | ||
586 | +/** | ||
587 | + * matchRecursiveRegExp | ||
588 | + * | ||
589 | + * (c) 2007 Steven Levithan <stevenlevithan.com> | ||
590 | + * MIT License | ||
591 | + * | ||
592 | + * Accepts a string to search, a left and right format delimiter | ||
593 | + * as regex patterns, and optional regex flags. Returns an array | ||
594 | + * of matches, allowing nested instances of left/right delimiters. | ||
595 | + * Use the "g" flag to return all matches, otherwise only the | ||
596 | + * first is returned. Be careful to ensure that the left and | ||
597 | + * right format delimiters produce mutually exclusive matches. | ||
598 | + * Backreferences are not supported within the right delimiter | ||
599 | + * due to how it is internally combined with the left delimiter. | ||
600 | + * When matching strings whose format delimiters are unbalanced | ||
601 | + * to the left or right, the output is intentionally as a | ||
602 | + * conventional regex library with recursion support would | ||
603 | + * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using | ||
604 | + * "<" and ">" as the delimiters (both strings contain a single, | ||
605 | + * balanced instance of "<x>"). | ||
606 | + * | ||
607 | + * examples: | ||
608 | + * matchRecursiveRegExp("test", "\\(", "\\)") | ||
609 | + * returns: [] | ||
610 | + * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g") | ||
611 | + * returns: ["t<<e>><s>", ""] | ||
612 | + * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi") | ||
613 | + * returns: ["test"] | ||
614 | + */ | ||
615 | +showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) { | ||
616 | + 'use strict'; | ||
617 | + | ||
618 | + var matchPos = rgxFindMatchPos (str, left, right, flags), | ||
619 | + results = []; | ||
620 | + | ||
621 | + for (var i = 0; i < matchPos.length; ++i) { | ||
622 | + results.push([ | ||
623 | + str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), | ||
624 | + str.slice(matchPos[i].match.start, matchPos[i].match.end), | ||
625 | + str.slice(matchPos[i].left.start, matchPos[i].left.end), | ||
626 | + str.slice(matchPos[i].right.start, matchPos[i].right.end) | ||
627 | + ]); | ||
628 | + } | ||
629 | + return results; | ||
630 | +}; | ||
631 | + | ||
632 | +/** | ||
633 | + * | ||
634 | + * @param {string} str | ||
635 | + * @param {string|function} replacement | ||
636 | + * @param {string} left | ||
637 | + * @param {string} right | ||
638 | + * @param {string} flags | ||
639 | + * @returns {string} | ||
640 | + */ | ||
641 | +showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) { | ||
642 | + 'use strict'; | ||
643 | + | ||
644 | + if (!showdown.helper.isFunction(replacement)) { | ||
645 | + var repStr = replacement; | ||
646 | + replacement = function () { | ||
647 | + return repStr; | ||
648 | + }; | ||
649 | + } | ||
650 | + | ||
651 | + var matchPos = rgxFindMatchPos(str, left, right, flags), | ||
652 | + finalStr = str, | ||
653 | + lng = matchPos.length; | ||
654 | + | ||
655 | + if (lng > 0) { | ||
656 | + var bits = []; | ||
657 | + if (matchPos[0].wholeMatch.start !== 0) { | ||
658 | + bits.push(str.slice(0, matchPos[0].wholeMatch.start)); | ||
659 | + } | ||
660 | + for (var i = 0; i < lng; ++i) { | ||
661 | + bits.push( | ||
662 | + replacement( | ||
663 | + str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), | ||
664 | + str.slice(matchPos[i].match.start, matchPos[i].match.end), | ||
665 | + str.slice(matchPos[i].left.start, matchPos[i].left.end), | ||
666 | + str.slice(matchPos[i].right.start, matchPos[i].right.end) | ||
667 | + ) | ||
668 | + ); | ||
669 | + if (i < lng - 1) { | ||
670 | + bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start)); | ||
671 | + } | ||
672 | + } | ||
673 | + if (matchPos[lng - 1].wholeMatch.end < str.length) { | ||
674 | + bits.push(str.slice(matchPos[lng - 1].wholeMatch.end)); | ||
675 | + } | ||
676 | + finalStr = bits.join(''); | ||
677 | + } | ||
678 | + return finalStr; | ||
679 | +}; | ||
680 | + | ||
681 | +/** | ||
682 | + * POLYFILLS | ||
683 | + */ | ||
684 | +if (showdown.helper.isUndefined(console)) { | ||
685 | + console = { | ||
686 | + warn: function (msg) { | ||
687 | + 'use strict'; | ||
688 | + alert(msg); | ||
689 | + }, | ||
690 | + log: function (msg) { | ||
691 | + 'use strict'; | ||
692 | + alert(msg); | ||
693 | + }, | ||
694 | + error: function (msg) { | ||
695 | + 'use strict'; | ||
696 | + throw msg; | ||
697 | + } | ||
698 | + }; | ||
699 | +} | ||
700 | + | ||
701 | +/** | ||
702 | + * Created by Estevao on 31-05-2015. | ||
703 | + */ | ||
704 | + | ||
705 | +/** | ||
706 | + * Showdown Converter class | ||
707 | + * @class | ||
708 | + * @param {object} [converterOptions] | ||
709 | + * @returns {Converter} | ||
710 | + */ | ||
711 | +showdown.Converter = function (converterOptions) { | ||
712 | + 'use strict'; | ||
713 | + | ||
714 | + var | ||
715 | + /** | ||
716 | + * Options used by this converter | ||
717 | + * @private | ||
718 | + * @type {{}} | ||
719 | + */ | ||
720 | + options = {}, | ||
721 | + | ||
722 | + /** | ||
723 | + * Language extensions used by this converter | ||
724 | + * @private | ||
725 | + * @type {Array} | ||
726 | + */ | ||
727 | + langExtensions = [], | ||
728 | + | ||
729 | + /** | ||
730 | + * Output modifiers extensions used by this converter | ||
731 | + * @private | ||
732 | + * @type {Array} | ||
733 | + */ | ||
734 | + outputModifiers = [], | ||
735 | + | ||
736 | + /** | ||
737 | + * Event listeners | ||
738 | + * @private | ||
739 | + * @type {{}} | ||
740 | + */ | ||
741 | + listeners = {}; | ||
742 | + | ||
743 | + _constructor(); | ||
744 | + | ||
745 | + /** | ||
746 | + * Converter constructor | ||
747 | + * @private | ||
748 | + */ | ||
749 | + function _constructor() { | ||
750 | + converterOptions = converterOptions || {}; | ||
751 | + | ||
752 | + for (var gOpt in globalOptions) { | ||
753 | + if (globalOptions.hasOwnProperty(gOpt)) { | ||
754 | + options[gOpt] = globalOptions[gOpt]; | ||
755 | + } | ||
756 | + } | ||
757 | + | ||
758 | + // Merge options | ||
759 | + if (typeof converterOptions === 'object') { | ||
760 | + for (var opt in converterOptions) { | ||
761 | + if (converterOptions.hasOwnProperty(opt)) { | ||
762 | + options[opt] = converterOptions[opt]; | ||
763 | + } | ||
764 | + } | ||
765 | + } else { | ||
766 | + throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + | ||
767 | + ' was passed instead.'); | ||
768 | + } | ||
769 | + | ||
770 | + if (options.extensions) { | ||
771 | + showdown.helper.forEach(options.extensions, _parseExtension); | ||
772 | + } | ||
773 | + } | ||
774 | + | ||
775 | + /** | ||
776 | + * Parse extension | ||
777 | + * @param {*} ext | ||
778 | + * @param {string} [name=''] | ||
779 | + * @private | ||
780 | + */ | ||
781 | + function _parseExtension(ext, name) { | ||
782 | + | ||
783 | + name = name || null; | ||
784 | + // If it's a string, the extension was previously loaded | ||
785 | + if (showdown.helper.isString(ext)) { | ||
786 | + ext = showdown.helper.stdExtName(ext); | ||
787 | + name = ext; | ||
788 | + | ||
789 | + // LEGACY_SUPPORT CODE | ||
790 | + if (showdown.extensions[ext]) { | ||
791 | + console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' + | ||
792 | + 'Please inform the developer that the extension should be updated!'); | ||
793 | + legacyExtensionLoading(showdown.extensions[ext], ext); | ||
794 | + return; | ||
795 | + // END LEGACY SUPPORT CODE | ||
796 | + | ||
797 | + } else if (!showdown.helper.isUndefined(extensions[ext])) { | ||
798 | + ext = extensions[ext]; | ||
799 | + | ||
800 | + } else { | ||
801 | + throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.'); | ||
802 | + } | ||
803 | + } | ||
804 | + | ||
805 | + if (typeof ext === 'function') { | ||
806 | + ext = ext(); | ||
807 | + } | ||
808 | + | ||
809 | + if (!showdown.helper.isArray(ext)) { | ||
810 | + ext = [ext]; | ||
811 | + } | ||
812 | + | ||
813 | + var validExt = validate(ext, name); | ||
814 | + if (!validExt.valid) { | ||
815 | + throw Error(validExt.error); | ||
816 | + } | ||
817 | + | ||
818 | + for (var i = 0; i < ext.length; ++i) { | ||
819 | + switch (ext[i].type) { | ||
820 | + | ||
821 | + case 'lang': | ||
822 | + langExtensions.push(ext[i]); | ||
823 | + break; | ||
824 | + | ||
825 | + case 'output': | ||
826 | + outputModifiers.push(ext[i]); | ||
827 | + break; | ||
828 | + } | ||
829 | + if (ext[i].hasOwnProperty(listeners)) { | ||
830 | + for (var ln in ext[i].listeners) { | ||
831 | + if (ext[i].listeners.hasOwnProperty(ln)) { | ||
832 | + listen(ln, ext[i].listeners[ln]); | ||
833 | + } | ||
834 | + } | ||
835 | + } | ||
836 | + } | ||
837 | + | ||
838 | + } | ||
839 | + | ||
840 | + /** | ||
841 | + * LEGACY_SUPPORT | ||
842 | + * @param {*} ext | ||
843 | + * @param {string} name | ||
844 | + */ | ||
845 | + function legacyExtensionLoading(ext, name) { | ||
846 | + if (typeof ext === 'function') { | ||
847 | + ext = ext(new showdown.Converter()); | ||
848 | + } | ||
849 | + if (!showdown.helper.isArray(ext)) { | ||
850 | + ext = [ext]; | ||
851 | + } | ||
852 | + var valid = validate(ext, name); | ||
853 | + | ||
854 | + if (!valid.valid) { | ||
855 | + throw Error(valid.error); | ||
856 | + } | ||
857 | + | ||
858 | + for (var i = 0; i < ext.length; ++i) { | ||
859 | + switch (ext[i].type) { | ||
860 | + case 'lang': | ||
861 | + langExtensions.push(ext[i]); | ||
862 | + break; | ||
863 | + case 'output': | ||
864 | + outputModifiers.push(ext[i]); | ||
865 | + break; | ||
866 | + default:// should never reach here | ||
867 | + throw Error('Extension loader error: Type unrecognized!!!'); | ||
868 | + } | ||
869 | + } | ||
870 | + } | ||
871 | + | ||
872 | + /** | ||
873 | + * Listen to an event | ||
874 | + * @param {string} name | ||
875 | + * @param {function} callback | ||
876 | + */ | ||
877 | + function listen(name, callback) { | ||
878 | + if (!showdown.helper.isString(name)) { | ||
879 | + throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given'); | ||
880 | + } | ||
881 | + | ||
882 | + if (typeof callback !== 'function') { | ||
883 | + throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given'); | ||
884 | + } | ||
885 | + | ||
886 | + if (!listeners.hasOwnProperty(name)) { | ||
887 | + listeners[name] = []; | ||
888 | + } | ||
889 | + listeners[name].push(callback); | ||
890 | + } | ||
891 | + | ||
892 | + function rTrimInputText(text) { | ||
893 | + var rsp = text.match(/^\s*/)[0].length, | ||
894 | + rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm'); | ||
895 | + return text.replace(rgx, ''); | ||
896 | + } | ||
897 | + | ||
898 | + /** | ||
899 | + * Dispatch an event | ||
900 | + * @private | ||
901 | + * @param {string} evtName Event name | ||
902 | + * @param {string} text Text | ||
903 | + * @param {{}} options Converter Options | ||
904 | + * @param {{}} globals | ||
905 | + * @returns {string} | ||
906 | + */ | ||
907 | + this._dispatch = function dispatch (evtName, text, options, globals) { | ||
908 | + if (listeners.hasOwnProperty(evtName)) { | ||
909 | + for (var ei = 0; ei < listeners[evtName].length; ++ei) { | ||
910 | + var nText = listeners[evtName][ei](evtName, text, this, options, globals); | ||
911 | + if (nText && typeof nText !== 'undefined') { | ||
912 | + text = nText; | ||
913 | + } | ||
914 | + } | ||
915 | + } | ||
916 | + return text; | ||
917 | + }; | ||
918 | + | ||
919 | + /** | ||
920 | + * Listen to an event | ||
921 | + * @param {string} name | ||
922 | + * @param {function} callback | ||
923 | + * @returns {showdown.Converter} | ||
924 | + */ | ||
925 | + this.listen = function (name, callback) { | ||
926 | + listen(name, callback); | ||
927 | + return this; | ||
928 | + }; | ||
929 | + | ||
930 | + /** | ||
931 | + * Converts a markdown string into HTML | ||
932 | + * @param {string} text | ||
933 | + * @returns {*} | ||
934 | + */ | ||
935 | + this.makeHtml = function (text) { | ||
936 | + //check if text is not falsy | ||
937 | + if (!text) { | ||
938 | + return text; | ||
939 | + } | ||
940 | + | ||
941 | + var globals = { | ||
942 | + gHtmlBlocks: [], | ||
943 | + gHtmlMdBlocks: [], | ||
944 | + gHtmlSpans: [], | ||
945 | + gUrls: {}, | ||
946 | + gTitles: {}, | ||
947 | + gDimensions: {}, | ||
948 | + gListLevel: 0, | ||
949 | + hashLinkCounts: {}, | ||
950 | + langExtensions: langExtensions, | ||
951 | + outputModifiers: outputModifiers, | ||
952 | + converter: this, | ||
953 | + ghCodeBlocks: [] | ||
954 | + }; | ||
955 | + | ||
956 | + // attacklab: Replace ~ with ~T | ||
957 | + // This lets us use tilde as an escape char to avoid md5 hashes | ||
958 | + // The choice of character is arbitrary; anything that isn't | ||
959 | + // magic in Markdown will work. | ||
960 | + text = text.replace(/~/g, '~T'); | ||
961 | + | ||
962 | + // attacklab: Replace $ with ~D | ||
963 | + // RegExp interprets $ as a special character | ||
964 | + // when it's in a replacement string | ||
965 | + text = text.replace(/\$/g, '~D'); | ||
966 | + | ||
967 | + // Standardize line endings | ||
968 | + text = text.replace(/\r\n/g, '\n'); // DOS to Unix | ||
969 | + text = text.replace(/\r/g, '\n'); // Mac to Unix | ||
970 | + | ||
971 | + if (options.smartIndentationFix) { | ||
972 | + text = rTrimInputText(text); | ||
973 | + } | ||
974 | + | ||
975 | + // Make sure text begins and ends with a couple of newlines: | ||
976 | + //text = '\n\n' + text + '\n\n'; | ||
977 | + text = text; | ||
978 | + // detab | ||
979 | + text = showdown.subParser('detab')(text, options, globals); | ||
980 | + | ||
981 | + // stripBlankLines | ||
982 | + text = showdown.subParser('stripBlankLines')(text, options, globals); | ||
983 | + | ||
984 | + //run languageExtensions | ||
985 | + showdown.helper.forEach(langExtensions, function (ext) { | ||
986 | + text = showdown.subParser('runExtension')(ext, text, options, globals); | ||
987 | + }); | ||
988 | + | ||
989 | + // run the sub parsers | ||
990 | + text = showdown.subParser('hashPreCodeTags')(text, options, globals); | ||
991 | + text = showdown.subParser('githubCodeBlocks')(text, options, globals); | ||
992 | + text = showdown.subParser('hashHTMLBlocks')(text, options, globals); | ||
993 | + text = showdown.subParser('hashHTMLSpans')(text, options, globals); | ||
994 | + text = showdown.subParser('stripLinkDefinitions')(text, options, globals); | ||
995 | + text = showdown.subParser('blockGamut')(text, options, globals); | ||
996 | + text = showdown.subParser('unhashHTMLSpans')(text, options, globals); | ||
997 | + text = showdown.subParser('unescapeSpecialChars')(text, options, globals); | ||
998 | + | ||
999 | + // attacklab: Restore dollar signs | ||
1000 | + text = text.replace(/~D/g, '$$'); | ||
1001 | + | ||
1002 | + // attacklab: Restore tildes | ||
1003 | + text = text.replace(/~T/g, '~'); | ||
1004 | + | ||
1005 | + // Run output modifiers | ||
1006 | + showdown.helper.forEach(outputModifiers, function (ext) { | ||
1007 | + text = showdown.subParser('runExtension')(ext, text, options, globals); | ||
1008 | + }); | ||
1009 | + return text; | ||
1010 | + }; | ||
1011 | + | ||
1012 | + /** | ||
1013 | + * Set an option of this Converter instance | ||
1014 | + * @param {string} key | ||
1015 | + * @param {*} value | ||
1016 | + */ | ||
1017 | + this.setOption = function (key, value) { | ||
1018 | + options[key] = value; | ||
1019 | + }; | ||
1020 | + | ||
1021 | + /** | ||
1022 | + * Get the option of this Converter instance | ||
1023 | + * @param {string} key | ||
1024 | + * @returns {*} | ||
1025 | + */ | ||
1026 | + this.getOption = function (key) { | ||
1027 | + return options[key]; | ||
1028 | + }; | ||
1029 | + | ||
1030 | + /** | ||
1031 | + * Get the options of this Converter instance | ||
1032 | + * @returns {{}} | ||
1033 | + */ | ||
1034 | + this.getOptions = function () { | ||
1035 | + return options; | ||
1036 | + }; | ||
1037 | + | ||
1038 | + /** | ||
1039 | + * Add extension to THIS converter | ||
1040 | + * @param {{}} extension | ||
1041 | + * @param {string} [name=null] | ||
1042 | + */ | ||
1043 | + this.addExtension = function (extension, name) { | ||
1044 | + name = name || null; | ||
1045 | + _parseExtension(extension, name); | ||
1046 | + }; | ||
1047 | + | ||
1048 | + /** | ||
1049 | + * Use a global registered extension with THIS converter | ||
1050 | + * @param {string} extensionName Name of the previously registered extension | ||
1051 | + */ | ||
1052 | + this.useExtension = function (extensionName) { | ||
1053 | + _parseExtension(extensionName); | ||
1054 | + }; | ||
1055 | + | ||
1056 | + /** | ||
1057 | + * Set the flavor THIS converter should use | ||
1058 | + * @param {string} name | ||
1059 | + */ | ||
1060 | + this.setFlavor = function (name) { | ||
1061 | + if (flavor.hasOwnProperty(name)) { | ||
1062 | + var preset = flavor[name]; | ||
1063 | + for (var option in preset) { | ||
1064 | + if (preset.hasOwnProperty(option)) { | ||
1065 | + options[option] = preset[option]; | ||
1066 | + } | ||
1067 | + } | ||
1068 | + } | ||
1069 | + }; | ||
1070 | + | ||
1071 | + /** | ||
1072 | + * Remove an extension from THIS converter. | ||
1073 | + * Note: This is a costly operation. It's better to initialize a new converter | ||
1074 | + * and specify the extensions you wish to use | ||
1075 | + * @param {Array} extension | ||
1076 | + */ | ||
1077 | + this.removeExtension = function (extension) { | ||
1078 | + if (!showdown.helper.isArray(extension)) { | ||
1079 | + extension = [extension]; | ||
1080 | + } | ||
1081 | + for (var a = 0; a < extension.length; ++a) { | ||
1082 | + var ext = extension[a]; | ||
1083 | + for (var i = 0; i < langExtensions.length; ++i) { | ||
1084 | + if (langExtensions[i] === ext) { | ||
1085 | + langExtensions[i].splice(i, 1); | ||
1086 | + } | ||
1087 | + } | ||
1088 | + for (var ii = 0; ii < outputModifiers.length; ++i) { | ||
1089 | + if (outputModifiers[ii] === ext) { | ||
1090 | + outputModifiers[ii].splice(i, 1); | ||
1091 | + } | ||
1092 | + } | ||
1093 | + } | ||
1094 | + }; | ||
1095 | + | ||
1096 | + /** | ||
1097 | + * Get all extension of THIS converter | ||
1098 | + * @returns {{language: Array, output: Array}} | ||
1099 | + */ | ||
1100 | + this.getAllExtensions = function () { | ||
1101 | + return { | ||
1102 | + language: langExtensions, | ||
1103 | + output: outputModifiers | ||
1104 | + }; | ||
1105 | + }; | ||
1106 | +}; | ||
1107 | + | ||
1108 | +/** | ||
1109 | + * Turn Markdown link shortcuts into XHTML <a> tags. | ||
1110 | + */ | ||
1111 | +showdown.subParser('anchors', function (text, options, globals) { | ||
1112 | + 'use strict'; | ||
1113 | + | ||
1114 | + text = globals.converter._dispatch('anchors.before', text, options, globals); | ||
1115 | + | ||
1116 | + var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { | ||
1117 | + if (showdown.helper.isUndefined(m7)) { | ||
1118 | + m7 = ''; | ||
1119 | + } | ||
1120 | + wholeMatch = m1; | ||
1121 | + var linkText = m2, | ||
1122 | + linkId = m3.toLowerCase(), | ||
1123 | + url = m4, | ||
1124 | + title = m7; | ||
1125 | + | ||
1126 | + if (!url) { | ||
1127 | + if (!linkId) { | ||
1128 | + // lower-case and turn embedded newlines into spaces | ||
1129 | + linkId = linkText.toLowerCase().replace(/ ?\n/g, ' '); | ||
1130 | + } | ||
1131 | + url = '#' + linkId; | ||
1132 | + | ||
1133 | + if (!showdown.helper.isUndefined(globals.gUrls[linkId])) { | ||
1134 | + url = globals.gUrls[linkId]; | ||
1135 | + if (!showdown.helper.isUndefined(globals.gTitles[linkId])) { | ||
1136 | + title = globals.gTitles[linkId]; | ||
1137 | + } | ||
1138 | + } else { | ||
1139 | + if (wholeMatch.search(/\(\s*\)$/m) > -1) { | ||
1140 | + // Special case for explicit empty url | ||
1141 | + url = ''; | ||
1142 | + } else { | ||
1143 | + return wholeMatch; | ||
1144 | + } | ||
1145 | + } | ||
1146 | + } | ||
1147 | + | ||
1148 | + url = showdown.helper.escapeCharacters(url, '*_', false); | ||
1149 | + var result = '<a href="' + url + '"'; | ||
1150 | + | ||
1151 | + if (title !== '' && title !== null) { | ||
1152 | + title = title.replace(/"/g, '"'); | ||
1153 | + title = showdown.helper.escapeCharacters(title, '*_', false); | ||
1154 | + result += ' title="' + title + '"'; | ||
1155 | + } | ||
1156 | + | ||
1157 | + result += '>' + linkText + '</a>'; | ||
1158 | + | ||
1159 | + return result; | ||
1160 | + }; | ||
1161 | + | ||
1162 | + // First, handle reference-style links: [link text] [id] | ||
1163 | + /* | ||
1164 | + text = text.replace(/ | ||
1165 | + ( // wrap whole match in $1 | ||
1166 | + \[ | ||
1167 | + ( | ||
1168 | + (?: | ||
1169 | + \[[^\]]*\] // allow brackets nested one level | ||
1170 | + | | ||
1171 | + [^\[] // or anything else | ||
1172 | + )* | ||
1173 | + ) | ||
1174 | + \] | ||
1175 | + | ||
1176 | + [ ]? // one optional space | ||
1177 | + (?:\n[ ]*)? // one optional newline followed by spaces | ||
1178 | + | ||
1179 | + \[ | ||
1180 | + (.*?) // id = $3 | ||
1181 | + \] | ||
1182 | + )()()()() // pad remaining backreferences | ||
1183 | + /g,_DoAnchors_callback); | ||
1184 | + */ | ||
1185 | + text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag); | ||
1186 | + | ||
1187 | + // | ||
1188 | + // Next, inline-style links: [link text](url "optional title") | ||
1189 | + // | ||
1190 | + | ||
1191 | + /* | ||
1192 | + text = text.replace(/ | ||
1193 | + ( // wrap whole match in $1 | ||
1194 | + \[ | ||
1195 | + ( | ||
1196 | + (?: | ||
1197 | + \[[^\]]*\] // allow brackets nested one level | ||
1198 | + | | ||
1199 | + [^\[\]] // or anything else | ||
1200 | + ) | ||
1201 | + ) | ||
1202 | + \] | ||
1203 | + \( // literal paren | ||
1204 | + [ \t]* | ||
1205 | + () // no id, so leave $3 empty | ||
1206 | + <?(.*?)>? // href = $4 | ||
1207 | + [ \t]* | ||
1208 | + ( // $5 | ||
1209 | + (['"]) // quote char = $6 | ||
1210 | + (.*?) // Title = $7 | ||
1211 | + \6 // matching quote | ||
1212 | + [ \t]* // ignore any spaces/tabs between closing quote and ) | ||
1213 | + )? // title is optional | ||
1214 | + \) | ||
1215 | + ) | ||
1216 | + /g,writeAnchorTag); | ||
1217 | + */ | ||
1218 | + text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, | ||
1219 | + writeAnchorTag); | ||
1220 | + | ||
1221 | + // | ||
1222 | + // Last, handle reference-style shortcuts: [link text] | ||
1223 | + // These must come last in case you've also got [link test][1] | ||
1224 | + // or [link test](/foo) | ||
1225 | + // | ||
1226 | + | ||
1227 | + /* | ||
1228 | + text = text.replace(/ | ||
1229 | + ( // wrap whole match in $1 | ||
1230 | + \[ | ||
1231 | + ([^\[\]]+) // link text = $2; can't contain '[' or ']' | ||
1232 | + \] | ||
1233 | + )()()()()() // pad rest of backreferences | ||
1234 | + /g, writeAnchorTag); | ||
1235 | + */ | ||
1236 | + text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag); | ||
1237 | + | ||
1238 | + text = globals.converter._dispatch('anchors.after', text, options, globals); | ||
1239 | + return text; | ||
1240 | +}); | ||
1241 | + | ||
1242 | +showdown.subParser('autoLinks', function (text, options, globals) { | ||
1243 | + 'use strict'; | ||
1244 | + | ||
1245 | + text = globals.converter._dispatch('autoLinks.before', text, options, globals); | ||
1246 | + | ||
1247 | + var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi, | ||
1248 | + delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi, | ||
1249 | + simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi, | ||
1250 | + delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; | ||
1251 | + | ||
1252 | + text = text.replace(delimUrlRegex, replaceLink); | ||
1253 | + text = text.replace(delimMailRegex, replaceMail); | ||
1254 | + // simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, | ||
1255 | + // Email addresses: <address@domain.foo> | ||
1256 | + | ||
1257 | + if (options.simplifiedAutoLink) { | ||
1258 | + text = text.replace(simpleURLRegex, replaceLink); | ||
1259 | + text = text.replace(simpleMailRegex, replaceMail); | ||
1260 | + } | ||
1261 | + | ||
1262 | + function replaceLink(wm, link) { | ||
1263 | + var lnkTxt = link; | ||
1264 | + if (/^www\./i.test(link)) { | ||
1265 | + link = link.replace(/^www\./i, 'http://www.'); | ||
1266 | + } | ||
1267 | + return '<a href="' + link + '">' + lnkTxt + '</a>'; | ||
1268 | + } | ||
1269 | + | ||
1270 | + function replaceMail(wholeMatch, m1) { | ||
1271 | + var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1); | ||
1272 | + return showdown.subParser('encodeEmailAddress')(unescapedStr); | ||
1273 | + } | ||
1274 | + | ||
1275 | + text = globals.converter._dispatch('autoLinks.after', text, options, globals); | ||
1276 | + | ||
1277 | + return text; | ||
1278 | +}); | ||
1279 | + | ||
1280 | +/** | ||
1281 | + * These are all the transformations that form block-level | ||
1282 | + * tags like paragraphs, headers, and list items. | ||
1283 | + */ | ||
1284 | +showdown.subParser('blockGamut', function (text, options, globals) { | ||
1285 | + 'use strict'; | ||
1286 | + | ||
1287 | + text = globals.converter._dispatch('blockGamut.before', text, options, globals); | ||
1288 | + | ||
1289 | + // we parse blockquotes first so that we can have headings and hrs | ||
1290 | + // inside blockquotes | ||
1291 | + text = showdown.subParser('blockQuotes')(text, options, globals); | ||
1292 | + text = showdown.subParser('headers')(text, options, globals); | ||
1293 | + | ||
1294 | + // Do Horizontal Rules: | ||
1295 | + var key = showdown.subParser('hashBlock')('<hr />', options, globals); | ||
1296 | + text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key); | ||
1297 | + text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key); | ||
1298 | + text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key); | ||
1299 | + | ||
1300 | + text = showdown.subParser('lists')(text, options, globals); | ||
1301 | + text = showdown.subParser('codeBlocks')(text, options, globals); | ||
1302 | + text = showdown.subParser('tables')(text, options, globals); | ||
1303 | + | ||
1304 | + // We already ran _HashHTMLBlocks() before, in Markdown(), but that | ||
1305 | + // was to escape raw HTML in the original Markdown source. This time, | ||
1306 | + // we're escaping the markup we've just created, so that we don't wrap | ||
1307 | + // <p> tags around block-level tags. | ||
1308 | + text = showdown.subParser('hashHTMLBlocks')(text, options, globals); | ||
1309 | + text = showdown.subParser('paragraphs')(text, options, globals); | ||
1310 | + | ||
1311 | + text = globals.converter._dispatch('blockGamut.after', text, options, globals); | ||
1312 | + | ||
1313 | + return text; | ||
1314 | +}); | ||
1315 | + | ||
1316 | +showdown.subParser('blockQuotes', function (text, options, globals) { | ||
1317 | + 'use strict'; | ||
1318 | + | ||
1319 | + text = globals.converter._dispatch('blockQuotes.before', text, options, globals); | ||
1320 | + /* | ||
1321 | + text = text.replace(/ | ||
1322 | + ( // Wrap whole match in $1 | ||
1323 | + ( | ||
1324 | + ^[ \t]*>[ \t]? // '>' at the start of a line | ||
1325 | + .+\n // rest of the first line | ||
1326 | + (.+\n)* // subsequent consecutive lines | ||
1327 | + \n* // blanks | ||
1328 | + )+ | ||
1329 | + ) | ||
1330 | + /gm, function(){...}); | ||
1331 | + */ | ||
1332 | + | ||
1333 | + text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) { | ||
1334 | + var bq = m1; | ||
1335 | + | ||
1336 | + // attacklab: hack around Konqueror 3.5.4 bug: | ||
1337 | + // "----------bug".replace(/^-/g,"") == "bug" | ||
1338 | + bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting | ||
1339 | + | ||
1340 | + // attacklab: clean up hack | ||
1341 | + bq = bq.replace(/~0/g, ''); | ||
1342 | + | ||
1343 | + bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines | ||
1344 | + bq = showdown.subParser('githubCodeBlocks')(bq, options, globals); | ||
1345 | + bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse | ||
1346 | + | ||
1347 | + bq = bq.replace(/(^|\n)/g, '$1 '); | ||
1348 | + // These leading spaces screw with <pre> content, so we need to fix that: | ||
1349 | + bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) { | ||
1350 | + var pre = m1; | ||
1351 | + // attacklab: hack around Konqueror 3.5.4 bug: | ||
1352 | + pre = pre.replace(/^ /mg, '~0'); | ||
1353 | + pre = pre.replace(/~0/g, ''); | ||
1354 | + return pre; | ||
1355 | + }); | ||
1356 | + | ||
1357 | + return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals); | ||
1358 | + }); | ||
1359 | + | ||
1360 | + text = globals.converter._dispatch('blockQuotes.after', text, options, globals); | ||
1361 | + return text; | ||
1362 | +}); | ||
1363 | + | ||
1364 | +/** | ||
1365 | + * Process Markdown `<pre><code>` blocks. | ||
1366 | + */ | ||
1367 | +showdown.subParser('codeBlocks', function (text, options, globals) { | ||
1368 | + 'use strict'; | ||
1369 | + | ||
1370 | + text = globals.converter._dispatch('codeBlocks.before', text, options, globals); | ||
1371 | + /* | ||
1372 | + text = text.replace(text, | ||
1373 | + /(?:\n\n|^) | ||
1374 | + ( // $1 = the code block -- one or more lines, starting with a space/tab | ||
1375 | + (?: | ||
1376 | + (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width | ||
1377 | + .*\n+ | ||
1378 | + )+ | ||
1379 | + ) | ||
1380 | + (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width | ||
1381 | + /g,function(){...}); | ||
1382 | + */ | ||
1383 | + | ||
1384 | + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug | ||
1385 | + text += '~0'; | ||
1386 | + | ||
1387 | + var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g; | ||
1388 | + text = text.replace(pattern, function (wholeMatch, m1, m2) { | ||
1389 | + var codeblock = m1, | ||
1390 | + nextChar = m2, | ||
1391 | + end = '\n'; | ||
1392 | + | ||
1393 | + codeblock = showdown.subParser('outdent')(codeblock); | ||
1394 | + codeblock = showdown.subParser('encodeCode')(codeblock); | ||
1395 | + codeblock = showdown.subParser('detab')(codeblock); | ||
1396 | + codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines | ||
1397 | + codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines | ||
1398 | + | ||
1399 | + if (options.omitExtraWLInCodeBlocks) { | ||
1400 | + end = ''; | ||
1401 | + } | ||
1402 | + | ||
1403 | + codeblock = '<pre><code>' + codeblock + end + '</code></pre>'; | ||
1404 | + | ||
1405 | + return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar; | ||
1406 | + }); | ||
1407 | + | ||
1408 | + // attacklab: strip sentinel | ||
1409 | + text = text.replace(/~0/, ''); | ||
1410 | + | ||
1411 | + text = globals.converter._dispatch('codeBlocks.after', text, options, globals); | ||
1412 | + return text; | ||
1413 | +}); | ||
1414 | + | ||
1415 | +/** | ||
1416 | + * | ||
1417 | + * * Backtick quotes are used for <code></code> spans. | ||
1418 | + * | ||
1419 | + * * You can use multiple backticks as the delimiters if you want to | ||
1420 | + * include literal backticks in the code span. So, this input: | ||
1421 | + * | ||
1422 | + * Just type ``foo `bar` baz`` at the prompt. | ||
1423 | + * | ||
1424 | + * Will translate to: | ||
1425 | + * | ||
1426 | + * <p>Just type <code>foo `bar` baz</code> at the prompt.</p> | ||
1427 | + * | ||
1428 | + * There's no arbitrary limit to the number of backticks you | ||
1429 | + * can use as delimters. If you need three consecutive backticks | ||
1430 | + * in your code, use four for delimiters, etc. | ||
1431 | + * | ||
1432 | + * * You can use spaces to get literal backticks at the edges: | ||
1433 | + * | ||
1434 | + * ... type `` `bar` `` ... | ||
1435 | + * | ||
1436 | + * Turns to: | ||
1437 | + * | ||
1438 | + * ... type <code>`bar`</code> ... | ||
1439 | + */ | ||
1440 | +showdown.subParser('codeSpans', function (text, options, globals) { | ||
1441 | + 'use strict'; | ||
1442 | + | ||
1443 | + text = globals.converter._dispatch('codeSpans.before', text, options, globals); | ||
1444 | + | ||
1445 | + /* | ||
1446 | + text = text.replace(/ | ||
1447 | + (^|[^\\]) // Character before opening ` can't be a backslash | ||
1448 | + (`+) // $2 = Opening run of ` | ||
1449 | + ( // $3 = The code block | ||
1450 | + [^\r]*? | ||
1451 | + [^`] // attacklab: work around lack of lookbehind | ||
1452 | + ) | ||
1453 | + \2 // Matching closer | ||
1454 | + (?!`) | ||
1455 | + /gm, function(){...}); | ||
1456 | + */ | ||
1457 | + | ||
1458 | + if (typeof(text) === 'undefined') { | ||
1459 | + text = ''; | ||
1460 | + } | ||
1461 | + text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, | ||
1462 | + function (wholeMatch, m1, m2, m3) { | ||
1463 | + var c = m3; | ||
1464 | + c = c.replace(/^([ \t]*)/g, ''); // leading whitespace | ||
1465 | + c = c.replace(/[ \t]*$/g, ''); // trailing whitespace | ||
1466 | + c = showdown.subParser('encodeCode')(c); | ||
1467 | + return m1 + '<code>' + c + '</code>'; | ||
1468 | + } | ||
1469 | + ); | ||
1470 | + | ||
1471 | + text = globals.converter._dispatch('codeSpans.after', text, options, globals); | ||
1472 | + return text; | ||
1473 | +}); | ||
1474 | + | ||
1475 | +/** | ||
1476 | + * Convert all tabs to spaces | ||
1477 | + */ | ||
1478 | +showdown.subParser('detab', function (text) { | ||
1479 | + 'use strict'; | ||
1480 | + | ||
1481 | + // expand first n-1 tabs | ||
1482 | + text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width | ||
1483 | + | ||
1484 | + // replace the nth with two sentinels | ||
1485 | + text = text.replace(/\t/g, '~A~B'); | ||
1486 | + | ||
1487 | + // use the sentinel to anchor our regex so it doesn't explode | ||
1488 | + text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) { | ||
1489 | + var leadingText = m1, | ||
1490 | + numSpaces = 4 - leadingText.length % 4; // g_tab_width | ||
1491 | + | ||
1492 | + // there *must* be a better way to do this: | ||
1493 | + for (var i = 0; i < numSpaces; i++) { | ||
1494 | + leadingText += ' '; | ||
1495 | + } | ||
1496 | + | ||
1497 | + return leadingText; | ||
1498 | + }); | ||
1499 | + | ||
1500 | + // clean up sentinels | ||
1501 | + text = text.replace(/~A/g, ' '); // g_tab_width | ||
1502 | + text = text.replace(/~B/g, ''); | ||
1503 | + | ||
1504 | + return text; | ||
1505 | + | ||
1506 | +}); | ||
1507 | + | ||
1508 | +/** | ||
1509 | + * Smart processing for ampersands and angle brackets that need to be encoded. | ||
1510 | + */ | ||
1511 | +showdown.subParser('encodeAmpsAndAngles', function (text) { | ||
1512 | + 'use strict'; | ||
1513 | + // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: | ||
1514 | + // http://bumppo.net/projects/amputator/ | ||
1515 | + text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&'); | ||
1516 | + | ||
1517 | + // Encode naked <'s | ||
1518 | + text = text.replace(/<(?![a-z\/?\$!])/gi, '<'); | ||
1519 | + | ||
1520 | + return text; | ||
1521 | +}); | ||
1522 | + | ||
1523 | +/** | ||
1524 | + * Returns the string, with after processing the following backslash escape sequences. | ||
1525 | + * | ||
1526 | + * attacklab: The polite way to do this is with the new escapeCharacters() function: | ||
1527 | + * | ||
1528 | + * text = escapeCharacters(text,"\\",true); | ||
1529 | + * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); | ||
1530 | + * | ||
1531 | + * ...but we're sidestepping its use of the (slow) RegExp constructor | ||
1532 | + * as an optimization for Firefox. This function gets called a LOT. | ||
1533 | + */ | ||
1534 | +showdown.subParser('encodeBackslashEscapes', function (text) { | ||
1535 | + 'use strict'; | ||
1536 | + text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback); | ||
1537 | + text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback); | ||
1538 | + return text; | ||
1539 | +}); | ||
1540 | + | ||
1541 | +/** | ||
1542 | + * Encode/escape certain characters inside Markdown code runs. | ||
1543 | + * The point is that in code, these characters are literals, | ||
1544 | + * and lose their special Markdown meanings. | ||
1545 | + */ | ||
1546 | +showdown.subParser('encodeCode', function (text) { | ||
1547 | + 'use strict'; | ||
1548 | + | ||
1549 | + // Encode all ampersands; HTML entities are not | ||
1550 | + // entities within a Markdown code span. | ||
1551 | + text = text.replace(/&/g, '&'); | ||
1552 | + | ||
1553 | + // Do the angle bracket song and dance: | ||
1554 | + text = text.replace(/</g, '<'); | ||
1555 | + text = text.replace(/>/g, '>'); | ||
1556 | + | ||
1557 | + // Now, escape characters that are magic in Markdown: | ||
1558 | + text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false); | ||
1559 | + | ||
1560 | + // jj the line above breaks this: | ||
1561 | + //--- | ||
1562 | + //* Item | ||
1563 | + // 1. Subitem | ||
1564 | + // special char: * | ||
1565 | + // --- | ||
1566 | + | ||
1567 | + return text; | ||
1568 | +}); | ||
1569 | + | ||
1570 | +/** | ||
1571 | + * Input: an email address, e.g. "foo@example.com" | ||
1572 | + * | ||
1573 | + * Output: the email address as a mailto link, with each character | ||
1574 | + * of the address encoded as either a decimal or hex entity, in | ||
1575 | + * the hopes of foiling most address harvesting spam bots. E.g.: | ||
1576 | + * | ||
1577 | + * <a href="mailto:foo@e | ||
1578 | + * xample.com">foo | ||
1579 | + * @example.com</a> | ||
1580 | + * | ||
1581 | + * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk | ||
1582 | + * mailing list: <http://tinyurl.com/yu7ue> | ||
1583 | + * | ||
1584 | + */ | ||
1585 | +showdown.subParser('encodeEmailAddress', function (addr) { | ||
1586 | + 'use strict'; | ||
1587 | + | ||
1588 | + var encode = [ | ||
1589 | + function (ch) { | ||
1590 | + return '&#' + ch.charCodeAt(0) + ';'; | ||
1591 | + }, | ||
1592 | + function (ch) { | ||
1593 | + return '&#x' + ch.charCodeAt(0).toString(16) + ';'; | ||
1594 | + }, | ||
1595 | + function (ch) { | ||
1596 | + return ch; | ||
1597 | + } | ||
1598 | + ]; | ||
1599 | + | ||
1600 | + addr = 'mailto:' + addr; | ||
1601 | + | ||
1602 | + addr = addr.replace(/./g, function (ch) { | ||
1603 | + if (ch === '@') { | ||
1604 | + // this *must* be encoded. I insist. | ||
1605 | + ch = encode[Math.floor(Math.random() * 2)](ch); | ||
1606 | + } else if (ch !== ':') { | ||
1607 | + // leave ':' alone (to spot mailto: later) | ||
1608 | + var r = Math.random(); | ||
1609 | + // roughly 10% raw, 45% hex, 45% dec | ||
1610 | + ch = ( | ||
1611 | + r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) | ||
1612 | + ); | ||
1613 | + } | ||
1614 | + return ch; | ||
1615 | + }); | ||
1616 | + | ||
1617 | + addr = '<a href="' + addr + '">' + addr + '</a>'; | ||
1618 | + addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part | ||
1619 | + | ||
1620 | + return addr; | ||
1621 | +}); | ||
1622 | + | ||
1623 | +/** | ||
1624 | + * Within tags -- meaning between < and > -- encode [\ ` * _] so they | ||
1625 | + * don't conflict with their use in Markdown for code, italics and strong. | ||
1626 | + */ | ||
1627 | +showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) { | ||
1628 | + 'use strict'; | ||
1629 | + | ||
1630 | + // Build a regex to find HTML tags and comments. See Friedl's | ||
1631 | + // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. | ||
1632 | + var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi; | ||
1633 | + | ||
1634 | + text = text.replace(regex, function (wholeMatch) { | ||
1635 | + var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`'); | ||
1636 | + tag = showdown.helper.escapeCharacters(tag, '\\`*_', false); | ||
1637 | + return tag; | ||
1638 | + }); | ||
1639 | + | ||
1640 | + return text; | ||
1641 | +}); | ||
1642 | + | ||
1643 | +/** | ||
1644 | + * Handle github codeblocks prior to running HashHTML so that | ||
1645 | + * HTML contained within the codeblock gets escaped properly | ||
1646 | + * Example: | ||
1647 | + * ```ruby | ||
1648 | + * def hello_world(x) | ||
1649 | + * puts "Hello, #{x}" | ||
1650 | + * end | ||
1651 | + * ``` | ||
1652 | + */ | ||
1653 | +showdown.subParser('githubCodeBlocks', function (text, options, globals) { | ||
1654 | + 'use strict'; | ||
1655 | + | ||
1656 | + // early exit if option is not enabled | ||
1657 | + if (!options.ghCodeBlocks) { | ||
1658 | + return text; | ||
1659 | + } | ||
1660 | + | ||
1661 | + text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals); | ||
1662 | + | ||
1663 | + text += '~0'; | ||
1664 | + | ||
1665 | + text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) { | ||
1666 | + var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; | ||
1667 | + | ||
1668 | + // First parse the github code block | ||
1669 | + codeblock = showdown.subParser('encodeCode')(codeblock); | ||
1670 | + codeblock = showdown.subParser('detab')(codeblock); | ||
1671 | + codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines | ||
1672 | + codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace | ||
1673 | + | ||
1674 | + codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>'; | ||
1675 | + | ||
1676 | + codeblock = showdown.subParser('hashBlock')(codeblock, options, globals); | ||
1677 | + | ||
1678 | + // Since GHCodeblocks can be false positives, we need to | ||
1679 | + // store the primitive text and the parsed text in a global var, | ||
1680 | + // and then return a token | ||
1681 | + return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; | ||
1682 | + }); | ||
1683 | + | ||
1684 | + // attacklab: strip sentinel | ||
1685 | + text = text.replace(/~0/, ''); | ||
1686 | + | ||
1687 | + return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals); | ||
1688 | +}); | ||
1689 | + | ||
1690 | +showdown.subParser('hashBlock', function (text, options, globals) { | ||
1691 | + 'use strict'; | ||
1692 | + text = text.replace(/(^\n+|\n+$)/g, ''); | ||
1693 | + return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n'; | ||
1694 | +}); | ||
1695 | + | ||
1696 | +showdown.subParser('hashElement', function (text, options, globals) { | ||
1697 | + 'use strict'; | ||
1698 | + | ||
1699 | + return function (wholeMatch, m1) { | ||
1700 | + var blockText = m1; | ||
1701 | + | ||
1702 | + // Undo double lines | ||
1703 | + blockText = blockText.replace(/\n\n/g, '\n'); | ||
1704 | + blockText = blockText.replace(/^\n/, ''); | ||
1705 | + | ||
1706 | + // strip trailing blank lines | ||
1707 | + blockText = blockText.replace(/\n+$/g, ''); | ||
1708 | + | ||
1709 | + // Replace the element text with a marker ("~KxK" where x is its key) | ||
1710 | + blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n'; | ||
1711 | + | ||
1712 | + return blockText; | ||
1713 | + }; | ||
1714 | +}); | ||
1715 | + | ||
1716 | +showdown.subParser('hashHTMLBlocks', function (text, options, globals) { | ||
1717 | + 'use strict'; | ||
1718 | + | ||
1719 | + var blockTags = [ | ||
1720 | + 'pre', | ||
1721 | + 'div', | ||
1722 | + 'h1', | ||
1723 | + 'h2', | ||
1724 | + 'h3', | ||
1725 | + 'h4', | ||
1726 | + 'h5', | ||
1727 | + 'h6', | ||
1728 | + 'blockquote', | ||
1729 | + 'table', | ||
1730 | + 'dl', | ||
1731 | + 'ol', | ||
1732 | + 'ul', | ||
1733 | + 'script', | ||
1734 | + 'noscript', | ||
1735 | + 'form', | ||
1736 | + 'fieldset', | ||
1737 | + 'iframe', | ||
1738 | + 'math', | ||
1739 | + 'style', | ||
1740 | + 'section', | ||
1741 | + 'header', | ||
1742 | + 'footer', | ||
1743 | + 'nav', | ||
1744 | + 'article', | ||
1745 | + 'aside', | ||
1746 | + 'address', | ||
1747 | + 'audio', | ||
1748 | + 'canvas', | ||
1749 | + 'figure', | ||
1750 | + 'hgroup', | ||
1751 | + 'output', | ||
1752 | + 'video', | ||
1753 | + 'p' | ||
1754 | + ], | ||
1755 | + repFunc = function (wholeMatch, match, left, right) { | ||
1756 | + var txt = wholeMatch; | ||
1757 | + // check if this html element is marked as markdown | ||
1758 | + // if so, it's contents should be parsed as markdown | ||
1759 | + if (left.search(/\bmarkdown\b/) !== -1) { | ||
1760 | + txt = left + globals.converter.makeHtml(match) + right; | ||
1761 | + } | ||
1762 | + return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; | ||
1763 | + }; | ||
1764 | + | ||
1765 | + for (var i = 0; i < blockTags.length; ++i) { | ||
1766 | + text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<' + blockTags[i] + '\\b[^>]*>', '</' + blockTags[i] + '>', 'gim'); | ||
1767 | + } | ||
1768 | + | ||
1769 | + // HR SPECIAL CASE | ||
1770 | + text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, | ||
1771 | + showdown.subParser('hashElement')(text, options, globals)); | ||
1772 | + | ||
1773 | + // Special case for standalone HTML comments: | ||
1774 | + text = text.replace(/(<!--[\s\S]*?-->)/g, | ||
1775 | + showdown.subParser('hashElement')(text, options, globals)); | ||
1776 | + | ||
1777 | + // PHP and ASP-style processor instructions (<?...?> and <%...%>) | ||
1778 | + text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, | ||
1779 | + showdown.subParser('hashElement')(text, options, globals)); | ||
1780 | + return text; | ||
1781 | +}); | ||
1782 | + | ||
1783 | +/** | ||
1784 | + * Hash span elements that should not be parsed as markdown | ||
1785 | + */ | ||
1786 | +showdown.subParser('hashHTMLSpans', function (text, config, globals) { | ||
1787 | + 'use strict'; | ||
1788 | + | ||
1789 | + var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi'); | ||
1790 | + | ||
1791 | + for (var i = 0; i < matches.length; ++i) { | ||
1792 | + text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L'); | ||
1793 | + } | ||
1794 | + return text; | ||
1795 | +}); | ||
1796 | + | ||
1797 | +/** | ||
1798 | + * Unhash HTML spans | ||
1799 | + */ | ||
1800 | +showdown.subParser('unhashHTMLSpans', function (text, config, globals) { | ||
1801 | + 'use strict'; | ||
1802 | + | ||
1803 | + for (var i = 0; i < globals.gHtmlSpans.length; ++i) { | ||
1804 | + text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]); | ||
1805 | + } | ||
1806 | + | ||
1807 | + return text; | ||
1808 | +}); | ||
1809 | + | ||
1810 | +/** | ||
1811 | + * Hash span elements that should not be parsed as markdown | ||
1812 | + */ | ||
1813 | +showdown.subParser('hashPreCodeTags', function (text, config, globals) { | ||
1814 | + 'use strict'; | ||
1815 | + | ||
1816 | + var repFunc = function (wholeMatch, match, left, right) { | ||
1817 | + // encode html entities | ||
1818 | + var codeblock = left + showdown.subParser('encodeCode')(match) + right; | ||
1819 | + return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; | ||
1820 | + }; | ||
1821 | + | ||
1822 | + text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^(?: |\\t){0,3}</code>\\s*</pre>', 'gim'); | ||
1823 | + return text; | ||
1824 | +}); | ||
1825 | + | ||
1826 | +showdown.subParser('headers', function (text, options, globals) { | ||
1827 | + 'use strict'; | ||
1828 | + | ||
1829 | + text = globals.converter._dispatch('headers.before', text, options, globals); | ||
1830 | + | ||
1831 | + var prefixHeader = options.prefixHeaderId, | ||
1832 | + headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart), | ||
1833 | + | ||
1834 | + // Set text-style headers: | ||
1835 | + // Header 1 | ||
1836 | + // ======== | ||
1837 | + // | ||
1838 | + // Header 2 | ||
1839 | + // -------- | ||
1840 | + // | ||
1841 | + setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm, | ||
1842 | + setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm; | ||
1843 | + | ||
1844 | + text = text.replace(setextRegexH1, function (wholeMatch, m1) { | ||
1845 | + | ||
1846 | + var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), | ||
1847 | + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', | ||
1848 | + hLevel = headerLevelStart, | ||
1849 | + hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>'; | ||
1850 | + return showdown.subParser('hashBlock')(hashBlock, options, globals); | ||
1851 | + }); | ||
1852 | + | ||
1853 | + text = text.replace(setextRegexH2, function (matchFound, m1) { | ||
1854 | + var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), | ||
1855 | + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', | ||
1856 | + hLevel = headerLevelStart + 1, | ||
1857 | + hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>'; | ||
1858 | + return showdown.subParser('hashBlock')(hashBlock, options, globals); | ||
1859 | + }); | ||
1860 | + | ||
1861 | + // atx-style headers: | ||
1862 | + // # Header 1 | ||
1863 | + // ## Header 2 | ||
1864 | + // ## Header 2 with closing hashes ## | ||
1865 | + // ... | ||
1866 | + // ###### Header 6 | ||
1867 | + // | ||
1868 | + text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) { | ||
1869 | + var span = showdown.subParser('spanGamut')(m2, options, globals), | ||
1870 | + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"', | ||
1871 | + hLevel = headerLevelStart - 1 + m1.length, | ||
1872 | + header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>'; | ||
1873 | + | ||
1874 | + return showdown.subParser('hashBlock')(header, options, globals); | ||
1875 | + }); | ||
1876 | + | ||
1877 | + function headerId(m) { | ||
1878 | + var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase(); | ||
1879 | + | ||
1880 | + if (globals.hashLinkCounts[escapedId]) { | ||
1881 | + title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++); | ||
1882 | + } else { | ||
1883 | + title = escapedId; | ||
1884 | + globals.hashLinkCounts[escapedId] = 1; | ||
1885 | + } | ||
1886 | + | ||
1887 | + // Prefix id to prevent causing inadvertent pre-existing style matches. | ||
1888 | + if (prefixHeader === true) { | ||
1889 | + prefixHeader = 'section'; | ||
1890 | + } | ||
1891 | + | ||
1892 | + if (showdown.helper.isString(prefixHeader)) { | ||
1893 | + return prefixHeader + title; | ||
1894 | + } | ||
1895 | + return title; | ||
1896 | + } | ||
1897 | + | ||
1898 | + text = globals.converter._dispatch('headers.after', text, options, globals); | ||
1899 | + return text; | ||
1900 | +}); | ||
1901 | + | ||
1902 | +/** | ||
1903 | + * Turn Markdown image shortcuts into <img> tags. | ||
1904 | + */ | ||
1905 | +showdown.subParser('images', function (text, options, globals) { | ||
1906 | + 'use strict'; | ||
1907 | + | ||
1908 | + text = globals.converter._dispatch('images.before', text, options, globals); | ||
1909 | + | ||
1910 | + var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g, | ||
1911 | + referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g; | ||
1912 | + | ||
1913 | + function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { | ||
1914 | + | ||
1915 | + var gUrls = globals.gUrls, | ||
1916 | + gTitles = globals.gTitles, | ||
1917 | + gDims = globals.gDimensions; | ||
1918 | + | ||
1919 | + linkId = linkId.toLowerCase(); | ||
1920 | + | ||
1921 | + if (!title) { | ||
1922 | + title = ''; | ||
1923 | + } | ||
1924 | + | ||
1925 | + if (url === '' || url === null) { | ||
1926 | + if (linkId === '' || linkId === null) { | ||
1927 | + // lower-case and turn embedded newlines into spaces | ||
1928 | + linkId = altText.toLowerCase().replace(/ ?\n/g, ' '); | ||
1929 | + } | ||
1930 | + url = '#' + linkId; | ||
1931 | + | ||
1932 | + if (!showdown.helper.isUndefined(gUrls[linkId])) { | ||
1933 | + url = gUrls[linkId]; | ||
1934 | + if (!showdown.helper.isUndefined(gTitles[linkId])) { | ||
1935 | + title = gTitles[linkId]; | ||
1936 | + } | ||
1937 | + if (!showdown.helper.isUndefined(gDims[linkId])) { | ||
1938 | + width = gDims[linkId].width; | ||
1939 | + height = gDims[linkId].height; | ||
1940 | + } | ||
1941 | + } else { | ||
1942 | + return wholeMatch; | ||
1943 | + } | ||
1944 | + } | ||
1945 | + | ||
1946 | + altText = altText.replace(/"/g, '"'); | ||
1947 | + altText = showdown.helper.escapeCharacters(altText, '*_', false); | ||
1948 | + url = showdown.helper.escapeCharacters(url, '*_', false); | ||
1949 | + var result = '<img src="' + url + '" alt="' + altText + '"'; | ||
1950 | + | ||
1951 | + if (title) { | ||
1952 | + title = title.replace(/"/g, '"'); | ||
1953 | + title = showdown.helper.escapeCharacters(title, '*_', false); | ||
1954 | + result += ' title="' + title + '"'; | ||
1955 | + } | ||
1956 | + | ||
1957 | + if (width && height) { | ||
1958 | + width = (width === '*') ? 'auto' : width; | ||
1959 | + height = (height === '*') ? 'auto' : height; | ||
1960 | + | ||
1961 | + result += ' width="' + width + '"'; | ||
1962 | + result += ' height="' + height + '"'; | ||
1963 | + } | ||
1964 | + | ||
1965 | + result += ' />'; | ||
1966 | + return result; | ||
1967 | + } | ||
1968 | + | ||
1969 | + // First, handle reference-style labeled images: ![alt text][id] | ||
1970 | + text = text.replace(referenceRegExp, writeImageTag); | ||
1971 | + | ||
1972 | + // Next, handle inline images: ![alt text](url =<width>x<height> "optional title") | ||
1973 | + text = text.replace(inlineRegExp, writeImageTag); | ||
1974 | + | ||
1975 | + text = globals.converter._dispatch('images.after', text, options, globals); | ||
1976 | + return text; | ||
1977 | +}); | ||
1978 | + | ||
1979 | +showdown.subParser('italicsAndBold', function (text, options, globals) { | ||
1980 | + 'use strict'; | ||
1981 | + | ||
1982 | + text = globals.converter._dispatch('italicsAndBold.before', text, options, globals); | ||
1983 | + | ||
1984 | + if (options.literalMidWordUnderscores) { | ||
1985 | + //underscores | ||
1986 | + // Since we are consuming a \s character, we need to add it | ||
1987 | + text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>'); | ||
1988 | + text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>'); | ||
1989 | + //asterisks | ||
1990 | + text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>'); | ||
1991 | + text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>'); | ||
1992 | + | ||
1993 | + } else { | ||
1994 | + // <strong> must go first: | ||
1995 | + text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>'); | ||
1996 | + text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>'); | ||
1997 | + } | ||
1998 | + | ||
1999 | + text = globals.converter._dispatch('italicsAndBold.after', text, options, globals); | ||
2000 | + return text; | ||
2001 | +}); | ||
2002 | + | ||
2003 | +/** | ||
2004 | + * Form HTML ordered (numbered) and unordered (bulleted) lists. | ||
2005 | + */ | ||
2006 | +showdown.subParser('lists', function (text, options, globals) { | ||
2007 | + 'use strict'; | ||
2008 | + | ||
2009 | + text = globals.converter._dispatch('lists.before', text, options, globals); | ||
2010 | + /** | ||
2011 | + * Process the contents of a single ordered or unordered list, splitting it | ||
2012 | + * into individual list items. | ||
2013 | + * @param {string} listStr | ||
2014 | + * @param {boolean} trimTrailing | ||
2015 | + * @returns {string} | ||
2016 | + */ | ||
2017 | + function processListItems (listStr, trimTrailing) { | ||
2018 | + // The $g_list_level global keeps track of when we're inside a list. | ||
2019 | + // Each time we enter a list, we increment it; when we leave a list, | ||
2020 | + // we decrement. If it's zero, we're not in a list anymore. | ||
2021 | + // | ||
2022 | + // We do this because when we're not inside a list, we want to treat | ||
2023 | + // something like this: | ||
2024 | + // | ||
2025 | + // I recommend upgrading to version | ||
2026 | + // 8. Oops, now this line is treated | ||
2027 | + // as a sub-list. | ||
2028 | + // | ||
2029 | + // As a single paragraph, despite the fact that the second line starts | ||
2030 | + // with a digit-period-space sequence. | ||
2031 | + // | ||
2032 | + // Whereas when we're inside a list (or sub-list), that line will be | ||
2033 | + // treated as the start of a sub-list. What a kludge, huh? This is | ||
2034 | + // an aspect of Markdown's syntax that's hard to parse perfectly | ||
2035 | + // without resorting to mind-reading. Perhaps the solution is to | ||
2036 | + // change the syntax rules such that sub-lists must start with a | ||
2037 | + // starting cardinal number; e.g. "1." or "a.". | ||
2038 | + globals.gListLevel++; | ||
2039 | + | ||
2040 | + // trim trailing blank lines: | ||
2041 | + listStr = listStr.replace(/\n{2,}$/, '\n'); | ||
2042 | + | ||
2043 | + // attacklab: add sentinel to emulate \z | ||
2044 | + listStr += '~0'; | ||
2045 | + | ||
2046 | + var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, | ||
2047 | + isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr)); | ||
2048 | + | ||
2049 | + listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { | ||
2050 | + checked = (checked && checked.trim() !== ''); | ||
2051 | + var item = showdown.subParser('outdent')(m4, options, globals), | ||
2052 | + bulletStyle = ''; | ||
2053 | + | ||
2054 | + // Support for github tasklists | ||
2055 | + if (taskbtn && options.tasklists) { | ||
2056 | + bulletStyle = ' class="task-list-item" style="list-style-type: none;"'; | ||
2057 | + item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () { | ||
2058 | + var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"'; | ||
2059 | + if (checked) { | ||
2060 | + otp += ' checked'; | ||
2061 | + } | ||
2062 | + otp += '>'; | ||
2063 | + return otp; | ||
2064 | + }); | ||
2065 | + } | ||
2066 | + // m1 - Leading line or | ||
2067 | + // Has a double return (multi paragraph) or | ||
2068 | + // Has sublist | ||
2069 | + if (m1 || (item.search(/\n{2,}/) > -1)) { | ||
2070 | + item = showdown.subParser('githubCodeBlocks')(item, options, globals); | ||
2071 | + item = showdown.subParser('blockGamut')(item, options, globals); | ||
2072 | + } else { | ||
2073 | + // Recursion for sub-lists: | ||
2074 | + item = showdown.subParser('lists')(item, options, globals); | ||
2075 | + item = item.replace(/\n$/, ''); // chomp(item) | ||
2076 | + if (isParagraphed) { | ||
2077 | + item = showdown.subParser('paragraphs')(item, options, globals); | ||
2078 | + } else { | ||
2079 | + item = showdown.subParser('spanGamut')(item, options, globals); | ||
2080 | + } | ||
2081 | + } | ||
2082 | + item = '\n<li' + bulletStyle + '>' + item + '</li>\n'; | ||
2083 | + return item; | ||
2084 | + }); | ||
2085 | + | ||
2086 | + // attacklab: strip sentinel | ||
2087 | + listStr = listStr.replace(/~0/g, ''); | ||
2088 | + | ||
2089 | + globals.gListLevel--; | ||
2090 | + | ||
2091 | + if (trimTrailing) { | ||
2092 | + listStr = listStr.replace(/\s+$/, ''); | ||
2093 | + } | ||
2094 | + | ||
2095 | + return listStr; | ||
2096 | + } | ||
2097 | + | ||
2098 | + /** | ||
2099 | + * Check and parse consecutive lists (better fix for issue #142) | ||
2100 | + * @param {string} list | ||
2101 | + * @param {string} listType | ||
2102 | + * @param {boolean} trimTrailing | ||
2103 | + * @returns {string} | ||
2104 | + */ | ||
2105 | + function parseConsecutiveLists(list, listType, trimTrailing) { | ||
2106 | + // check if we caught 2 or more consecutive lists by mistake | ||
2107 | + // we use the counterRgx, meaning if listType is UL we look for UL and vice versa | ||
2108 | + var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm, | ||
2109 | + subLists = [], | ||
2110 | + result = ''; | ||
2111 | + | ||
2112 | + if (list.search(counterRxg) !== -1) { | ||
2113 | + (function parseCL(txt) { | ||
2114 | + var pos = txt.search(counterRxg); | ||
2115 | + if (pos !== -1) { | ||
2116 | + // slice | ||
2117 | + result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n\n'; | ||
2118 | + | ||
2119 | + // invert counterType and listType | ||
2120 | + listType = (listType === 'ul') ? 'ol' : 'ul'; | ||
2121 | + counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm; | ||
2122 | + | ||
2123 | + //recurse | ||
2124 | + parseCL(txt.slice(pos)); | ||
2125 | + } else { | ||
2126 | + result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n\n'; | ||
2127 | + } | ||
2128 | + })(list); | ||
2129 | + for (var i = 0; i < subLists.length; ++i) { | ||
2130 | + | ||
2131 | + } | ||
2132 | + } else { | ||
2133 | + result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n\n'; | ||
2134 | + } | ||
2135 | + | ||
2136 | + return result; | ||
2137 | + } | ||
2138 | + | ||
2139 | + // attacklab: add sentinel to hack around khtml/safari bug: | ||
2140 | + // http://bugs.webkit.org/show_bug.cgi?id=11231 | ||
2141 | + text += '~0'; | ||
2142 | + | ||
2143 | + // Re-usable pattern to match any entire ul or ol list: | ||
2144 | + var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; | ||
2145 | + | ||
2146 | + if (globals.gListLevel) { | ||
2147 | + text = text.replace(wholeList, function (wholeMatch, list, m2) { | ||
2148 | + var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; | ||
2149 | + return parseConsecutiveLists(list, listType, true); | ||
2150 | + }); | ||
2151 | + } else { | ||
2152 | + wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; | ||
2153 | + //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g; | ||
2154 | + text = text.replace(wholeList, function (wholeMatch, m1, list, m3) { | ||
2155 | + | ||
2156 | + var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; | ||
2157 | + return parseConsecutiveLists(list, listType); | ||
2158 | + }); | ||
2159 | + } | ||
2160 | + | ||
2161 | + // attacklab: strip sentinel | ||
2162 | + text = text.replace(/~0/, ''); | ||
2163 | + | ||
2164 | + text = globals.converter._dispatch('lists.after', text, options, globals); | ||
2165 | + return text; | ||
2166 | +}); | ||
2167 | + | ||
2168 | +/** | ||
2169 | + * Remove one level of line-leading tabs or spaces | ||
2170 | + */ | ||
2171 | +showdown.subParser('outdent', function (text) { | ||
2172 | + 'use strict'; | ||
2173 | + | ||
2174 | + // attacklab: hack around Konqueror 3.5.4 bug: | ||
2175 | + // "----------bug".replace(/^-/g,"") == "bug" | ||
2176 | + text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width | ||
2177 | + | ||
2178 | + // attacklab: clean up hack | ||
2179 | + text = text.replace(/~0/g, ''); | ||
2180 | + | ||
2181 | + return text; | ||
2182 | +}); | ||
2183 | + | ||
2184 | +/** | ||
2185 | + * | ||
2186 | + */ | ||
2187 | +showdown.subParser('paragraphs', function (text, options, globals) { | ||
2188 | + 'use strict'; | ||
2189 | + | ||
2190 | + text = globals.converter._dispatch('paragraphs.before', text, options, globals); | ||
2191 | + // Strip leading and trailing lines: | ||
2192 | + text = text.replace(/^\n+/g, ''); | ||
2193 | + text = text.replace(/\n+$/g, ''); | ||
2194 | + | ||
2195 | + var grafs = text.split(/\n{2,}/g), | ||
2196 | + grafsOut = [], | ||
2197 | + end = grafs.length; // Wrap <p> tags | ||
2198 | + | ||
2199 | + for (var i = 0; i < end; i++) { | ||
2200 | + var str = grafs[i]; | ||
2201 | + // if this is an HTML marker, copy it | ||
2202 | + if (str.search(/~(K|G)(\d+)\1/g) >= 0) { | ||
2203 | + grafsOut.push(str); | ||
2204 | + } else { | ||
2205 | + str = showdown.subParser('spanGamut')(str, options, globals); | ||
2206 | + str = str.replace(/^([ \t]*)/g, '<p>'); | ||
2207 | + str += '</p>'; | ||
2208 | + grafsOut.push(str); | ||
2209 | + } | ||
2210 | + } | ||
2211 | + | ||
2212 | + /** Unhashify HTML blocks */ | ||
2213 | + end = grafsOut.length; | ||
2214 | + for (i = 0; i < end; i++) { | ||
2215 | + var blockText = '', | ||
2216 | + grafsOutIt = grafsOut[i], | ||
2217 | + codeFlag = false; | ||
2218 | + // if this is a marker for an html block... | ||
2219 | + while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) { | ||
2220 | + var delim = RegExp.$1, | ||
2221 | + num = RegExp.$2; | ||
2222 | + | ||
2223 | + if (delim === 'K') { | ||
2224 | + blockText = globals.gHtmlBlocks[num]; | ||
2225 | + } else { | ||
2226 | + // we need to check if ghBlock is a false positive | ||
2227 | + if (codeFlag) { | ||
2228 | + // use encoded version of all text | ||
2229 | + blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text); | ||
2230 | + } else { | ||
2231 | + blockText = globals.ghCodeBlocks[num].codeblock; | ||
2232 | + } | ||
2233 | + } | ||
2234 | + blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs | ||
2235 | + | ||
2236 | + grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText); | ||
2237 | + // Check if grafsOutIt is a pre->code | ||
2238 | + if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) { | ||
2239 | + codeFlag = true; | ||
2240 | + } | ||
2241 | + } | ||
2242 | + grafsOut[i] = grafsOutIt; | ||
2243 | + } | ||
2244 | + text = grafsOut.join('\n\n'); | ||
2245 | + // Strip leading and trailing lines: | ||
2246 | + text = text.replace(/^\n+/g, ''); | ||
2247 | + text = text.replace(/\n+$/g, ''); | ||
2248 | + return globals.converter._dispatch('paragraphs.after', text, options, globals); | ||
2249 | +}); | ||
2250 | + | ||
2251 | +/** | ||
2252 | + * Run extension | ||
2253 | + */ | ||
2254 | +showdown.subParser('runExtension', function (ext, text, options, globals) { | ||
2255 | + 'use strict'; | ||
2256 | + | ||
2257 | + if (ext.filter) { | ||
2258 | + text = ext.filter(text, globals.converter, options); | ||
2259 | + | ||
2260 | + } else if (ext.regex) { | ||
2261 | + // TODO remove this when old extension loading mechanism is deprecated | ||
2262 | + var re = ext.regex; | ||
2263 | + if (!re instanceof RegExp) { | ||
2264 | + re = new RegExp(re, 'g'); | ||
2265 | + } | ||
2266 | + text = text.replace(re, ext.replace); | ||
2267 | + } | ||
2268 | + | ||
2269 | + return text; | ||
2270 | +}); | ||
2271 | + | ||
2272 | +/** | ||
2273 | + * These are all the transformations that occur *within* block-level | ||
2274 | + * tags like paragraphs, headers, and list items. | ||
2275 | + */ | ||
2276 | +showdown.subParser('spanGamut', function (text, options, globals) { | ||
2277 | + 'use strict'; | ||
2278 | + | ||
2279 | + text = globals.converter._dispatch('spanGamut.before', text, options, globals); | ||
2280 | + text = showdown.subParser('codeSpans')(text, options, globals); | ||
2281 | + text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals); | ||
2282 | + text = showdown.subParser('encodeBackslashEscapes')(text, options, globals); | ||
2283 | + | ||
2284 | + // Process anchor and image tags. Images must come first, | ||
2285 | + // because ![foo][f] looks like an anchor. | ||
2286 | + text = showdown.subParser('images')(text, options, globals); | ||
2287 | + text = showdown.subParser('anchors')(text, options, globals); | ||
2288 | + | ||
2289 | + // Make links out of things like `<http://example.com/>` | ||
2290 | + // Must come after _DoAnchors(), because you can use < and > | ||
2291 | + // delimiters in inline links like [this](<url>). | ||
2292 | + text = showdown.subParser('autoLinks')(text, options, globals); | ||
2293 | + text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals); | ||
2294 | + text = showdown.subParser('italicsAndBold')(text, options, globals); | ||
2295 | + text = showdown.subParser('strikethrough')(text, options, globals); | ||
2296 | + | ||
2297 | + // Do hard breaks: | ||
2298 | + text = text.replace(/ +\n/g, ' <br />\n'); | ||
2299 | + | ||
2300 | + text = globals.converter._dispatch('spanGamut.after', text, options, globals); | ||
2301 | + return text; | ||
2302 | +}); | ||
2303 | + | ||
2304 | +showdown.subParser('strikethrough', function (text, options, globals) { | ||
2305 | + 'use strict'; | ||
2306 | + | ||
2307 | + if (options.strikethrough) { | ||
2308 | + text = globals.converter._dispatch('strikethrough.before', text, options, globals); | ||
2309 | + text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>'); | ||
2310 | + text = globals.converter._dispatch('strikethrough.after', text, options, globals); | ||
2311 | + } | ||
2312 | + | ||
2313 | + return text; | ||
2314 | +}); | ||
2315 | + | ||
2316 | +/** | ||
2317 | + * Strip any lines consisting only of spaces and tabs. | ||
2318 | + * This makes subsequent regexs easier to write, because we can | ||
2319 | + * match consecutive blank lines with /\n+/ instead of something | ||
2320 | + * contorted like /[ \t]*\n+/ | ||
2321 | + */ | ||
2322 | +showdown.subParser('stripBlankLines', function (text) { | ||
2323 | + 'use strict'; | ||
2324 | + return text.replace(/^[ \t]+$/mg, ''); | ||
2325 | +}); | ||
2326 | + | ||
2327 | +/** | ||
2328 | + * Strips link definitions from text, stores the URLs and titles in | ||
2329 | + * hash references. | ||
2330 | + * Link defs are in the form: ^[id]: url "optional title" | ||
2331 | + * | ||
2332 | + * ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 | ||
2333 | + * [ \t]* | ||
2334 | + * \n? // maybe *one* newline | ||
2335 | + * [ \t]* | ||
2336 | + * <?(\S+?)>? // url = $2 | ||
2337 | + * [ \t]* | ||
2338 | + * \n? // maybe one newline | ||
2339 | + * [ \t]* | ||
2340 | + * (?: | ||
2341 | + * (\n*) // any lines skipped = $3 attacklab: lookbehind removed | ||
2342 | + * ["(] | ||
2343 | + * (.+?) // title = $4 | ||
2344 | + * [")] | ||
2345 | + * [ \t]* | ||
2346 | + * )? // title is optional | ||
2347 | + * (?:\n+|$) | ||
2348 | + * /gm, | ||
2349 | + * function(){...}); | ||
2350 | + * | ||
2351 | + */ | ||
2352 | +showdown.subParser('stripLinkDefinitions', function (text, options, globals) { | ||
2353 | + 'use strict'; | ||
2354 | + | ||
2355 | + var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm; | ||
2356 | + | ||
2357 | + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug | ||
2358 | + text += '~0'; | ||
2359 | + | ||
2360 | + text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) { | ||
2361 | + linkId = linkId.toLowerCase(); | ||
2362 | + globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive | ||
2363 | + | ||
2364 | + if (blankLines) { | ||
2365 | + // Oops, found blank lines, so it's not a title. | ||
2366 | + // Put back the parenthetical statement we stole. | ||
2367 | + return blankLines + title; | ||
2368 | + | ||
2369 | + } else { | ||
2370 | + if (title) { | ||
2371 | + globals.gTitles[linkId] = title.replace(/"|'/g, '"'); | ||
2372 | + } | ||
2373 | + if (options.parseImgDimensions && width && height) { | ||
2374 | + globals.gDimensions[linkId] = { | ||
2375 | + width: width, | ||
2376 | + height: height | ||
2377 | + }; | ||
2378 | + } | ||
2379 | + } | ||
2380 | + // Completely remove the definition from the text | ||
2381 | + return ''; | ||
2382 | + }); | ||
2383 | + | ||
2384 | + // attacklab: strip sentinel | ||
2385 | + text = text.replace(/~0/, ''); | ||
2386 | + | ||
2387 | + return text; | ||
2388 | +}); | ||
2389 | + | ||
2390 | +showdown.subParser('tables', function (text, options, globals) { | ||
2391 | + 'use strict'; | ||
2392 | + | ||
2393 | + if (!options.tables) { | ||
2394 | + return text; | ||
2395 | + } | ||
2396 | + | ||
2397 | + var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm; | ||
2398 | + | ||
2399 | + function parseStyles(sLine) { | ||
2400 | + if (/^:[ \t]*--*$/.test(sLine)) { | ||
2401 | + return ' style="text-align:left;"'; | ||
2402 | + } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) { | ||
2403 | + return ' style="text-align:right;"'; | ||
2404 | + } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) { | ||
2405 | + return ' style="text-align:center;"'; | ||
2406 | + } else { | ||
2407 | + return ''; | ||
2408 | + } | ||
2409 | + } | ||
2410 | + | ||
2411 | + function parseHeaders(header, style) { | ||
2412 | + var id = ''; | ||
2413 | + header = header.trim(); | ||
2414 | + if (options.tableHeaderId) { | ||
2415 | + id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; | ||
2416 | + } | ||
2417 | + header = showdown.subParser('spanGamut')(header, options, globals); | ||
2418 | + | ||
2419 | + return '<th' + id + style + '>' + header + '</th>\n'; | ||
2420 | + } | ||
2421 | + | ||
2422 | + function parseCells(cell, style) { | ||
2423 | + var subText = showdown.subParser('spanGamut')(cell, options, globals); | ||
2424 | + return '<td' + style + '>' + subText + '</td>\n'; | ||
2425 | + } | ||
2426 | + | ||
2427 | + function buildTable(headers, cells) { | ||
2428 | + var tb = '<table>\n<thead>\n<tr>\n', | ||
2429 | + tblLgn = headers.length; | ||
2430 | + | ||
2431 | + for (var i = 0; i < tblLgn; ++i) { | ||
2432 | + tb += headers[i]; | ||
2433 | + } | ||
2434 | + tb += '</tr>\n</thead>\n<tbody>\n'; | ||
2435 | + | ||
2436 | + for (i = 0; i < cells.length; ++i) { | ||
2437 | + tb += '<tr>\n'; | ||
2438 | + for (var ii = 0; ii < tblLgn; ++ii) { | ||
2439 | + tb += cells[i][ii]; | ||
2440 | + } | ||
2441 | + tb += '</tr>\n'; | ||
2442 | + } | ||
2443 | + tb += '</tbody>\n</table>\n'; | ||
2444 | + return tb; | ||
2445 | + } | ||
2446 | + | ||
2447 | + text = globals.converter._dispatch('tables.before', text, options, globals); | ||
2448 | + | ||
2449 | + text = text.replace(tableRgx, function (rawTable) { | ||
2450 | + | ||
2451 | + var i, tableLines = rawTable.split('\n'); | ||
2452 | + | ||
2453 | + // strip wrong first and last column if wrapped tables are used | ||
2454 | + for (i = 0; i < tableLines.length; ++i) { | ||
2455 | + if (/^[ \t]{0,3}\|/.test(tableLines[i])) { | ||
2456 | + tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, ''); | ||
2457 | + } | ||
2458 | + if (/\|[ \t]*$/.test(tableLines[i])) { | ||
2459 | + tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); | ||
2460 | + } | ||
2461 | + } | ||
2462 | + | ||
2463 | + var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}), | ||
2464 | + rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}), | ||
2465 | + rawCells = [], | ||
2466 | + headers = [], | ||
2467 | + styles = [], | ||
2468 | + cells = []; | ||
2469 | + | ||
2470 | + tableLines.shift(); | ||
2471 | + tableLines.shift(); | ||
2472 | + | ||
2473 | + for (i = 0; i < tableLines.length; ++i) { | ||
2474 | + if (tableLines[i].trim() === '') { | ||
2475 | + continue; | ||
2476 | + } | ||
2477 | + rawCells.push( | ||
2478 | + tableLines[i] | ||
2479 | + .split('|') | ||
2480 | + .map(function (s) { | ||
2481 | + return s.trim(); | ||
2482 | + }) | ||
2483 | + ); | ||
2484 | + } | ||
2485 | + | ||
2486 | + if (rawHeaders.length < rawStyles.length) { | ||
2487 | + return rawTable; | ||
2488 | + } | ||
2489 | + | ||
2490 | + for (i = 0; i < rawStyles.length; ++i) { | ||
2491 | + styles.push(parseStyles(rawStyles[i])); | ||
2492 | + } | ||
2493 | + | ||
2494 | + for (i = 0; i < rawHeaders.length; ++i) { | ||
2495 | + if (showdown.helper.isUndefined(styles[i])) { | ||
2496 | + styles[i] = ''; | ||
2497 | + } | ||
2498 | + headers.push(parseHeaders(rawHeaders[i], styles[i])); | ||
2499 | + } | ||
2500 | + | ||
2501 | + for (i = 0; i < rawCells.length; ++i) { | ||
2502 | + var row = []; | ||
2503 | + for (var ii = 0; ii < headers.length; ++ii) { | ||
2504 | + if (showdown.helper.isUndefined(rawCells[i][ii])) { | ||
2505 | + | ||
2506 | + } | ||
2507 | + row.push(parseCells(rawCells[i][ii], styles[ii])); | ||
2508 | + } | ||
2509 | + cells.push(row); | ||
2510 | + } | ||
2511 | + | ||
2512 | + return buildTable(headers, cells); | ||
2513 | + }); | ||
2514 | + | ||
2515 | + text = globals.converter._dispatch('tables.after', text, options, globals); | ||
2516 | + | ||
2517 | + return text; | ||
2518 | +}); | ||
2519 | + | ||
2520 | +/** | ||
2521 | + * Swap back in all the special characters we've hidden. | ||
2522 | + */ | ||
2523 | +showdown.subParser('unescapeSpecialChars', function (text) { | ||
2524 | + 'use strict'; | ||
2525 | + | ||
2526 | + text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) { | ||
2527 | + var charCodeToReplace = parseInt(m1); | ||
2528 | + return String.fromCharCode(charCodeToReplace); | ||
2529 | + }); | ||
2530 | + return text; | ||
2531 | +}); | ||
2532 | +module.exports = showdown; |
wxParse/wxDiscode.js
0 → 100644
1 | +// HTML 支持的数学符号 | ||
2 | +function strNumDiscode(str){ | ||
3 | + str = str.replace(/∀/g, '∀'); | ||
4 | + str = str.replace(/∂/g, '∂'); | ||
5 | + str = str.replace(/&exists;/g, '∃'); | ||
6 | + str = str.replace(/∅/g, '∅'); | ||
7 | + str = str.replace(/∇/g, '∇'); | ||
8 | + str = str.replace(/∈/g, '∈'); | ||
9 | + str = str.replace(/∉/g, '∉'); | ||
10 | + str = str.replace(/∋/g, '∋'); | ||
11 | + str = str.replace(/∏/g, '∏'); | ||
12 | + str = str.replace(/∑/g, '∑'); | ||
13 | + str = str.replace(/−/g, '−'); | ||
14 | + str = str.replace(/∗/g, '∗'); | ||
15 | + str = str.replace(/√/g, '√'); | ||
16 | + str = str.replace(/∝/g, '∝'); | ||
17 | + str = str.replace(/∞/g, '∞'); | ||
18 | + str = str.replace(/∠/g, '∠'); | ||
19 | + str = str.replace(/∧/g, '∧'); | ||
20 | + str = str.replace(/∨/g, '∨'); | ||
21 | + str = str.replace(/∩/g, '∩'); | ||
22 | + str = str.replace(/∩/g, '∪'); | ||
23 | + str = str.replace(/∫/g, '∫'); | ||
24 | + str = str.replace(/∴/g, '∴'); | ||
25 | + str = str.replace(/∼/g, '∼'); | ||
26 | + str = str.replace(/≅/g, '≅'); | ||
27 | + str = str.replace(/≈/g, '≈'); | ||
28 | + str = str.replace(/≠/g, '≠'); | ||
29 | + str = str.replace(/≤/g, '≤'); | ||
30 | + str = str.replace(/≥/g, '≥'); | ||
31 | + str = str.replace(/⊂/g, '⊂'); | ||
32 | + str = str.replace(/⊃/g, '⊃'); | ||
33 | + str = str.replace(/⊄/g, '⊄'); | ||
34 | + str = str.replace(/⊆/g, '⊆'); | ||
35 | + str = str.replace(/⊇/g, '⊇'); | ||
36 | + str = str.replace(/⊕/g, '⊕'); | ||
37 | + str = str.replace(/⊗/g, '⊗'); | ||
38 | + str = str.replace(/⊥/g, '⊥'); | ||
39 | + str = str.replace(/⋅/g, '⋅'); | ||
40 | + return str; | ||
41 | +} | ||
42 | + | ||
43 | +//HTML 支持的希腊字母 | ||
44 | +function strGreeceDiscode(str){ | ||
45 | + str = str.replace(/Α/g, 'Α'); | ||
46 | + str = str.replace(/Β/g, 'Β'); | ||
47 | + str = str.replace(/Γ/g, 'Γ'); | ||
48 | + str = str.replace(/Δ/g, 'Δ'); | ||
49 | + str = str.replace(/Ε/g, 'Ε'); | ||
50 | + str = str.replace(/Ζ/g, 'Ζ'); | ||
51 | + str = str.replace(/Η/g, 'Η'); | ||
52 | + str = str.replace(/Θ/g, 'Θ'); | ||
53 | + str = str.replace(/Ι/g, 'Ι'); | ||
54 | + str = str.replace(/Κ/g, 'Κ'); | ||
55 | + str = str.replace(/Λ/g, 'Λ'); | ||
56 | + str = str.replace(/Μ/g, 'Μ'); | ||
57 | + str = str.replace(/Ν/g, 'Ν'); | ||
58 | + str = str.replace(/Ξ/g, 'Ν'); | ||
59 | + str = str.replace(/Ο/g, 'Ο'); | ||
60 | + str = str.replace(/Π/g, 'Π'); | ||
61 | + str = str.replace(/Ρ/g, 'Ρ'); | ||
62 | + str = str.replace(/Σ/g, 'Σ'); | ||
63 | + str = str.replace(/Τ/g, 'Τ'); | ||
64 | + str = str.replace(/Υ/g, 'Υ'); | ||
65 | + str = str.replace(/Φ/g, 'Φ'); | ||
66 | + str = str.replace(/Χ/g, 'Χ'); | ||
67 | + str = str.replace(/Ψ/g, 'Ψ'); | ||
68 | + str = str.replace(/Ω/g, 'Ω'); | ||
69 | + | ||
70 | + str = str.replace(/α/g, 'α'); | ||
71 | + str = str.replace(/β/g, 'β'); | ||
72 | + str = str.replace(/γ/g, 'γ'); | ||
73 | + str = str.replace(/δ/g, 'δ'); | ||
74 | + str = str.replace(/ε/g, 'ε'); | ||
75 | + str = str.replace(/ζ/g, 'ζ'); | ||
76 | + str = str.replace(/η/g, 'η'); | ||
77 | + str = str.replace(/θ/g, 'θ'); | ||
78 | + str = str.replace(/ι/g, 'ι'); | ||
79 | + str = str.replace(/κ/g, 'κ'); | ||
80 | + str = str.replace(/λ/g, 'λ'); | ||
81 | + str = str.replace(/μ/g, 'μ'); | ||
82 | + str = str.replace(/ν/g, 'ν'); | ||
83 | + str = str.replace(/ξ/g, 'ξ'); | ||
84 | + str = str.replace(/ο/g, 'ο'); | ||
85 | + str = str.replace(/π/g, 'π'); | ||
86 | + str = str.replace(/ρ/g, 'ρ'); | ||
87 | + str = str.replace(/ς/g, 'ς'); | ||
88 | + str = str.replace(/σ/g, 'σ'); | ||
89 | + str = str.replace(/τ/g, 'τ'); | ||
90 | + str = str.replace(/υ/g, 'υ'); | ||
91 | + str = str.replace(/φ/g, 'φ'); | ||
92 | + str = str.replace(/χ/g, 'χ'); | ||
93 | + str = str.replace(/ψ/g, 'ψ'); | ||
94 | + str = str.replace(/ω/g, 'ω'); | ||
95 | + str = str.replace(/ϑ/g, 'ϑ'); | ||
96 | + str = str.replace(/ϒ/g, 'ϒ'); | ||
97 | + str = str.replace(/ϖ/g, 'ϖ'); | ||
98 | + str = str.replace(/·/g, '·'); | ||
99 | + return str; | ||
100 | +} | ||
101 | + | ||
102 | +// | ||
103 | + | ||
104 | +function strcharacterDiscode(str){ | ||
105 | + // 加入常用解析 | ||
106 | + str = str.replace(/↵;/g, '\n'); | ||
107 | + str = str.replace(/ /g, ' '); | ||
108 | + str = str.replace(/"/g, "'"); | ||
109 | + str = str.replace(/&/g, '&'); | ||
110 | + // str = str.replace(/</g, '‹'); | ||
111 | + // str = str.replace(/>/g, '›'); | ||
112 | + | ||
113 | + str = str.replace(/</g, '<'); | ||
114 | + str = str.replace(/>/g, '>'); | ||
115 | + str = str.replace(/•/g, '•'); | ||
116 | + | ||
117 | + return str; | ||
118 | +} | ||
119 | + | ||
120 | +// HTML 支持的其他实体 | ||
121 | +function strOtherDiscode(str){ | ||
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 | + str = str.replace(/›/g, '›'); | ||
154 | + str = str.replace(/‾/g, '‾'); | ||
155 | + str = str.replace(/€/g, '€'); | ||
156 | + str = str.replace(/™/g, '™'); | ||
157 | + | ||
158 | + str = str.replace(/←/g, '←'); | ||
159 | + str = str.replace(/↑/g, '↑'); | ||
160 | + str = str.replace(/→/g, '→'); | ||
161 | + str = str.replace(/↓/g, '↓'); | ||
162 | + str = str.replace(/↔/g, '↔'); | ||
163 | + str = str.replace(/↵/g, '↵'); | ||
164 | + str = str.replace(/⌈/g, '⌈'); | ||
165 | + str = str.replace(/⌉/g, '⌉'); | ||
166 | + | ||
167 | + str = str.replace(/⌊/g, '⌊'); | ||
168 | + str = str.replace(/⌋/g, '⌋'); | ||
169 | + str = str.replace(/◊/g, '◊'); | ||
170 | + str = str.replace(/♠/g, '♠'); | ||
171 | + str = str.replace(/♣/g, '♣'); | ||
172 | + str = str.replace(/♥/g, '♥'); | ||
173 | + | ||
174 | + str = str.replace(/♦/g, '♦'); | ||
175 | + str = str.replace(/'/g, '\''); | ||
176 | + return str; | ||
177 | +} | ||
178 | + | ||
179 | +function strMoreDiscode(str){ | ||
180 | + //str = str.replace(/\r\n/g,""); | ||
181 | + //str = str.replace(/\n/g,""); | ||
182 | + | ||
183 | + str = str.replace(/code/g,"wxxxcode-style"); | ||
184 | + return str; | ||
185 | +} | ||
186 | + | ||
187 | +function strDiscode(str){ | ||
188 | + str = strNumDiscode(str); | ||
189 | + str = strGreeceDiscode(str); | ||
190 | + str = strcharacterDiscode(str); | ||
191 | + str = strOtherDiscode(str); | ||
192 | + str = strMoreDiscode(str); | ||
193 | + return str; | ||
194 | +} | ||
195 | +function urlToHttpUrl(url,rep){ | ||
196 | + | ||
197 | + var patt1 = new RegExp("^//"); | ||
198 | + var result = patt1.test(url); | ||
199 | + if(result){ | ||
200 | + url = rep+":"+url; | ||
201 | + } | ||
202 | + return url; | ||
203 | +} | ||
204 | + | ||
205 | +module.exports = { | ||
206 | + strDiscode:strDiscode, | ||
207 | + urlToHttpUrl:urlToHttpUrl | ||
208 | +} |
wxParse/wxParse.js
0 → 100644
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 | +/** | ||
13 | + * utils函数引入 | ||
14 | + **/ | ||
15 | +import showdown from './showdown.js'; | ||
16 | +import HtmlToJson from './html2json.js'; | ||
17 | +/** | ||
18 | + * 配置及公有属性 | ||
19 | + **/ | ||
20 | +var realWindowWidth = 0; | ||
21 | +var realWindowHeight = 0; | ||
22 | +wx.getSystemInfo({ | ||
23 | + success: function (res) { | ||
24 | + realWindowWidth = res.windowWidth | ||
25 | + realWindowHeight = res.windowHeight | ||
26 | + } | ||
27 | +}) | ||
28 | +/** | ||
29 | + * 主函数入口区 | ||
30 | + **/ | ||
31 | +function wxParse(bindName = 'wxParseData', type='html', data='<div class="color:red;">数据不能为空</div>', target,imagePadding) { | ||
32 | + var that = target; | ||
33 | + var transData = {};//存放转化后的数据 | ||
34 | + if (type == 'html') { | ||
35 | + transData = HtmlToJson.html2json(data, bindName); | ||
36 | + // console.log(JSON.stringify(transData, ' ', ' ')); | ||
37 | + } else if (type == 'md' || type == 'markdown') { | ||
38 | + var converter = new showdown.Converter(); | ||
39 | + var html = converter.makeHtml(data); | ||
40 | + transData = HtmlToJson.html2json(html, bindName); | ||
41 | + // console.log(JSON.stringify(transData, ' ', ' ')); | ||
42 | + } | ||
43 | + transData.view = {}; | ||
44 | + transData.view.imagePadding = 0; | ||
45 | + if(typeof(imagePadding) != 'undefined'){ | ||
46 | + transData.view.imagePadding = imagePadding | ||
47 | + } | ||
48 | + var bindData = {}; | ||
49 | + bindData[bindName] = transData; | ||
50 | + that.setData(bindData) | ||
51 | + that.wxParseImgLoad = wxParseImgLoad; | ||
52 | + that.wxParseImgTap = wxParseImgTap; | ||
53 | +} | ||
54 | +// 图片点击事件 | ||
55 | +function wxParseImgTap(e) { | ||
56 | + var that = this; | ||
57 | + var nowImgUrl = e.target.dataset.src; | ||
58 | + var tagFrom = e.target.dataset.from; | ||
59 | + if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) { | ||
60 | + wx.previewImage({ | ||
61 | + current: nowImgUrl, // 当前显示图片的http链接 | ||
62 | + urls: that.data[tagFrom].imageUrls // 需要预览的图片http链接列表 | ||
63 | + }) | ||
64 | + } | ||
65 | +} | ||
66 | + | ||
67 | +/** | ||
68 | + * 图片视觉宽高计算函数区 | ||
69 | + **/ | ||
70 | +function wxParseImgLoad(e) { | ||
71 | + var that = this; | ||
72 | + var tagFrom = e.target.dataset.from; | ||
73 | + var idx = e.target.dataset.idx; | ||
74 | + if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) { | ||
75 | + calMoreImageInfo(e, idx, that, tagFrom) | ||
76 | + } | ||
77 | +} | ||
78 | +// 假循环获取计算图片视觉最佳宽高 | ||
79 | +function calMoreImageInfo(e, idx, that, bindName) { | ||
80 | + var temData = that.data[bindName]; | ||
81 | + if (!temData || temData.images.length == 0) { | ||
82 | + return; | ||
83 | + } | ||
84 | + var temImages = temData.images; | ||
85 | + //因为无法获取view宽度 需要自定义padding进行计算,稍后处理 | ||
86 | + var recal = wxAutoImageCal(e.detail.width, e.detail.height,that,bindName); | ||
87 | + // temImages[idx].width = recal.imageWidth; | ||
88 | + // temImages[idx].height = recal.imageheight; | ||
89 | + // temData.images = temImages; | ||
90 | + // var bindData = {}; | ||
91 | + // bindData[bindName] = temData; | ||
92 | + // that.setData(bindData); | ||
93 | + var index = temImages[idx].index | ||
94 | + var key = `${bindName}` | ||
95 | + for (var i of index.split('.')) key+=`.nodes[${i}]` | ||
96 | + var keyW = key + '.width' | ||
97 | + var keyH = key + '.height' | ||
98 | + that.setData({ | ||
99 | + [keyW]: recal.imageWidth, | ||
100 | + [keyH]: recal.imageheight, | ||
101 | + }) | ||
102 | +} | ||
103 | + | ||
104 | +// 计算视觉优先的图片宽高 | ||
105 | +function wxAutoImageCal(originalWidth, originalHeight,that,bindName) { | ||
106 | + //获取图片的原始长宽 | ||
107 | + var windowWidth = 0, windowHeight = 0; | ||
108 | + var autoWidth = 0, autoHeight = 0; | ||
109 | + var results = {}; | ||
110 | + var padding = that.data[bindName].view.imagePadding; | ||
111 | + windowWidth = realWindowWidth-2*padding; | ||
112 | + windowHeight = realWindowHeight; | ||
113 | + //判断按照那种方式进行缩放 | ||
114 | + // console.log("windowWidth" + windowWidth); | ||
115 | + if (originalWidth > windowWidth) {//在图片width大于手机屏幕width时候 | ||
116 | + autoWidth = windowWidth; | ||
117 | + // console.log("autoWidth" + autoWidth); | ||
118 | + autoHeight = (autoWidth * originalHeight) / originalWidth; | ||
119 | + // console.log("autoHeight" + autoHeight); | ||
120 | + results.imageWidth = autoWidth; | ||
121 | + results.imageheight = autoHeight; | ||
122 | + } else {//否则展示原来的数据 | ||
123 | + results.imageWidth = originalWidth; | ||
124 | + results.imageheight = originalHeight; | ||
125 | + } | ||
126 | + return results; | ||
127 | +} | ||
128 | + | ||
129 | +function wxParseTemArray(temArrayName,bindNameReg,total,that){ | ||
130 | + var array = []; | ||
131 | + var temData = that.data; | ||
132 | + var obj = null; | ||
133 | + for(var i = 0; i < total; i++){ | ||
134 | + var simArr = temData[bindNameReg+i].nodes; | ||
135 | + array.push(simArr); | ||
136 | + } | ||
137 | + | ||
138 | + temArrayName = temArrayName || 'wxParseTemArray'; | ||
139 | + obj = JSON.parse('{"'+ temArrayName +'":""}'); | ||
140 | + obj[temArrayName] = array; | ||
141 | + that.setData(obj); | ||
142 | +} | ||
143 | + | ||
144 | +/** | ||
145 | + * 配置emojis | ||
146 | + * | ||
147 | + */ | ||
148 | + | ||
149 | +function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){ | ||
150 | + HtmlToJson.emojisInit(reg,baseSrc,emojis); | ||
151 | +} | ||
152 | + | ||
153 | +module.exports = { | ||
154 | + wxParse: wxParse, | ||
155 | + wxParseTemArray:wxParseTemArray, | ||
156 | + emojisInit:emojisInit | ||
157 | +} | ||
158 | + | ||
159 | + |
wxParse/wxParse.wxml
0 → 100644
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 | +<!--基础元素--> | ||
13 | +<template name="wxParseVideo"> | ||
14 | + <!--增加video标签支持,并循环添加--> | ||
15 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
16 | + <video class="{{item.classStr}} wxParse-{{item.tag}}-video" src="{{item.attr.src}}"></video> | ||
17 | + </view> | ||
18 | +</template> | ||
19 | + | ||
20 | +<template name="wxParseImg"> | ||
21 | + <image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" mode="aspectFit" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:{{item.width}}px;" | ||
22 | + /> | ||
23 | +</template> | ||
24 | + | ||
25 | +<template name="WxEmojiView"> | ||
26 | + <view class="WxEmojiView wxParse-inline" style="{{item.styleStr}}"> | ||
27 | + <block wx:for="{{item.textArray}}" wx:key=""> | ||
28 | + <block class="{{item.text == '\\n' ? 'wxParse-hide':''}}" wx:if="{{item.node == 'text'}}">{{item.text}}</block> | ||
29 | + <block wx:elif="{{item.node == 'element'}}"> | ||
30 | + <image class="wxEmoji" src="{{item.baseSrc}}{{item.text}}" /> | ||
31 | + </block> | ||
32 | + </block> | ||
33 | + </view> | ||
34 | +</template> | ||
35 | + | ||
36 | +<template name="WxParseBr"> | ||
37 | + <text>\n</text> | ||
38 | +</template> | ||
39 | +<!--入口模版--> | ||
40 | + | ||
41 | +<template name="wxParse"> | ||
42 | + <block wx:for="{{wxParseData}}" wx:key=""> | ||
43 | + <template is="wxParse0" data="{{item}}" /> | ||
44 | + </block> | ||
45 | +</template> | ||
46 | + | ||
47 | + | ||
48 | +<!--循环模版--> | ||
49 | +<template name="wxParse0"> | ||
50 | + <!--<template is="wxParse1" data="{{item}}" />--> | ||
51 | + <!--判断是否是标签节点--> | ||
52 | + <block wx:if="{{item.node == 'element'}}"> | ||
53 | + <block wx:if="{{item.tag == 'button'}}"> | ||
54 | + <button type="default" size="mini"> | ||
55 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
56 | + <template is="wxParse1" data="{{item}}" /> | ||
57 | + </block> | ||
58 | + </button> | ||
59 | + </block> | ||
60 | + <!--li类型--> | ||
61 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
62 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
63 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
64 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
65 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
66 | + </view> | ||
67 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
68 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
69 | + <template is="wxParse1" data="{{item}}" /> | ||
70 | + </block> | ||
71 | + </view> | ||
72 | + </view> | ||
73 | + </view> | ||
74 | + </block> | ||
75 | + | ||
76 | + <!--video类型--> | ||
77 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
78 | + <template is="wxParseVideo" data="{{item}}" /> | ||
79 | + </block> | ||
80 | + | ||
81 | + <!--img类型--> | ||
82 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
83 | + <template is="wxParseImg" data="{{item}}" /> | ||
84 | + </block> | ||
85 | + | ||
86 | + <!--a类型--> | ||
87 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
88 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
89 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
90 | + <template is="wxParse1" data="{{item}}" /> | ||
91 | + </block> | ||
92 | + </view> | ||
93 | + </block> | ||
94 | + <block wx:elif="{{item.tag == 'table'}}"> | ||
95 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
96 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
97 | + <template is="wxParse1" data="{{item}}" /> | ||
98 | + </block> | ||
99 | + </view> | ||
100 | + </block> | ||
101 | + | ||
102 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
103 | + <template is="WxParseBr"></template> | ||
104 | + </block> | ||
105 | + <!--其他块级标签--> | ||
106 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
107 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
108 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
109 | + <template is="wxParse1" data="{{item}}" /> | ||
110 | + </block> | ||
111 | + </view> | ||
112 | + </block> | ||
113 | + | ||
114 | + <!--内联标签--> | ||
115 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
116 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
117 | + <template is="wxParse1" data="{{item}}" /> | ||
118 | + </block> | ||
119 | + </view> | ||
120 | + | ||
121 | + </block> | ||
122 | + | ||
123 | + <!--判断是否是文本节点--> | ||
124 | + <block wx:elif="{{item.node == 'text'}}"> | ||
125 | + <!--如果是,直接进行--> | ||
126 | + <template is="WxEmojiView" data="{{item}}" /> | ||
127 | + </block> | ||
128 | + | ||
129 | +</template> | ||
130 | + | ||
131 | + | ||
132 | + | ||
133 | +<!--循环模版--> | ||
134 | +<template name="wxParse1"> | ||
135 | + <!--<template is="wxParse2" data="{{item}}" />--> | ||
136 | + <!--判断是否是标签节点--> | ||
137 | + <block wx:if="{{item.node == 'element'}}"> | ||
138 | + <block wx:if="{{item.tag == 'button'}}"> | ||
139 | + <button type="default" size="mini"> | ||
140 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
141 | + <template is="wxParse2" data="{{item}}" /> | ||
142 | + </block> | ||
143 | + </button> | ||
144 | + </block> | ||
145 | + <!--li类型--> | ||
146 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
147 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
148 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
149 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
150 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
151 | + </view> | ||
152 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
153 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
154 | + <template is="wxParse2" data="{{item}}" /> | ||
155 | + </block> | ||
156 | + </view> | ||
157 | + </view> | ||
158 | + </view> | ||
159 | + </block> | ||
160 | + | ||
161 | + <!--video类型--> | ||
162 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
163 | + <template is="wxParseVideo" data="{{item}}" /> | ||
164 | + </block> | ||
165 | + | ||
166 | + <!--img类型--> | ||
167 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
168 | + <template is="wxParseImg" data="{{item}}" /> | ||
169 | + </block> | ||
170 | + | ||
171 | + <!--a类型--> | ||
172 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
173 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
174 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
175 | + <template is="wxParse2" data="{{item}}" /> | ||
176 | + </block> | ||
177 | + </view> | ||
178 | + </block> | ||
179 | + | ||
180 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
181 | + <template is="WxParseBr"></template> | ||
182 | + </block> | ||
183 | + <!--其他块级标签--> | ||
184 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
185 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
186 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
187 | + <template is="wxParse2" data="{{item}}" /> | ||
188 | + </block> | ||
189 | + </view> | ||
190 | + </block> | ||
191 | + | ||
192 | + <!--内联标签--> | ||
193 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
194 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
195 | + <template is="wxParse2" data="{{item}}" /> | ||
196 | + </block> | ||
197 | + </view> | ||
198 | + | ||
199 | + </block> | ||
200 | + | ||
201 | + <!--判断是否是文本节点--> | ||
202 | + <block wx:elif="{{item.node == 'text'}}"> | ||
203 | + <!--如果是,直接进行--> | ||
204 | + <template is="WxEmojiView" data="{{item}}" /> | ||
205 | + </block> | ||
206 | + | ||
207 | +</template> | ||
208 | + | ||
209 | + | ||
210 | +<!--循环模版--> | ||
211 | +<template name="wxParse2"> | ||
212 | + <!--<template is="wxParse3" data="{{item}}" />--> | ||
213 | + <!--判断是否是标签节点--> | ||
214 | + <block wx:if="{{item.node == 'element'}}"> | ||
215 | + <block wx:if="{{item.tag == 'button'}}"> | ||
216 | + <button type="default" size="mini"> | ||
217 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
218 | + <template is="wxParse3" data="{{item}}" /> | ||
219 | + </block> | ||
220 | + </button> | ||
221 | + </block> | ||
222 | + <!--li类型--> | ||
223 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
224 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
225 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
226 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
227 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
228 | + </view> | ||
229 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
230 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
231 | + <template is="wxParse3" data="{{item}}" /> | ||
232 | + </block> | ||
233 | + </view> | ||
234 | + </view> | ||
235 | + </view> | ||
236 | + </block> | ||
237 | + | ||
238 | + <!--video类型--> | ||
239 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
240 | + <template is="wxParseVideo" data="{{item}}" /> | ||
241 | + </block> | ||
242 | + | ||
243 | + <!--img类型--> | ||
244 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
245 | + <template is="wxParseImg" data="{{item}}" /> | ||
246 | + </block> | ||
247 | + | ||
248 | + <!--a类型--> | ||
249 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
250 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
251 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
252 | + <template is="wxParse3" data="{{item}}" /> | ||
253 | + </block> | ||
254 | + </view> | ||
255 | + </block> | ||
256 | + | ||
257 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
258 | + <template is="WxParseBr"></template> | ||
259 | + </block> | ||
260 | + <!--其他块级标签--> | ||
261 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
262 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
263 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
264 | + <template is="wxParse3" data="{{item}}" /> | ||
265 | + </block> | ||
266 | + </view> | ||
267 | + </block> | ||
268 | + | ||
269 | + <!--内联标签--> | ||
270 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
271 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
272 | + <template is="wxParse3" data="{{item}}" /> | ||
273 | + </block> | ||
274 | + </view> | ||
275 | + | ||
276 | + </block> | ||
277 | + | ||
278 | + <!--判断是否是文本节点--> | ||
279 | + <block wx:elif="{{item.node == 'text'}}"> | ||
280 | + <!--如果是,直接进行--> | ||
281 | + <template is="WxEmojiView" data="{{item}}" /> | ||
282 | + </block> | ||
283 | + | ||
284 | +</template> | ||
285 | + | ||
286 | +<!--循环模版--> | ||
287 | +<template name="wxParse3"> | ||
288 | + <!--<template is="wxParse4" data="{{item}}" />--> | ||
289 | + <!--判断是否是标签节点--> | ||
290 | + <block wx:if="{{item.node == 'element'}}"> | ||
291 | + <block wx:if="{{item.tag == 'button'}}"> | ||
292 | + <button type="default" size="mini"> | ||
293 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
294 | + <template is="wxParse4" data="{{item}}" /> | ||
295 | + </block> | ||
296 | + </button> | ||
297 | + </block> | ||
298 | + <!--li类型--> | ||
299 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
300 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
301 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
302 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
303 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
304 | + </view> | ||
305 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
306 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
307 | + <template is="wxParse4" data="{{item}}" /> | ||
308 | + </block> | ||
309 | + </view> | ||
310 | + </view> | ||
311 | + </view> | ||
312 | + </block> | ||
313 | + | ||
314 | + <!--video类型--> | ||
315 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
316 | + <template is="wxParseVideo" data="{{item}}" /> | ||
317 | + </block> | ||
318 | + | ||
319 | + <!--img类型--> | ||
320 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
321 | + <template is="wxParseImg" data="{{item}}" /> | ||
322 | + </block> | ||
323 | + | ||
324 | + <!--a类型--> | ||
325 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
326 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
327 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
328 | + <template is="wxParse4" data="{{item}}" /> | ||
329 | + </block> | ||
330 | + </view> | ||
331 | + </block> | ||
332 | + | ||
333 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
334 | + <template is="WxParseBr"></template> | ||
335 | + </block> | ||
336 | + <!--其他块级标签--> | ||
337 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
338 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
339 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
340 | + <template is="wxParse4" data="{{item}}" /> | ||
341 | + </block> | ||
342 | + </view> | ||
343 | + </block> | ||
344 | + | ||
345 | + <!--内联标签--> | ||
346 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
347 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
348 | + <template is="wxParse4" data="{{item}}" /> | ||
349 | + </block> | ||
350 | + </view> | ||
351 | + | ||
352 | + </block> | ||
353 | + | ||
354 | + <!--判断是否是文本节点--> | ||
355 | + <block wx:elif="{{item.node == 'text'}}"> | ||
356 | + <!--如果是,直接进行--> | ||
357 | + <template is="WxEmojiView" data="{{item}}" /> | ||
358 | + </block> | ||
359 | + | ||
360 | +</template> | ||
361 | + | ||
362 | +<!--循环模版--> | ||
363 | +<template name="wxParse4"> | ||
364 | + <!--<template is="wxParse5" data="{{item}}" />--> | ||
365 | + <!--判断是否是标签节点--> | ||
366 | + <block wx:if="{{item.node == 'element'}}"> | ||
367 | + <block wx:if="{{item.tag == 'button'}}"> | ||
368 | + <button type="default" size="mini"> | ||
369 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
370 | + <template is="wxParse5" data="{{item}}" /> | ||
371 | + </block> | ||
372 | + </button> | ||
373 | + </block> | ||
374 | + <!--li类型--> | ||
375 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
376 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
377 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
378 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
379 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
380 | + </view> | ||
381 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
382 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
383 | + <template is="wxParse5" data="{{item}}" /> | ||
384 | + </block> | ||
385 | + </view> | ||
386 | + </view> | ||
387 | + </view> | ||
388 | + </block> | ||
389 | + | ||
390 | + <!--video类型--> | ||
391 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
392 | + <template is="wxParseVideo" data="{{item}}" /> | ||
393 | + </block> | ||
394 | + | ||
395 | + <!--img类型--> | ||
396 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
397 | + <template is="wxParseImg" data="{{item}}" /> | ||
398 | + </block> | ||
399 | + | ||
400 | + <!--a类型--> | ||
401 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
402 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
403 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
404 | + <template is="wxParse5" data="{{item}}" /> | ||
405 | + </block> | ||
406 | + </view> | ||
407 | + </block> | ||
408 | + | ||
409 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
410 | + <template is="WxParseBr"></template> | ||
411 | + </block> | ||
412 | + <!--其他块级标签--> | ||
413 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
414 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
415 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
416 | + <template is="wxParse5" data="{{item}}" /> | ||
417 | + </block> | ||
418 | + </view> | ||
419 | + </block> | ||
420 | + | ||
421 | + <!--内联标签--> | ||
422 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
423 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
424 | + <template is="wxParse5" data="{{item}}" /> | ||
425 | + </block> | ||
426 | + </view> | ||
427 | + | ||
428 | + </block> | ||
429 | + | ||
430 | + <!--判断是否是文本节点--> | ||
431 | + <block wx:elif="{{item.node == 'text'}}"> | ||
432 | + <!--如果是,直接进行--> | ||
433 | + <template is="WxEmojiView" data="{{item}}" /> | ||
434 | + </block> | ||
435 | + | ||
436 | +</template> | ||
437 | + | ||
438 | +<!--循环模版--> | ||
439 | +<template name="wxParse5"> | ||
440 | + <!--<template is="wxParse6" data="{{item}}" />--> | ||
441 | + <!--判断是否是标签节点--> | ||
442 | + <block wx:if="{{item.node == 'element'}}"> | ||
443 | + <block wx:if="{{item.tag == 'button'}}"> | ||
444 | + <button type="default" size="mini"> | ||
445 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
446 | + <template is="wxParse6" data="{{item}}" /> | ||
447 | + </block> | ||
448 | + </button> | ||
449 | + </block> | ||
450 | + <!--li类型--> | ||
451 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
452 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
453 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
454 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
455 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
456 | + </view> | ||
457 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
458 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
459 | + <template is="wxParse6" data="{{item}}" /> | ||
460 | + </block> | ||
461 | + </view> | ||
462 | + </view> | ||
463 | + </view> | ||
464 | + </block> | ||
465 | + | ||
466 | + <!--video类型--> | ||
467 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
468 | + <template is="wxParseVideo" data="{{item}}" /> | ||
469 | + </block> | ||
470 | + | ||
471 | + <!--img类型--> | ||
472 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
473 | + <template is="wxParseImg" data="{{item}}" /> | ||
474 | + </block> | ||
475 | + | ||
476 | + <!--a类型--> | ||
477 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
478 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
479 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
480 | + <template is="wxParse6" data="{{item}}" /> | ||
481 | + </block> | ||
482 | + </view> | ||
483 | + </block> | ||
484 | + | ||
485 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
486 | + <template is="WxParseBr"></template> | ||
487 | + </block> | ||
488 | + <!--其他块级标签--> | ||
489 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
490 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
491 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
492 | + <template is="wxParse6" data="{{item}}" /> | ||
493 | + </block> | ||
494 | + </view> | ||
495 | + </block> | ||
496 | + | ||
497 | + <!--内联标签--> | ||
498 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
499 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
500 | + <template is="wxParse6" data="{{item}}" /> | ||
501 | + </block> | ||
502 | + </view> | ||
503 | + | ||
504 | + </block> | ||
505 | + | ||
506 | + <!--判断是否是文本节点--> | ||
507 | + <block wx:elif="{{item.node == 'text'}}"> | ||
508 | + <!--如果是,直接进行--> | ||
509 | + <template is="WxEmojiView" data="{{item}}" /> | ||
510 | + </block> | ||
511 | + | ||
512 | +</template> | ||
513 | + | ||
514 | +<!--循环模版--> | ||
515 | +<template name="wxParse6"> | ||
516 | + <!--<template is="wxParse7" data="{{item}}" />--> | ||
517 | + <!--判断是否是标签节点--> | ||
518 | + <block wx:if="{{item.node == 'element'}}"> | ||
519 | + <block wx:if="{{item.tag == 'button'}}"> | ||
520 | + <button type="default" size="mini"> | ||
521 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
522 | + <template is="wxParse7" data="{{item}}" /> | ||
523 | + </block> | ||
524 | + </button> | ||
525 | + </block> | ||
526 | + <!--li类型--> | ||
527 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
528 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
529 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
530 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
531 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
532 | + </view> | ||
533 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
534 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
535 | + <template is="wxParse7" data="{{item}}" /> | ||
536 | + </block> | ||
537 | + </view> | ||
538 | + </view> | ||
539 | + </view> | ||
540 | + </block> | ||
541 | + | ||
542 | + <!--video类型--> | ||
543 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
544 | + <template is="wxParseVideo" data="{{item}}" /> | ||
545 | + </block> | ||
546 | + | ||
547 | + <!--img类型--> | ||
548 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
549 | + <template is="wxParseImg" data="{{item}}" /> | ||
550 | + </block> | ||
551 | + | ||
552 | + <!--a类型--> | ||
553 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
554 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
555 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
556 | + <template is="wxParse7" data="{{item}}" /> | ||
557 | + </block> | ||
558 | + </view> | ||
559 | + </block> | ||
560 | + | ||
561 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
562 | + <template is="WxParseBr"></template> | ||
563 | + </block> | ||
564 | + <!--其他块级标签--> | ||
565 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
566 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
567 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
568 | + <template is="wxParse7" data="{{item}}" /> | ||
569 | + </block> | ||
570 | + </view> | ||
571 | + </block> | ||
572 | + | ||
573 | + <!--内联标签--> | ||
574 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
575 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
576 | + <template is="wxParse7" data="{{item}}" /> | ||
577 | + </block> | ||
578 | + </view> | ||
579 | + | ||
580 | + </block> | ||
581 | + | ||
582 | + <!--判断是否是文本节点--> | ||
583 | + <block wx:elif="{{item.node == 'text'}}"> | ||
584 | + <!--如果是,直接进行--> | ||
585 | + <template is="WxEmojiView" data="{{item}}" /> | ||
586 | + </block> | ||
587 | + | ||
588 | +</template> | ||
589 | +<!--循环模版--> | ||
590 | +<template name="wxParse7"> | ||
591 | + <!--<template is="wxParse8" data="{{item}}" />--> | ||
592 | + <!--判断是否是标签节点--> | ||
593 | + <block wx:if="{{item.node == 'element'}}"> | ||
594 | + <block wx:if="{{item.tag == 'button'}}"> | ||
595 | + <button type="default" size="mini"> | ||
596 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
597 | + <template is="wxParse8" data="{{item}}" /> | ||
598 | + </block> | ||
599 | + </button> | ||
600 | + </block> | ||
601 | + <!--li类型--> | ||
602 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
603 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
604 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
605 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
606 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
607 | + </view> | ||
608 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
609 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
610 | + <template is="wxParse8" data="{{item}}" /> | ||
611 | + </block> | ||
612 | + </view> | ||
613 | + </view> | ||
614 | + </view> | ||
615 | + </block> | ||
616 | + | ||
617 | + <!--video类型--> | ||
618 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
619 | + <template is="wxParseVideo" data="{{item}}" /> | ||
620 | + </block> | ||
621 | + | ||
622 | + <!--img类型--> | ||
623 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
624 | + <template is="wxParseImg" data="{{item}}" /> | ||
625 | + </block> | ||
626 | + | ||
627 | + <!--a类型--> | ||
628 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
629 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
630 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
631 | + <template is="wxParse8" data="{{item}}" /> | ||
632 | + </block> | ||
633 | + </view> | ||
634 | + </block> | ||
635 | + | ||
636 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
637 | + <template is="WxParseBr"></template> | ||
638 | + </block> | ||
639 | + <!--其他块级标签--> | ||
640 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
641 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
642 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
643 | + <template is="wxParse8" data="{{item}}" /> | ||
644 | + </block> | ||
645 | + </view> | ||
646 | + </block> | ||
647 | + | ||
648 | + <!--内联标签--> | ||
649 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
650 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
651 | + <template is="wxParse8" data="{{item}}" /> | ||
652 | + </block> | ||
653 | + </view> | ||
654 | + | ||
655 | + </block> | ||
656 | + | ||
657 | + <!--判断是否是文本节点--> | ||
658 | + <block wx:elif="{{item.node == 'text'}}"> | ||
659 | + <!--如果是,直接进行--> | ||
660 | + <template is="WxEmojiView" data="{{item}}" /> | ||
661 | + </block> | ||
662 | + | ||
663 | +</template> | ||
664 | + | ||
665 | +<!--循环模版--> | ||
666 | +<template name="wxParse8"> | ||
667 | + <!--<template is="wxParse9" data="{{item}}" />--> | ||
668 | + <!--判断是否是标签节点--> | ||
669 | + <block wx:if="{{item.node == 'element'}}"> | ||
670 | + <block wx:if="{{item.tag == 'button'}}"> | ||
671 | + <button type="default" size="mini"> | ||
672 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
673 | + <template is="wxParse9" data="{{item}}" /> | ||
674 | + </block> | ||
675 | + </button> | ||
676 | + </block> | ||
677 | + <!--li类型--> | ||
678 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
679 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
680 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
681 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
682 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
683 | + </view> | ||
684 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
685 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
686 | + <template is="wxParse9" data="{{item}}" /> | ||
687 | + </block> | ||
688 | + </view> | ||
689 | + </view> | ||
690 | + </view> | ||
691 | + </block> | ||
692 | + | ||
693 | + <!--video类型--> | ||
694 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
695 | + <template is="wxParseVideo" data="{{item}}" /> | ||
696 | + </block> | ||
697 | + | ||
698 | + <!--img类型--> | ||
699 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
700 | + <template is="wxParseImg" data="{{item}}" /> | ||
701 | + </block> | ||
702 | + | ||
703 | + <!--a类型--> | ||
704 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
705 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
706 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
707 | + <template is="wxParse9" data="{{item}}" /> | ||
708 | + </block> | ||
709 | + </view> | ||
710 | + </block> | ||
711 | + | ||
712 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
713 | + <template is="WxParseBr"></template> | ||
714 | + </block> | ||
715 | + <!--其他块级标签--> | ||
716 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
717 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
718 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
719 | + <template is="wxParse9" data="{{item}}" /> | ||
720 | + </block> | ||
721 | + </view> | ||
722 | + </block> | ||
723 | + | ||
724 | + <!--内联标签--> | ||
725 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
726 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
727 | + <template is="wxParse9" data="{{item}}" /> | ||
728 | + </block> | ||
729 | + </view> | ||
730 | + | ||
731 | + </block> | ||
732 | + | ||
733 | + <!--判断是否是文本节点--> | ||
734 | + <block wx:elif="{{item.node == 'text'}}"> | ||
735 | + <!--如果是,直接进行--> | ||
736 | + <template is="WxEmojiView" data="{{item}}" /> | ||
737 | + </block> | ||
738 | + | ||
739 | +</template> | ||
740 | + | ||
741 | +<!--循环模版--> | ||
742 | +<template name="wxParse9"> | ||
743 | + <!--<template is="wxParse10" data="{{item}}" />--> | ||
744 | + <!--判断是否是标签节点--> | ||
745 | + <block wx:if="{{item.node == 'element'}}"> | ||
746 | + <block wx:if="{{item.tag == 'button'}}"> | ||
747 | + <button type="default" size="mini"> | ||
748 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
749 | + <template is="wxParse10" data="{{item}}" /> | ||
750 | + </block> | ||
751 | + </button> | ||
752 | + </block> | ||
753 | + <!--li类型--> | ||
754 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
755 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
756 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
757 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
758 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
759 | + </view> | ||
760 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
761 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
762 | + <template is="wxParse10" data="{{item}}" /> | ||
763 | + </block> | ||
764 | + </view> | ||
765 | + </view> | ||
766 | + </view> | ||
767 | + </block> | ||
768 | + | ||
769 | + <!--video类型--> | ||
770 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
771 | + <template is="wxParseVideo" data="{{item}}" /> | ||
772 | + </block> | ||
773 | + | ||
774 | + <!--img类型--> | ||
775 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
776 | + <template is="wxParseImg" data="{{item}}" /> | ||
777 | + </block> | ||
778 | + | ||
779 | + <!--a类型--> | ||
780 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
781 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
782 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
783 | + <template is="wxParse10" data="{{item}}" /> | ||
784 | + </block> | ||
785 | + </view> | ||
786 | + </block> | ||
787 | + | ||
788 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
789 | + <template is="WxParseBr"></template> | ||
790 | + </block> | ||
791 | + <!--其他块级标签--> | ||
792 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
793 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
794 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
795 | + <template is="wxParse10" data="{{item}}" /> | ||
796 | + </block> | ||
797 | + </view> | ||
798 | + </block> | ||
799 | + | ||
800 | + <!--内联标签--> | ||
801 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
802 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
803 | + <template is="wxParse10" data="{{item}}" /> | ||
804 | + </block> | ||
805 | + </view> | ||
806 | + | ||
807 | + </block> | ||
808 | + | ||
809 | + <!--判断是否是文本节点--> | ||
810 | + <block wx:elif="{{item.node == 'text'}}"> | ||
811 | + <!--如果是,直接进行--> | ||
812 | + <template is="WxEmojiView" data="{{item}}" /> | ||
813 | + </block> | ||
814 | + | ||
815 | +</template> | ||
816 | + | ||
817 | +<!--循环模版--> | ||
818 | +<template name="wxParse10"> | ||
819 | + <!--<template is="wxParse11" data="{{item}}" />--> | ||
820 | + <!--判断是否是标签节点--> | ||
821 | + <block wx:if="{{item.node == 'element'}}"> | ||
822 | + <block wx:if="{{item.tag == 'button'}}"> | ||
823 | + <button type="default" size="mini"> | ||
824 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
825 | + <template is="wxParse11" data="{{item}}" /> | ||
826 | + </block> | ||
827 | + </button> | ||
828 | + </block> | ||
829 | + <!--li类型--> | ||
830 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
831 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
832 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
833 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
834 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
835 | + </view> | ||
836 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
837 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
838 | + <template is="wxParse11" data="{{item}}" /> | ||
839 | + </block> | ||
840 | + </view> | ||
841 | + </view> | ||
842 | + </view> | ||
843 | + </block> | ||
844 | + | ||
845 | + <!--video类型--> | ||
846 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
847 | + <template is="wxParseVideo" data="{{item}}" /> | ||
848 | + </block> | ||
849 | + | ||
850 | + <!--img类型--> | ||
851 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
852 | + <template is="wxParseImg" data="{{item}}" /> | ||
853 | + </block> | ||
854 | + | ||
855 | + <!--a类型--> | ||
856 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
857 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
858 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
859 | + <template is="wxParse11" data="{{item}}" /> | ||
860 | + </block> | ||
861 | + </view> | ||
862 | + </block> | ||
863 | + | ||
864 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
865 | + <template is="WxParseBr"></template> | ||
866 | + </block> | ||
867 | + <!--其他块级标签--> | ||
868 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
869 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
870 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
871 | + <template is="wxParse11" data="{{item}}" /> | ||
872 | + </block> | ||
873 | + </view> | ||
874 | + </block> | ||
875 | + | ||
876 | + <!--内联标签--> | ||
877 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
878 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
879 | + <template is="wxParse11" data="{{item}}" /> | ||
880 | + </block> | ||
881 | + </view> | ||
882 | + | ||
883 | + </block> | ||
884 | + | ||
885 | + <!--判断是否是文本节点--> | ||
886 | + <block wx:elif="{{item.node == 'text'}}"> | ||
887 | + <!--如果是,直接进行--> | ||
888 | + <template is="WxEmojiView" data="{{item}}" /> | ||
889 | + </block> | ||
890 | + | ||
891 | +</template> | ||
892 | + | ||
893 | +<!--循环模版--> | ||
894 | +<template name="wxParse11"> | ||
895 | + <!--<template is="wxParse12" data="{{item}}" />--> | ||
896 | + <!--判断是否是标签节点--> | ||
897 | + <block wx:if="{{item.node == 'element'}}"> | ||
898 | + <block wx:if="{{item.tag == 'button'}}"> | ||
899 | + <button type="default" size="mini"> | ||
900 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
901 | + <template is="wxParse12" data="{{item}}" /> | ||
902 | + </block> | ||
903 | + </button> | ||
904 | + </block> | ||
905 | + <!--li类型--> | ||
906 | + <block wx:elif="{{item.tag == 'li'}}"> | ||
907 | + <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}"> | ||
908 | + <view class="{{item.classStr}} wxParse-li-inner"> | ||
909 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
910 | + <view class="{{item.classStr}} wxParse-li-circle"></view> | ||
911 | + </view> | ||
912 | + <view class="{{item.classStr}} wxParse-li-text"> | ||
913 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
914 | + <template is="wxParse12" data="{{item}}" /> | ||
915 | + </block> | ||
916 | + </view> | ||
917 | + </view> | ||
918 | + </view> | ||
919 | + </block> | ||
920 | + | ||
921 | + <!--video类型--> | ||
922 | + <block wx:elif="{{item.tag == 'video'}}"> | ||
923 | + <template is="wxParseVideo" data="{{item}}" /> | ||
924 | + </block> | ||
925 | + | ||
926 | + <!--img类型--> | ||
927 | + <block wx:elif="{{item.tag == 'img'}}"> | ||
928 | + <template is="wxParseImg" data="{{item}}" /> | ||
929 | + </block> | ||
930 | + | ||
931 | + <!--a类型--> | ||
932 | + <block wx:elif="{{item.tag == 'a'}}"> | ||
933 | + <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}"> | ||
934 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
935 | + <template is="wxParse12" data="{{item}}" /> | ||
936 | + </block> | ||
937 | + </view> | ||
938 | + </block> | ||
939 | + | ||
940 | + <block wx:elif="{{item.tag == 'br'}}"> | ||
941 | + <template is="WxParseBr"></template> | ||
942 | + </block> | ||
943 | + <!--其他块级标签--> | ||
944 | + <block wx:elif="{{item.tagType == 'block'}}"> | ||
945 | + <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> | ||
946 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
947 | + <template is="wxParse12" data="{{item}}" /> | ||
948 | + </block> | ||
949 | + </view> | ||
950 | + </block> | ||
951 | + | ||
952 | + <!--内联标签--> | ||
953 | + <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}"> | ||
954 | + <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> | ||
955 | + <template is="wxParse12" data="{{item}}" /> | ||
956 | + </block> | ||
957 | + </view> | ||
958 | + | ||
959 | + </block> | ||
960 | + | ||
961 | + <!--判断是否是文本节点--> | ||
962 | + <block wx:elif="{{item.node == 'text'}}"> | ||
963 | + <!--如果是,直接进行--> | ||
964 | + <template is="WxEmojiView" data="{{item}}" /> | ||
965 | + </block> | ||
966 | + | ||
967 | +</template> |
wxParse/wxParse.wxss
0 → 100644
1 | + | ||
2 | +/** | ||
3 | + * author: Di (微信小程序开发工程师) | ||
4 | + * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) | ||
5 | + * 垂直微信小程序开发交流社区 | ||
6 | + * | ||
7 | + * github地址: https://github.com/icindy/wxParse | ||
8 | + * | ||
9 | + * for: 微信小程序富文本解析 | ||
10 | + * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 | ||
11 | + */ | ||
12 | + | ||
13 | +.wxParse{ | ||
14 | + margin: 0 5px; | ||
15 | + font-family: Helvetica,sans-serif; | ||
16 | + font-size: 28rpx; | ||
17 | + color: #666; | ||
18 | + line-height: 1.8; | ||
19 | +} | ||
20 | +view{ | ||
21 | + word-break:break-all; overflow:auto; | ||
22 | +} | ||
23 | +.wxParse-inline{ | ||
24 | + display: inline; | ||
25 | + margin: 0; | ||
26 | + padding: 0; | ||
27 | +} | ||
28 | +/*//标题 */ | ||
29 | +.wxParse-div{margin: 0;padding: 0;} | ||
30 | +.wxParse-h1{ font-size:2em; margin: .67em 0 } | ||
31 | +.wxParse-h2{ font-size:1.5em; margin: .75em 0 } | ||
32 | +.wxParse-h3{ font-size:1.17em; margin: .83em 0 } | ||
33 | +.wxParse-h4{ margin: 1.12em 0} | ||
34 | +.wxParse-h5 { font-size:.83em; margin: 1.5em 0 } | ||
35 | +.wxParse-h6{ font-size:.75em; margin: 1.67em 0 } | ||
36 | + | ||
37 | +.wxParse-h1 { | ||
38 | + font-size: 18px; | ||
39 | + font-weight: 400; | ||
40 | + margin-bottom: .9em; | ||
41 | +} | ||
42 | +.wxParse-h2 { | ||
43 | + font-size: 16px; | ||
44 | + font-weight: 400; | ||
45 | + margin-bottom: .34em; | ||
46 | +} | ||
47 | +.wxParse-h3 { | ||
48 | + font-weight: 400; | ||
49 | + font-size: 15px; | ||
50 | + margin-bottom: .34em; | ||
51 | +} | ||
52 | +.wxParse-h4 { | ||
53 | + font-weight: 400; | ||
54 | + font-size: 14px; | ||
55 | + margin-bottom: .24em; | ||
56 | +} | ||
57 | +.wxParse-h5 { | ||
58 | + font-weight: 400; | ||
59 | + font-size: 13px; | ||
60 | + margin-bottom: .14em; | ||
61 | +} | ||
62 | +.wxParse-h6 { | ||
63 | + font-weight: 400; | ||
64 | + font-size: 12px; | ||
65 | + margin-bottom: .04em; | ||
66 | +} | ||
67 | + | ||
68 | +.wxParse-h1, .wxParse-h2, .wxParse-h3, .wxParse-h4, .wxParse-h5, .wxParse-h6, .wxParse-b, .wxParse-strong { font-weight: bolder } | ||
69 | + | ||
70 | +.wxParse-i,.wxParse-cite,.wxParse-em,.wxParse-var,.wxParse-address{font-style:italic} | ||
71 | +.wxParse-pre,.wxParse-tt,.wxParse-code,.wxParse-kbd,.wxParse-samp{font-family:monospace} | ||
72 | +.wxParse-pre{white-space:pre} | ||
73 | +.wxParse-big{font-size:1.17em} | ||
74 | +.wxParse-small,.wxParse-sub,.wxParse-sup{font-size:.83em} | ||
75 | +.wxParse-sub{vertical-align:sub} | ||
76 | +.wxParse-sup{vertical-align:super} | ||
77 | +.wxParse-s,.wxParse-strike,.wxParse-del{text-decoration:line-through} | ||
78 | +/*wxparse-自定义个性化的css样式*/ | ||
79 | +/*增加video的css样式*/ | ||
80 | +.wxParse-strong,.wxParse-s{display: inline} | ||
81 | +.wxParse-a{ | ||
82 | + color: deepskyblue; | ||
83 | + word-break:break-all; | ||
84 | + overflow:auto; | ||
85 | +} | ||
86 | + | ||
87 | +.wxParse-video{ | ||
88 | + text-align: center; | ||
89 | + margin: 10px 0; | ||
90 | +} | ||
91 | + | ||
92 | +.wxParse-video-video{ | ||
93 | + width:100%; | ||
94 | +} | ||
95 | + | ||
96 | +.wxParse-img{ | ||
97 | + /*background-color: #efefef;*/ | ||
98 | + overflow: hidden; | ||
99 | +} | ||
100 | + | ||
101 | +.wxParse-blockquote { | ||
102 | + margin: 0; | ||
103 | + padding:10px 0 10px 5px; | ||
104 | + font-family:Courier, Calibri,"宋体"; | ||
105 | + background:#f5f5f5; | ||
106 | + border-left: 3px solid #dbdbdb; | ||
107 | +} | ||
108 | + | ||
109 | +.wxParse-code,.wxParse-wxxxcode-style{ | ||
110 | + display: inline; | ||
111 | + background:#f5f5f5; | ||
112 | +} | ||
113 | +.wxParse-ul{ | ||
114 | + margin: 20rpx 10rpx; | ||
115 | +} | ||
116 | + | ||
117 | +.wxParse-li,.wxParse-li-inner{ | ||
118 | + display: flex; | ||
119 | + align-items: baseline; | ||
120 | + margin: 10rpx 0; | ||
121 | +} | ||
122 | +.wxParse-li-text{ | ||
123 | + | ||
124 | + align-items: center; | ||
125 | + line-height: 20px; | ||
126 | +} | ||
127 | + | ||
128 | +.wxParse-li-circle{ | ||
129 | + display: inline-flex; | ||
130 | + width: 5px; | ||
131 | + height: 5px; | ||
132 | + background-color: #333; | ||
133 | + margin-right: 5px; | ||
134 | +} | ||
135 | + | ||
136 | +.wxParse-li-square{ | ||
137 | + display: inline-flex; | ||
138 | + width: 10rpx; | ||
139 | + height: 10rpx; | ||
140 | + background-color: #333; | ||
141 | + margin-right: 5px; | ||
142 | +} | ||
143 | +.wxParse-li-ring{ | ||
144 | + display: inline-flex; | ||
145 | + width: 10rpx; | ||
146 | + height: 10rpx; | ||
147 | + border: 2rpx solid #333; | ||
148 | + border-radius: 50%; | ||
149 | + background-color: #fff; | ||
150 | + margin-right: 5px; | ||
151 | +} | ||
152 | + | ||
153 | +/*.wxParse-table{ | ||
154 | + width: 100%; | ||
155 | + height: 400px; | ||
156 | +} | ||
157 | +.wxParse-thead,.wxParse-tfoot,.wxParse-tr{ | ||
158 | + display: flex; | ||
159 | + flex-direction: row; | ||
160 | +} | ||
161 | +.wxParse-th,.wxParse-td{ | ||
162 | + display: flex; | ||
163 | + width: 580px; | ||
164 | + overflow: auto; | ||
165 | +}*/ | ||
166 | + | ||
167 | +.wxParse-u { | ||
168 | + text-decoration: underline; | ||
169 | +} | ||
170 | +.wxParse-hide{ | ||
171 | + display: none; | ||
172 | +} | ||
173 | +.WxEmojiView{ | ||
174 | + align-items: center; | ||
175 | +} | ||
176 | +.wxEmoji{ | ||
177 | + width: 16px; | ||
178 | + height:16px; | ||
179 | +} | ||
180 | +.wxParse-tr{ | ||
181 | + display: flex; | ||
182 | + border-right:1px solid #e0e0e0; | ||
183 | + border-bottom:1px solid #e0e0e0; | ||
184 | + border-top:1px solid #e0e0e0; | ||
185 | +} | ||
186 | +.wxParse-th, | ||
187 | +.wxParse-td{ | ||
188 | + flex:1; | ||
189 | + padding:5px; | ||
190 | + font-size:28rpx; | ||
191 | + border-left:1px solid #e0e0e0; | ||
192 | + word-break: break-all; | ||
193 | +} | ||
194 | +.wxParse-td:last{ | ||
195 | + border-top:1px solid #e0e0e0; | ||
196 | +} | ||
197 | +.wxParse-th{ | ||
198 | + background:#f0f0f0; | ||
199 | + border-top:1px solid #e0e0e0; | ||
200 | +} | ||
201 | +.wxParse-del{ | ||
202 | + display: inline; | ||
203 | +} | ||
204 | +.wxParse-figure { | ||
205 | + overflow: hidden; | ||
206 | +} |
-
请 注册 或 登录 后发表评论