[docs] Use marked instead of Showdown and escape HTML
Fixes #3501.
Ben Alpert committed
Apr 13, 2015 at 15:50 UTC
36aefcb8cc68e35dbe0d416a0cd0210c904a47e4
6 files changed
+22
-1322
docs/_js/examples/markdown.js
+1
-3
@@ -1,6 +1,4 @@
1
var MARKDOWN_COMPONENT = `
2
-var converter = new Showdown.converter();
3
-
2
var MarkdownEditor = React.createClass({
3
getInitialState: function() {
4
return {value: 'Type some *markdown* here!'};
@@ -20,7 +18,7 @@ var MarkdownEditor = React.createClass({
18
<div
19
className="content"
20
dangerouslySetInnerHTML={{
23
- __html: converter.makeHtml(this.state.value)
21
+ __html: marked(this.state.value, {sanitize: true})
22
}}
23
/>
24
</div>
docs/_layouts/default.html
-1
@@ -33,7 +33,6 @@
33
<script type="text/javascript" src="/react/js/react.js"></script>
34
<script type="text/javascript" src="/react/js/JSXTransformer.js"></script>
35
<script type="text/javascript" src="/react/js/live_editor.js"></script>
36
- <script type="text/javascript" src="/react/js/showdown.js"></script>
36
</head>
37
<body>
38
docs/docs/tutorial.md
+9
-11
@@ -212,7 +212,7 @@ Note that we have passed some data from the parent `CommentList` component to th
212
213
Markdown is a simple way to format your text inline. For example, surrounding text with asterisks will make it emphasized.
214
215
-First, add the third-party **Showdown** library to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. This requires a script tag in your head (which we have already included in the React playground):
215
+First, add the third-party library **marked** to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. This requires a script tag in your head (which we have already included in the React playground):
216
217
```html{7}
218
<!-- index.html -->
@@ -221,15 +221,14 @@ First, add the third-party **Showdown** library to your application. This is a J
221
<script src="https://fb.me/react-{{site.react_version}}.js"></script>
222
<script src="https://fb.me/JSXTransformer-{{site.react_version}}.js"></script>
223
<script src="https://code.jquery.com/jquery-1.10.0.min.js"></script>
224
- <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>
224
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js"></script>
225
</head>
226
```
227
228
Next, let's convert the comment text to Markdown and output it:
229
230
-```javascript{2,10}
230
+```javascript{9}
231
// tutorial6.js
232
-var converter = new Showdown.converter();
232
var Comment = React.createClass({
233
render: function() {
234
return (
@@ -237,25 +236,24 @@ var Comment = React.createClass({
236
<h2 className="commentAuthor">
237
{this.props.author}
238
</h2>
240
- {converter.makeHtml(this.props.children.toString())}
239
+ {marked(this.props.children.toString())}
240
</div>
241
);
242
}
243
});
244
```
245
247
-All we're doing here is calling the Showdown library. We need to convert `this.props.children` from React's wrapped text to a raw string that Showdown will understand so we explicitly call `toString()`.
246
+All we're doing here is calling the marked library. We need to convert `this.props.children` from React's wrapped text to a raw string that marked will understand so we explicitly call `toString()`.
247
248
But there's a problem! Our rendered comments look like this in the browser: "`<p>`This is `<em>`another`</em>` comment`</p>`". We want those tags to actually render as HTML.
249
250
That's React protecting you from an XSS attack. There's a way to get around it but the framework warns you not to use it:
251
253
-```javascript{5,11}
252
+```javascript{4,10}
253
// tutorial7.js
255
-var converter = new Showdown.converter();
254
var Comment = React.createClass({
255
render: function() {
258
- var rawMarkup = converter.makeHtml(this.props.children.toString());
256
+ var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
257
return (
258
<div className="comment">
259
<h2 className="commentAuthor">
@@ -268,9 +266,9 @@ var Comment = React.createClass({
266
});
267
```
268
271
-This is a special API that intentionally makes it difficult to insert raw HTML, but for Showdown we'll take advantage of this backdoor.
269
+This is a special API that intentionally makes it difficult to insert raw HTML, but for marked we'll take advantage of this backdoor.
270
273
-**Remember:** by using this feature you're relying on Showdown to be secure.
271
+**Remember:** by using this feature you're relying on marked to be secure. In this case, we pass `sanitize: true` which tells marked to escape any HTML markup in the source instead of passing it through unchanged.
272
273
### Hook up the data model
274
docs/index.md
+6
-5
@@ -72,16 +72,17 @@ id: home
72
<h3>A Component Using External Plugins</h3>
73
<p>
74
React is flexible and provides hooks that allow you to interface with
75
- other libraries and frameworks. This example uses Showdown, an external
75
+ other libraries and frameworks. This example uses **marked**, an external
76
Markdown library, to convert the textarea's value in real-time.
77
</p>
78
<div id="markdownExample"></div>
79
</div>
80
</div>
81
- <script type="text/javascript" src="js/examples/hello.js"></script>
82
- <script type="text/javascript" src="js/examples/timer.js"></script>
83
- <script type="text/javascript" src="js/examples/todo.js"></script>
84
- <script type="text/javascript" src="js/examples/markdown.js"></script>
81
+ <script type="text/javascript" src="/react/js/marked.min.js"></script>
82
+ <script type="text/javascript" src="/react/js/examples/hello.js"></script>
83
+ <script type="text/javascript" src="/react/js/examples/timer.js"></script>
84
+ <script type="text/javascript" src="/react/js/examples/todo.js"></script>
85
+ <script type="text/javascript" src="/react/js/examples/markdown.js"></script>
86
</section>
87
<hr class="home-divider" />
88
<section class="home-bottom-section">
docs/js/marked.min.js
new
+6
@@ -0,0 +1,6 @@
1
+/**
2
+ * marked - a markdown parser
3
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
4
+ * https://github.com/chjj/marked
5
+ */
6
+(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *\| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script"||cap[1]==="style",text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^<\/a>/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(this.smartypants(cap[0]));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"—").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"<pre><code>"+(escaped?code:escape(code,true))+"\n</code></pre>"}return'<pre><code class="'+this.options.langPrefix+escape(lang,true)+'">'+(escaped?code:escape(code,true))+"\n</code></pre>\n"};Renderer.prototype.blockquote=function(quote){return"<blockquote>\n"+quote+"</blockquote>\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+'">'+text+"</h"+level+">\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"</"+type+">\n"};Renderer.prototype.listitem=function(text){return"<li>"+text+"</li>\n"};Renderer.prototype.paragraph=function(text){return"<p>"+text+"</p>\n"};Renderer.prototype.table=function(header,body){return"<table>\n"+"<thead>\n"+header+"</thead>\n"+"<tbody>\n"+body+"</tbody>\n"+"</table>\n"};Renderer.prototype.tablerow=function(content){return"<tr>\n"+content+"</tr>\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">\n"};Renderer.prototype.strong=function(text){return"<strong>"+text+"</strong>"};Renderer.prototype.em=function(text){return"<em>"+text+"</em>"};Renderer.prototype.codespan=function(text){return"<code>"+text+"</code>"};Renderer.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"};Renderer.prototype.del=function(text){return"<del>"+text+"</del>"};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0){return""}}var out='<a href="'+href+'"';if(title){out+=' title="'+title+'"'}out+=">"+text+"</a>";return out};Renderer.prototype.image=function(href,title,text){var out='<img src="'+href+'" alt="'+text+'"';if(title){out+=' title="'+title+'"'}out+=this.options.xhtml?"/>":">";return out};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(){var out,err;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"<p>An error occured:</p><pre>"+escape(e.message+"",true)+"</pre>"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());
\ No newline at end of file
docs/js/showdown.js
deleted
-1302
@@ -1,1302 +0,0 @@
1
-//
2
-// showdown.js -- A javascript port of Markdown.
3
-//
4
-// Copyright (c) 2007 John Fraser.
5
-//
6
-// Original Markdown Copyright (c) 2004-2005 John Gruber
7
-// <http://daringfireball.net/projects/markdown/>
8
-//
9
-// Redistributable under a BSD-style open source license.
10
-// See license.txt for more information.
11
-//
12
-// The full source distribution is at:
13
-//
14
-// A A L
15
-// T C A
16
-// T K B
17
-//
18
-// <http://www.attacklab.net/>
19
-//
20
-
21
-//
22
-// Wherever possible, Showdown is a straight, line-by-line port
23
-// of the Perl version of Markdown.
24
-//
25
-// This is not a normal parser design; it's basically just a
26
-// series of string substitutions. It's hard to read and
27
-// maintain this way, but keeping Showdown close to the original
28
-// design makes it easier to port new features.
29
-//
30
-// More importantly, Showdown behaves like markdown.pl in most
31
-// edge cases. So web applications can do client-side preview
32
-// in Javascript, and then build identical HTML on the server.
33
-//
34
-// This port needs the new RegExp functionality of ECMA 262,
35
-// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers
36
-// should do fine. Even with the new regular expression features,
37
-// We do a lot of work to emulate Perl's regex functionality.
38
-// The tricky changes in this file mostly have the "attacklab:"
39
-// label. Major or self-explanatory changes don't.
40
-//
41
-// Smart diff tools like Araxis Merge will be able to match up
42
-// this file with markdown.pl in a useful way. A little tweaking
43
-// helps: in a copy of markdown.pl, replace "#" with "//" and
44
-// replace "$text" with "text". Be sure to ignore whitespace
45
-// and line endings.
46
-//
47
-
48
-
49
-//
50
-// Showdown usage:
51
-//
52
-// var text = "Markdown *rocks*.";
53
-//
54
-// var converter = new Showdown.converter();
55
-// var html = converter.makeHtml(text);
56
-//
57
-// alert(html);
58
-//
59
-// Note: move the sample code to the bottom of this
60
-// file before uncommenting it.
61
-//
62
-
63
-
64
-//
65
-// Showdown namespace
66
-//
67
-var Showdown = {};
68
-
69
-//
70
-// converter
71
-//
72
-// Wraps all "globals" so that the only thing
73
-// exposed is makeHtml().
74
-//
75
-Showdown.converter = function() {
76
-
77
-//
78
-// Globals:
79
-//
80
-
81
-// Global hashes, used by various utility routines
82
-var g_urls;
83
-var g_titles;
84
-var g_html_blocks;
85
-
86
-// Used to track when we're inside an ordered or unordered list
87
-// (see _ProcessListItems() for details):
88
-var g_list_level = 0;
89
-
90
-
91
-this.makeHtml = function(text) {
92
-//
93
-// Main function. The order in which other subs are called here is
94
-// essential. Link and image substitutions need to happen before
95
-// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
96
-// and <img> tags get encoded.
97
-//
98
-
99
- // Clear the global hashes. If we don't clear these, you get conflicts
100
- // from other articles when generating a page which contains more than
101
- // one article (e.g. an index page that shows the N most recent
102
- // articles):
103
- g_urls = new Array();
104
- g_titles = new Array();
105
- g_html_blocks = new Array();
106
-
107
- // attacklab: Replace ~ with ~T
108
- // This lets us use tilde as an escape char to avoid md5 hashes
109
- // The choice of character is arbitray; anything that isn't
110
- // magic in Markdown will work.
111
- text = text.replace(/~/g,"~T");
112
-
113
- // attacklab: Replace $ with ~D
114
- // RegExp interprets $ as a special character
115
- // when it's in a replacement string
116
- text = text.replace(/\$/g,"~D");
117
-
118
- // Standardize line endings
119
- text = text.replace(/\r\n/g,"\n"); // DOS to Unix
120
- text = text.replace(/\r/g,"\n"); // Mac to Unix
121
-
122
- // Make sure text begins and ends with a couple of newlines:
123
- text = "\n\n" + text + "\n\n";
124
-
125
- // Convert all tabs to spaces.
126
- text = _Detab(text);
127
-
128
- // Strip any lines consisting only of spaces and tabs.
129
- // This makes subsequent regexen easier to write, because we can
130
- // match consecutive blank lines with /\n+/ instead of something
131
- // contorted like /[ \t]*\n+/ .
132
- text = text.replace(/^[ \t]+$/mg,"");
133
-
134
- // Turn block-level HTML blocks into hash entries
135
- text = _HashHTMLBlocks(text);
136
-
137
- // Strip link definitions, store in hashes.
138
- text = _StripLinkDefinitions(text);
139
-
140
- text = _RunBlockGamut(text);
141
-
142
- text = _UnescapeSpecialChars(text);
143
-
144
- // attacklab: Restore dollar signs
145
- text = text.replace(/~D/g,"$$");
146
-
147
- // attacklab: Restore tildes
148
- text = text.replace(/~T/g,"~");
149
-
150
- return text;
151
-}
152
-
153
-
154
-var _StripLinkDefinitions = function(text) {
155
-//
156
-// Strips link definitions from text, stores the URLs and titles in
157
-// hash references.
158
-//
159
-
160
- // Link defs are in the form: ^[id]: url "optional title"
161
-
162
- /*
163
- var text = text.replace(/
164
- ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
165
- [ \t]*
166
- \n? // maybe *one* newline
167
- [ \t]*
168
- <?(\S+?)>? // url = $2
169
- [ \t]*
170
- \n? // maybe one newline
171
- [ \t]*
172
- (?:
173
- (\n*) // any lines skipped = $3 attacklab: lookbehind removed
174
- ["(]
175
- (.+?) // title = $4
176
- [")]
177
- [ \t]*
178
- )? // title is optional
179
- (?:\n+|$)
180
- /gm,
181
- function(){...});
182
- */
183
- var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
184
- function (wholeMatch,m1,m2,m3,m4) {
185
- m1 = m1.toLowerCase();
186
- g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
187
- if (m3) {
188
- // Oops, found blank lines, so it's not a title.
189
- // Put back the parenthetical statement we stole.
190
- return m3+m4;
191
- } else if (m4) {
192
- g_titles[m1] = m4.replace(/"/g,""");
193
- }
194
-
195
- // Completely remove the definition from the text
196
- return "";
197
- }
198
- );
199
-
200
- return text;
201
-}
202
-
203
-
204
-var _HashHTMLBlocks = function(text) {
205
- // attacklab: Double up blank lines to reduce lookaround
206
- text = text.replace(/\n/g,"\n\n");
207
-
208
- // Hashify HTML blocks:
209
- // We only want to do this for block-level HTML tags, such as headers,
210
- // lists, and tables. That's because we still want to wrap <p>s around
211
- // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
212
- // phrase emphasis, and spans. The list of tags we're looking for is
213
- // hard-coded:
214
- var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
215
- var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
216
-
217
- // First, look for nested blocks, e.g.:
218
- // <div>
219
- // <div>
220
- // tags for inner block must be indented.
221
- // </div>
222
- // </div>
223
- //
224
- // The outermost tags must start at the left margin for this to match, and
225
- // the inner nested divs must be indented.
226
- // We need to do this before the next, more liberal match, because the next
227
- // match will start at the first `<div>` and stop at the first `</div>`.
228
-
229
- // attacklab: This regex can be expensive when it fails.
230
- /*
231
- var text = text.replace(/
232
- ( // save in $1
233
- ^ // start of line (with /m)
234
- <($block_tags_a) // start tag = $2
235
- \b // word break
236
- // attacklab: hack around khtml/pcre bug...
237
- [^\r]*?\n // any number of lines, minimally matching
238
- </\2> // the matching end tag
239
- [ \t]* // trailing spaces/tabs
240
- (?=\n+) // followed by a newline
241
- ) // attacklab: there are sentinel newlines at end of document
242
- /gm,function(){...}};
243
- */
244
- text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);
245
-
246
- //
247
- // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
248
- //
249
-
250
- /*
251
- var text = text.replace(/
252
- ( // save in $1
253
- ^ // start of line (with /m)
254
- <($block_tags_b) // start tag = $2
255
- \b // word break
256
- // attacklab: hack around khtml/pcre bug...
257
- [^\r]*? // any number of lines, minimally matching
258
- .*</\2> // the matching end tag
259
- [ \t]* // trailing spaces/tabs
260
- (?=\n+) // followed by a newline
261
- ) // attacklab: there are sentinel newlines at end of document
262
- /gm,function(){...}};
263
- */
264
- text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);
265
-
266
- // Special case just for <hr />. It was easier to make a special case than
267
- // to make the other regex more complicated.
268
-
269
- /*
270
- text = text.replace(/
271
- ( // save in $1
272
- \n\n // Starting after a blank line
273
- [ ]{0,3}
274
- (<(hr) // start tag = $2
275
- \b // word break
276
- ([^<>])*? //
277
- \/?>) // the matching end tag
278
- [ \t]*
279
- (?=\n{2,}) // followed by a blank line
280
- )
281
- /g,hashElement);
282
- */
283
- text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
284
-
285
- // Special case for standalone HTML comments:
286
-
287
- /*
288
- text = text.replace(/
289
- ( // save in $1
290
- \n\n // Starting after a blank line
291
- [ ]{0,3} // attacklab: g_tab_width - 1
292
- <!
293
- (--[^\r]*?--\s*)+
294
- >
295
- [ \t]*
296
- (?=\n{2,}) // followed by a blank line
297
- )
298
- /g,hashElement);
299
- */
300
- text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
301
-
302
- // PHP and ASP-style processor instructions (<?...?> and <%...%>)
303
-
304
- /*
305
- text = text.replace(/
306
- (?:
307
- \n\n // Starting after a blank line
308
- )
309
- ( // save in $1
310
- [ ]{0,3} // attacklab: g_tab_width - 1
311
- (?:
312
- <([?%]) // $2
313
- [^\r]*?
314
- \2>
315
- )
316
- [ \t]*
317
- (?=\n{2,}) // followed by a blank line
318
- )
319
- /g,hashElement);
320
- */
321
- text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
322
-
323
- // attacklab: Undo double lines (see comment at top of this function)
324
- text = text.replace(/\n\n/g,"\n");
325
- return text;
326
-}
327
-
328
-var hashElement = function(wholeMatch,m1) {
329
- var blockText = m1;
330
-
331
- // Undo double lines
332
- blockText = blockText.replace(/\n\n/g,"\n");
333
- blockText = blockText.replace(/^\n/,"");
334
-
335
- // strip trailing blank lines
336
- blockText = blockText.replace(/\n+$/g,"");
337
-
338
- // Replace the element text with a marker ("~KxK" where x is its key)
339
- blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
340
-
341
- return blockText;
342
-};
343
-
344
-var _RunBlockGamut = function(text) {
345
-//
346
-// These are all the transformations that form block-level
347
-// tags like paragraphs, headers, and list items.
348
-//
349
- text = _DoHeaders(text);
350
-
351
- // Do Horizontal Rules:
352
- var key = hashBlock("<hr />");
353
- text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
354
- text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
355
- text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
356
-
357
- text = _DoLists(text);
358
- text = _DoCodeBlocks(text);
359
- text = _DoBlockQuotes(text);
360
-
361
- // We already ran _HashHTMLBlocks() before, in Markdown(), but that
362
- // was to escape raw HTML in the original Markdown source. This time,
363
- // we're escaping the markup we've just created, so that we don't wrap
364
- // <p> tags around block-level tags.
365
- text = _HashHTMLBlocks(text);
366
- text = _FormParagraphs(text);
367
-
368
- return text;
369
-}
370
-
371
-
372
-var _RunSpanGamut = function(text) {
373
-//
374
-// These are all the transformations that occur *within* block-level
375
-// tags like paragraphs, headers, and list items.
376
-//
377
-
378
- text = _DoCodeSpans(text);
379
- text = _EscapeSpecialCharsWithinTagAttributes(text);
380
- text = _EncodeBackslashEscapes(text);
381
-
382
- // Process anchor and image tags. Images must come first,
383
- // because ![foo][f] looks like an anchor.
384
- text = _DoImages(text);
385
- text = _DoAnchors(text);
386
-
387
- // Make links out of things like `<http://example.com/>`
388
- // Must come after _DoAnchors(), because you can use < and >
389
- // delimiters in inline links like [this](<url>).
390
- text = _DoAutoLinks(text);
391
- text = _EncodeAmpsAndAngles(text);
392
- text = _DoItalicsAndBold(text);
393
-
394
- // Do hard breaks:
395
- text = text.replace(/ +\n/g," <br />\n");
396
-
397
- return text;
398
-}
399
-
400
-var _EscapeSpecialCharsWithinTagAttributes = function(text) {
401
-//
402
-// Within tags -- meaning between < and > -- encode [\ ` * _] so they
403
-// don't conflict with their use in Markdown for code, italics and strong.
404
-//
405
-
406
- // Build a regex to find HTML tags and comments. See Friedl's
407
- // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
408
- var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
409
-
410
- text = text.replace(regex, function(wholeMatch) {
411
- var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
412
- tag = escapeCharacters(tag,"\\`*_");
413
- return tag;
414
- });
415
-
416
- return text;
417
-}
418
-
419
-var _DoAnchors = function(text) {
420
-//
421
-// Turn Markdown link shortcuts into XHTML <a> tags.
422
-//
423
- //
424
- // First, handle reference-style links: [link text] [id]
425
- //
426
-
427
- /*
428
- text = text.replace(/
429
- ( // wrap whole match in $1
430
- \[
431
- (
432
- (?:
433
- \[[^\]]*\] // allow brackets nested one level
434
- |
435
- [^\[] // or anything else
436
- )*
437
- )
438
- \]
439
-
440
- [ ]? // one optional space
441
- (?:\n[ ]*)? // one optional newline followed by spaces
442
-
443
- \[
444
- (.*?) // id = $3
445
- \]
446
- )()()()() // pad remaining backreferences
447
- /g,_DoAnchors_callback);
448
- */
449
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
450
-
451
- //
452
- // Next, inline-style links: [link text](url "optional title")
453
- //
454
-
455
- /*
456
- text = text.replace(/
457
- ( // wrap whole match in $1
458
- \[
459
- (
460
- (?:
461
- \[[^\]]*\] // allow brackets nested one level
462
- |
463
- [^\[\]] // or anything else
464
- )
465
- )
466
- \]
467
- \( // literal paren
468
- [ \t]*
469
- () // no id, so leave $3 empty
470
- <?(.*?)>? // href = $4
471
- [ \t]*
472
- ( // $5
473
- (['"]) // quote char = $6
474
- (.*?) // Title = $7
475
- \6 // matching quote
476
- [ \t]* // ignore any spaces/tabs between closing quote and )
477
- )? // title is optional
478
- \)
479
- )
480
- /g,writeAnchorTag);
481
- */
482
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
483
-
484
- //
485
- // Last, handle reference-style shortcuts: [link text]
486
- // These must come last in case you've also got [link test][1]
487
- // or [link test](/foo)
488
- //
489
-
490
- /*
491
- text = text.replace(/
492
- ( // wrap whole match in $1
493
- \[
494
- ([^\[\]]+) // link text = $2; can't contain '[' or ']'
495
- \]
496
- )()()()()() // pad rest of backreferences
497
- /g, writeAnchorTag);
498
- */
499
- text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
500
-
501
- return text;
502
-}
503
-
504
-var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
505
- if (m7 == undefined) m7 = "";
506
- var whole_match = m1;
507
- var link_text = m2;
508
- var link_id = m3.toLowerCase();
509
- var url = m4;
510
- var title = m7;
511
-
512
- if (url == "") {
513
- if (link_id == "") {
514
- // lower-case and turn embedded newlines into spaces
515
- link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
516
- }
517
- url = "#"+link_id;
518
-
519
- if (g_urls[link_id] != undefined) {
520
- url = g_urls[link_id];
521
- if (g_titles[link_id] != undefined) {
522
- title = g_titles[link_id];
523
- }
524
- }
525
- else {
526
- if (whole_match.search(/\(\s*\)$/m)>-1) {
527
- // Special case for explicit empty url
528
- url = "";
529
- } else {
530
- return whole_match;
531
- }
532
- }
533
- }
534
-
535
- url = escapeCharacters(url,"*_");
536
- var result = "<a href=\"" + url + "\"";
537
-
538
- if (title != "") {
539
- title = title.replace(/"/g,""");
540
- title = escapeCharacters(title,"*_");
541
- result += " title=\"" + title + "\"";
542
- }
543
-
544
- result += ">" + link_text + "</a>";
545
-
546
- return result;
547
-}
548
-
549
-
550
-var _DoImages = function(text) {
551
-//
552
-// Turn Markdown image shortcuts into <img> tags.
553
-//
554
-
555
- //
556
- // First, handle reference-style labeled images: ![alt text][id]
557
- //
558
-
559
- /*
560
- text = text.replace(/
561
- ( // wrap whole match in $1
562
- !\[
563
- (.*?) // alt text = $2
564
- \]
565
-
566
- [ ]? // one optional space
567
- (?:\n[ ]*)? // one optional newline followed by spaces
568
-
569
- \[
570
- (.*?) // id = $3
571
- \]
572
- )()()()() // pad rest of backreferences
573
- /g,writeImageTag);
574
- */
575
- text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
576
-
577
- //
578
- // Next, handle inline images: 
579
- // Don't forget: encode * and _
580
-
581
- /*
582
- text = text.replace(/
583
- ( // wrap whole match in $1
584
- !\[
585
- (.*?) // alt text = $2
586
- \]
587
- \s? // One optional whitespace character
588
- \( // literal paren
589
- [ \t]*
590
- () // no id, so leave $3 empty
591
- <?(\S+?)>? // src url = $4
592
- [ \t]*
593
- ( // $5
594
- (['"]) // quote char = $6
595
- (.*?) // title = $7
596
- \6 // matching quote
597
- [ \t]*
598
- )? // title is optional
599
- \)
600
- )
601
- /g,writeImageTag);
602
- */
603
- text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
604
-
605
- return text;
606
-}
607
-
608
-var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
609
- var whole_match = m1;
610
- var alt_text = m2;
611
- var link_id = m3.toLowerCase();
612
- var url = m4;
613
- var title = m7;
614
-
615
- if (!title) title = "";
616
-
617
- if (url == "") {
618
- if (link_id == "") {
619
- // lower-case and turn embedded newlines into spaces
620
- link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
621
- }
622
- url = "#"+link_id;
623
-
624
- if (g_urls[link_id] != undefined) {
625
- url = g_urls[link_id];
626
- if (g_titles[link_id] != undefined) {
627
- title = g_titles[link_id];
628
- }
629
- }
630
- else {
631
- return whole_match;
632
- }
633
- }
634
-
635
- alt_text = alt_text.replace(/"/g,""");
636
- url = escapeCharacters(url,"*_");
637
- var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
638
-
639
- // attacklab: Markdown.pl adds empty title attributes to images.
640
- // Replicate this bug.
641
-
642
- //if (title != "") {
643
- title = title.replace(/"/g,""");
644
- title = escapeCharacters(title,"*_");
645
- result += " title=\"" + title + "\"";
646
- //}
647
-
648
- result += " />";
649
-
650
- return result;
651
-}
652
-
653
-
654
-var _DoHeaders = function(text) {
655
-
656
- // Setext-style headers:
657
- // Header 1
658
- // ========
659
- //
660
- // Header 2
661
- // --------
662
- //
663
- text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
664
- function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
665
-
666
- text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
667
- function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
668
-
669
- // atx-style headers:
670
- // # Header 1
671
- // ## Header 2
672
- // ## Header 2 with closing hashes ##
673
- // ...
674
- // ###### Header 6
675
- //
676
-
677
- /*
678
- text = text.replace(/
679
- ^(\#{1,6}) // $1 = string of #'s
680
- [ \t]*
681
- (.+?) // $2 = Header text
682
- [ \t]*
683
- \#* // optional closing #'s (not counted)
684
- \n+
685
- /gm, function() {...});
686
- */
687
-
688
- text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
689
- function(wholeMatch,m1,m2) {
690
- var h_level = m1.length;
691
- return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
692
- });
693
-
694
- function headerId(m) {
695
- return m.replace(/[^\w]/g, '').toLowerCase();
696
- }
697
- return text;
698
-}
699
-
700
-// This declaration keeps Dojo compressor from outputting garbage:
701
-var _ProcessListItems;
702
-
703
-var _DoLists = function(text) {
704
-//
705
-// Form HTML ordered (numbered) and unordered (bulleted) lists.
706
-//
707
-
708
- // attacklab: add sentinel to hack around khtml/safari bug:
709
- // http://bugs.webkit.org/show_bug.cgi?id=11231
710
- text += "~0";
711
-
712
- // Re-usable pattern to match any entirel ul or ol list:
713
-
714
- /*
715
- var whole_list = /
716
- ( // $1 = whole list
717
- ( // $2
718
- [ ]{0,3} // attacklab: g_tab_width - 1
719
- ([*+-]|\d+[.]) // $3 = first list item marker
720
- [ \t]+
721
- )
722
- [^\r]+?
723
- ( // $4
724
- ~0 // sentinel for workaround; should be $
725
- |
726
- \n{2,}
727
- (?=\S)
728
- (?! // Negative lookahead for another list item marker
729
- [ \t]*
730
- (?:[*+-]|\d+[.])[ \t]+
731
- )
732
- )
733
- )/g
734
- */
735
- var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
736
-
737
- if (g_list_level) {
738
- text = text.replace(whole_list,function(wholeMatch,m1,m2) {
739
- var list = m1;
740
- var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
741
-
742
- // Turn double returns into triple returns, so that we can make a
743
- // paragraph for the last item in a list, if necessary:
744
- list = list.replace(/\n{2,}/g,"\n\n\n");;
745
- var result = _ProcessListItems(list);
746
-
747
- // Trim any trailing whitespace, to put the closing `</$list_type>`
748
- // up on the preceding line, to get it past the current stupid
749
- // HTML block parser. This is a hack to work around the terrible
750
- // hack that is the HTML block parser.
751
- result = result.replace(/\s+$/,"");
752
- result = "<"+list_type+">" + result + "</"+list_type+">\n";
753
- return result;
754
- });
755
- } else {
756
- whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
757
- text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
758
- var runup = m1;
759
- var list = m2;
760
-
761
- var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
762
- // Turn double returns into triple returns, so that we can make a
763
- // paragraph for the last item in a list, if necessary:
764
- var list = list.replace(/\n{2,}/g,"\n\n\n");;
765
- var result = _ProcessListItems(list);
766
- result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
767
- return result;
768
- });
769
- }
770
-
771
- // attacklab: strip sentinel
772
- text = text.replace(/~0/,"");
773
-
774
- return text;
775
-}
776
-
777
-_ProcessListItems = function(list_str) {
778
-//
779
-// Process the contents of a single ordered or unordered list, splitting it
780
-// into individual list items.
781
-//
782
- // The $g_list_level global keeps track of when we're inside a list.
783
- // Each time we enter a list, we increment it; when we leave a list,
784
- // we decrement. If it's zero, we're not in a list anymore.
785
- //
786
- // We do this because when we're not inside a list, we want to treat
787
- // something like this:
788
- //
789
- // I recommend upgrading to version
790
- // 8. Oops, now this line is treated
791
- // as a sub-list.
792
- //
793
- // As a single paragraph, despite the fact that the second line starts
794
- // with a digit-period-space sequence.
795
- //
796
- // Whereas when we're inside a list (or sub-list), that line will be
797
- // treated as the start of a sub-list. What a kludge, huh? This is
798
- // an aspect of Markdown's syntax that's hard to parse perfectly
799
- // without resorting to mind-reading. Perhaps the solution is to
800
- // change the syntax rules such that sub-lists must start with a
801
- // starting cardinal number; e.g. "1." or "a.".
802
-
803
- g_list_level++;
804
-
805
- // trim trailing blank lines:
806
- list_str = list_str.replace(/\n{2,}$/,"\n");
807
-
808
- // attacklab: add sentinel to emulate \z
809
- list_str += "~0";
810
-
811
- /*
812
- list_str = list_str.replace(/
813
- (\n)? // leading line = $1
814
- (^[ \t]*) // leading whitespace = $2
815
- ([*+-]|\d+[.]) [ \t]+ // list marker = $3
816
- ([^\r]+? // list item text = $4
817
- (\n{1,2}))
818
- (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
819
- /gm, function(){...});
820
- */
821
- list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
822
- function(wholeMatch,m1,m2,m3,m4){
823
- var item = m4;
824
- var leading_line = m1;
825
- var leading_space = m2;
826
-
827
- if (leading_line || (item.search(/\n{2,}/)>-1)) {
828
- item = _RunBlockGamut(_Outdent(item));
829
- }
830
- else {
831
- // Recursion for sub-lists:
832
- item = _DoLists(_Outdent(item));
833
- item = item.replace(/\n$/,""); // chomp(item)
834
- item = _RunSpanGamut(item);
835
- }
836
-
837
- return "<li>" + item + "</li>\n";
838
- }
839
- );
840
-
841
- // attacklab: strip sentinel
842
- list_str = list_str.replace(/~0/g,"");
843
-
844
- g_list_level--;
845
- return list_str;
846
-}
847
-
848
-
849
-var _DoCodeBlocks = function(text) {
850
-//
851
-// Process Markdown `<pre><code>` blocks.
852
-//
853
-
854
- /*
855
- text = text.replace(text,
856
- /(?:\n\n|^)
857
- ( // $1 = the code block -- one or more lines, starting with a space/tab
858
- (?:
859
- (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
860
- .*\n+
861
- )+
862
- )
863
- (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
864
- /g,function(){...});
865
- */
866
-
867
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
868
- text += "~0";
869
-
870
- text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
871
- function(wholeMatch,m1,m2) {
872
- var codeblock = m1;
873
- var nextChar = m2;
874
-
875
- codeblock = _EncodeCode( _Outdent(codeblock));
876
- codeblock = _Detab(codeblock);
877
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
878
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
879
-
880
- codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
881
-
882
- return hashBlock(codeblock) + nextChar;
883
- }
884
- );
885
-
886
- // attacklab: strip sentinel
887
- text = text.replace(/~0/,"");
888
-
889
- return text;
890
-}
891
-
892
-var hashBlock = function(text) {
893
- text = text.replace(/(^\n+|\n+$)/g,"");
894
- return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
895
-}
896
-
897
-
898
-var _DoCodeSpans = function(text) {
899
-//
900
-// * Backtick quotes are used for <code></code> spans.
901
-//
902
-// * You can use multiple backticks as the delimiters if you want to
903
-// include literal backticks in the code span. So, this input:
904
-//
905
-// Just type ``foo `bar` baz`` at the prompt.
906
-//
907
-// Will translate to:
908
-//
909
-// <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
910
-//
911
-// There's no arbitrary limit to the number of backticks you
912
-// can use as delimters. If you need three consecutive backticks
913
-// in your code, use four for delimiters, etc.
914
-//
915
-// * You can use spaces to get literal backticks at the edges:
916
-//
917
-// ... type `` `bar` `` ...
918
-//
919
-// Turns to:
920
-//
921
-// ... type <code>`bar`</code> ...
922
-//
923
-
924
- /*
925
- text = text.replace(/
926
- (^|[^\\]) // Character before opening ` can't be a backslash
927
- (`+) // $2 = Opening run of `
928
- ( // $3 = The code block
929
- [^\r]*?
930
- [^`] // attacklab: work around lack of lookbehind
931
- )
932
- \2 // Matching closer
933
- (?!`)
934
- /gm, function(){...});
935
- */
936
-
937
- text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
938
- function(wholeMatch,m1,m2,m3,m4) {
939
- var c = m3;
940
- c = c.replace(/^([ \t]*)/g,""); // leading whitespace
941
- c = c.replace(/[ \t]*$/g,""); // trailing whitespace
942
- c = _EncodeCode(c);
943
- return m1+"<code>"+c+"</code>";
944
- });
945
-
946
- return text;
947
-}
948
-
949
-
950
-var _EncodeCode = function(text) {
951
-//
952
-// Encode/escape certain characters inside Markdown code runs.
953
-// The point is that in code, these characters are literals,
954
-// and lose their special Markdown meanings.
955
-//
956
- // Encode all ampersands; HTML entities are not
957
- // entities within a Markdown code span.
958
- text = text.replace(/&/g,"&");
959
-
960
- // Do the angle bracket song and dance:
961
- text = text.replace(/</g,"<");
962
- text = text.replace(/>/g,">");
963
-
964
- // Now, escape characters that are magic in Markdown:
965
- text = escapeCharacters(text,"\*_{}[]\\",false);
966
-
967
-// jj the line above breaks this:
968
-//---
969
-
970
-//* Item
971
-
972
-// 1. Subitem
973
-
974
-// special char: *
975
-//---
976
-
977
- return text;
978
-}
979
-
980
-
981
-var _DoItalicsAndBold = function(text) {
982
-
983
- // <strong> must go first:
984
- text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
985
- "<strong>$2</strong>");
986
-
987
- text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
988
- "<em>$2</em>");
989
-
990
- return text;
991
-}
992
-
993
-
994
-var _DoBlockQuotes = function(text) {
995
-
996
- /*
997
- text = text.replace(/
998
- ( // Wrap whole match in $1
999
- (
1000
- ^[ \t]*>[ \t]? // '>' at the start of a line
1001
- .+\n // rest of the first line
1002
- (.+\n)* // subsequent consecutive lines
1003
- \n* // blanks
1004
- )+
1005
- )
1006
- /gm, function(){...});
1007
- */
1008
-
1009
- text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1010
- function(wholeMatch,m1) {
1011
- var bq = m1;
1012
-
1013
- // attacklab: hack around Konqueror 3.5.4 bug:
1014
- // "----------bug".replace(/^-/g,"") == "bug"
1015
-
1016
- bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
1017
-
1018
- // attacklab: clean up hack
1019
- bq = bq.replace(/~0/g,"");
1020
-
1021
- bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
1022
- bq = _RunBlockGamut(bq); // recurse
1023
-
1024
- bq = bq.replace(/(^|\n)/g,"$1 ");
1025
- // These leading spaces screw with <pre> content, so we need to fix that:
1026
- bq = bq.replace(
1027
- /(\s*<pre>[^\r]+?<\/pre>)/gm,
1028
- function(wholeMatch,m1) {
1029
- var pre = m1;
1030
- // attacklab: hack around Konqueror 3.5.4 bug:
1031
- pre = pre.replace(/^ /mg,"~0");
1032
- pre = pre.replace(/~0/g,"");
1033
- return pre;
1034
- });
1035
-
1036
- return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
1037
- });
1038
- return text;
1039
-}
1040
-
1041
-
1042
-var _FormParagraphs = function(text) {
1043
-//
1044
-// Params:
1045
-// $text - string to process with html <p> tags
1046
-//
1047
-
1048
- // Strip leading and trailing lines:
1049
- text = text.replace(/^\n+/g,"");
1050
- text = text.replace(/\n+$/g,"");
1051
-
1052
- var grafs = text.split(/\n{2,}/g);
1053
- var grafsOut = new Array();
1054
-
1055
- //
1056
- // Wrap <p> tags.
1057
- //
1058
- var end = grafs.length;
1059
- for (var i=0; i<end; i++) {
1060
- var str = grafs[i];
1061
-
1062
- // if this is an HTML marker, copy it
1063
- if (str.search(/~K(\d+)K/g) >= 0) {
1064
- grafsOut.push(str);
1065
- }
1066
- else if (str.search(/\S/) >= 0) {
1067
- str = _RunSpanGamut(str);
1068
- str = str.replace(/^([ \t]*)/g,"<p>");
1069
- str += "</p>"
1070
- grafsOut.push(str);
1071
- }
1072
-
1073
- }
1074
-
1075
- //
1076
- // Unhashify HTML blocks
1077
- //
1078
- end = grafsOut.length;
1079
- for (var i=0; i<end; i++) {
1080
- // if this is a marker for an html block...
1081
- while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
1082
- var blockText = g_html_blocks[RegExp.$1];
1083
- blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
1084
- grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
1085
- }
1086
- }
1087
-
1088
- return grafsOut.join("\n\n");
1089
-}
1090
-
1091
-
1092
-var _EncodeAmpsAndAngles = function(text) {
1093
-// Smart processing for ampersands and angle brackets that need to be encoded.
1094
-
1095
- // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1096
- // http://bumppo.net/projects/amputator/
1097
- text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
1098
-
1099
- // Encode naked <'s
1100
- text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
1101
-
1102
- return text;
1103
-}
1104
-
1105
-
1106
-var _EncodeBackslashEscapes = function(text) {
1107
-//
1108
-// Parameter: String.
1109
-// Returns: The string, with after processing the following backslash
1110
-// escape sequences.
1111
-//
1112
-
1113
- // attacklab: The polite way to do this is with the new
1114
- // escapeCharacters() function:
1115
- //
1116
- // text = escapeCharacters(text,"\\",true);
1117
- // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1118
- //
1119
- // ...but we're sidestepping its use of the (slow) RegExp constructor
1120
- // as an optimization for Firefox. This function gets called a LOT.
1121
-
1122
- text = text.replace(/\\(\\)/g,escapeCharacters_callback);
1123
- text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
1124
- return text;
1125
-}
1126
-
1127
-
1128
-var _DoAutoLinks = function(text) {
1129
-
1130
- text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
1131
-
1132
- // Email addresses: <address@domain.foo>
1133
-
1134
- /*
1135
- text = text.replace(/
1136
- <
1137
- (?:mailto:)?
1138
- (
1139
- [-.\w]+
1140
- \@
1141
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1142
- )
1143
- >
1144
- /gi, _DoAutoLinks_callback());
1145
- */
1146
- text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
1147
- function(wholeMatch,m1) {
1148
- return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
1149
- }
1150
- );
1151
-
1152
- return text;
1153
-}
1154
-
1155
-
1156
-var _EncodeEmailAddress = function(addr) {
1157
-//
1158
-// Input: an email address, e.g. "foo@example.com"
1159
-//
1160
-// Output: the email address as a mailto link, with each character
1161
-// of the address encoded as either a decimal or hex entity, in
1162
-// the hopes of foiling most address harvesting spam bots. E.g.:
1163
-//
1164
-// <a href="mailto:foo@e
1165
-// xample.com">foo
1166
-// @example.com</a>
1167
-//
1168
-// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1169
-// mailing list: <http://tinyurl.com/yu7ue>
1170
-//
1171
-
1172
- // attacklab: why can't javascript speak hex?
1173
- function char2hex(ch) {
1174
- var hexDigits = '0123456789ABCDEF';
1175
- var dec = ch.charCodeAt(0);
1176
- return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
1177
- }
1178
-
1179
- var encode = [
1180
- function(ch){return "&#"+ch.charCodeAt(0)+";";},
1181
- function(ch){return "&#x"+char2hex(ch)+";";},
1182
- function(ch){return ch;}
1183
- ];
1184
-
1185
- addr = "mailto:" + addr;
1186
-
1187
- addr = addr.replace(/./g, function(ch) {
1188
- if (ch == "@") {
1189
- // this *must* be encoded. I insist.
1190
- ch = encode[Math.floor(Math.random()*2)](ch);
1191
- } else if (ch !=":") {
1192
- // leave ':' alone (to spot mailto: later)
1193
- var r = Math.random();
1194
- // roughly 10% raw, 45% hex, 45% dec
1195
- ch = (
1196
- r > .9 ? encode[2](ch) :
1197
- r > .45 ? encode[1](ch) :
1198
- encode[0](ch)
1199
- );
1200
- }
1201
- return ch;
1202
- });
1203
-
1204
- addr = "<a href=\"" + addr + "\">" + addr + "</a>";
1205
- addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
1206
-
1207
- return addr;
1208
-}
1209
-
1210
-
1211
-var _UnescapeSpecialChars = function(text) {
1212
-//
1213
-// Swap back in all the special characters we've hidden.
1214
-//
1215
- text = text.replace(/~E(\d+)E/g,
1216
- function(wholeMatch,m1) {
1217
- var charCodeToReplace = parseInt(m1);
1218
- return String.fromCharCode(charCodeToReplace);
1219
- }
1220
- );
1221
- return text;
1222
-}
1223
-
1224
-
1225
-var _Outdent = function(text) {
1226
-//
1227
-// Remove one level of line-leading tabs or spaces
1228
-//
1229
-
1230
- // attacklab: hack around Konqueror 3.5.4 bug:
1231
- // "----------bug".replace(/^-/g,"") == "bug"
1232
-
1233
- text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
1234
-
1235
- // attacklab: clean up hack
1236
- text = text.replace(/~0/g,"")
1237
-
1238
- return text;
1239
-}
1240
-
1241
-var _Detab = function(text) {
1242
-// attacklab: Detab's completely rewritten for speed.
1243
-// In perl we could fix it by anchoring the regexp with \G.
1244
-// In javascript we're less fortunate.
1245
-
1246
- // expand first n-1 tabs
1247
- text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
1248
-
1249
- // replace the nth with two sentinels
1250
- text = text.replace(/\t/g,"~A~B");
1251
-
1252
- // use the sentinel to anchor our regex so it doesn't explode
1253
- text = text.replace(/~B(.+?)~A/g,
1254
- function(wholeMatch,m1,m2) {
1255
- var leadingText = m1;
1256
- var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
1257
-
1258
- // there *must* be a better way to do this:
1259
- for (var i=0; i<numSpaces; i++) leadingText+=" ";
1260
-
1261
- return leadingText;
1262
- }
1263
- );
1264
-
1265
- // clean up sentinels
1266
- text = text.replace(/~A/g," "); // attacklab: g_tab_width
1267
- text = text.replace(/~B/g,"");
1268
-
1269
- return text;
1270
-}
1271
-
1272
-
1273
-//
1274
-// attacklab: Utility functions
1275
-//
1276
-
1277
-
1278
-var escapeCharacters = function(text, charsToEscape, afterBackslash) {
1279
- // First we have to escape the escape characters so that
1280
- // we can build a character class out of them
1281
- var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
1282
-
1283
- if (afterBackslash) {
1284
- regexString = "\\\\" + regexString;
1285
- }
1286
-
1287
- var regex = new RegExp(regexString,"g");
1288
- text = text.replace(regex,escapeCharacters_callback);
1289
-
1290
- return text;
1291
-}
1292
-
1293
-
1294
-var escapeCharacters_callback = function(wholeMatch,m1) {
1295
- var charCodeToEscape = m1.charCodeAt(0);
1296
- return "~E"+charCodeToEscape+"E";
1297
-}
1298
-
1299
-} // end of Showdown.converter
1300
-
1301
-// export
1302
-if (typeof exports != 'undefined') exports.Showdown = Showdown;
\ No newline at end of file