@samitouri / QOS-React-2 / commits / 36aefcb8cc

[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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}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,"&quot;");
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,"&quot;");
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: ![alt text](url "optional title")
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,"&quot;");
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,"&quot;");
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,"&amp;");
959 -
960 - // Do the angle bracket song and dance:
961 - text = text.replace(/</g,"&lt;");
962 - text = text.replace(/>/g,"&gt;");
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,"&amp;");
1098 -
1099 - // Encode naked <'s
1100 - text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
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="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1165 -// x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1166 -// &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</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