├── vendor ├── composer │ ├── installed.json │ ├── installed_dev.json │ ├── autoload_classmap.php │ ├── autoload_namespaces.php │ ├── autoload_real.php │ └── ClassLoader.php ├── autoload.php └── markdown │ ├── MarkdownInterface.inc.php │ ├── Michelf │ ├── MarkdownInterface.inc.php │ ├── Markdown.inc.php │ ├── MarkdownExtra.inc.php │ ├── MarkdownInterface.php │ └── MarkdownExtra.php │ ├── Markdown.inc.php │ ├── MarkdownExtra.inc.php │ ├── Readme.php │ ├── MarkdownInterface.php │ └── MarkdownExtra.php ├── app.php ├── single.php ├── layout.php ├── README.md ├── config.php ├── comment.php ├── index.php ├── views ├── layout.php ├── admin │ ├── edit.view.php │ ├── create.view.php │ └── dashboard.view.php ├── index.view.php └── single.view.php ├── static ├── markdown │ ├── css │ │ └── jquery.pagedown-bootstrap.css │ ├── README.md │ ├── less │ │ └── jquery.pagedown-bootstrap.less │ ├── LICENSE.txt │ ├── js │ │ ├── jquery.pagedown-bootstrap.js │ │ ├── Markdown.Sanitizer.js │ │ ├── jquery.pagedown-bootstrap.combined.min.js │ │ └── Markdown.Converter.js │ └── demo │ │ └── index.html ├── css │ ├── styles.css │ └── bootstrap-responsive.min.css └── js │ └── bootstrap.min.js ├── sftp-config.json ├── admin ├── auth.php └── index.php └── db.php /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /vendor/composer/installed_dev.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | getById($_GET['id'],'posts'); 7 | if ($post == FALSE) { 8 | header('Location: index.php'); 9 | 10 | } else { 11 | 12 | $layout->view('single', array( 13 | 'article' => $post 14 | )); 15 | } 16 | -------------------------------------------------------------------------------- /layout.php: -------------------------------------------------------------------------------- 1 | 'duythien', 8 | 'password' => 'qazwsx2013@', 9 | 'dbname' => 'blog', 10 | //'cn' => sprintf('mongodb://%s:%d/%s', $hosts, $port,$database), 11 | 'connection_string'=> sprintf('mongodb://%s:%d/%s','127.0.0.1','27017','blog') 12 | ); 13 | -------------------------------------------------------------------------------- /vendor/markdown/Markdown.inc.php: -------------------------------------------------------------------------------- 1 | $_POST['fName'], 10 | 'email' => $_POST['fEmail'], 11 | 'comment' => $_POST['fComment'], 12 | 'posted_at' => new MongoDate() 13 | ); 14 | $status = $db->commentId($id,'posts',$comment); 15 | 16 | if ($status == TRUE) { 17 | header('Location: single.php?id='.$id); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | get($currentPage,'posts'); 9 | 10 | 11 | $layout->view('index',array( 12 | 'currentPage' => $data[0], 13 | 'totalPages' => $data[1], 14 | 'cursor' => $data[2], 15 | 'name' => 'Duy Thien' 16 | 17 | )); 18 | 19 | 20 | } catch (Exception $e) { 21 | echo 'Caught exception: ', $e->getMessage(), "\n"; 22 | } 23 | -------------------------------------------------------------------------------- /views/layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
4 |
5 |
8 |
| Title | 9 |Saved at | 10 |Confirmed? | 11 |||
|---|---|---|---|---|
| 18 | | sec);?> | 19 |20 | View 21 | | 22 |23 | Edit 24 | 25 | | 26 |27 | Delete 28 | | 29 |
|for twitter bootstrap 31 | var pre_white = /^(
|<\/pre>)$/i;
32 |
33 | function sanitizeTag(tag) {
34 | if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white) || tag.match(pre_white))
35 | return tag;
36 | else
37 | return "";
38 | }
39 |
40 | ///
41 | /// attempt to balance HTML tags in the html string
42 | /// by removing any unmatched opening or closing tags
43 | /// IMPORTANT: we *assume* HTML has *already* been
44 | /// sanitized and is safe/sane before balancing!
45 | ///
46 | /// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593
47 | ///
48 | function balanceTags(html) {
49 |
50 | if (html == "")
51 | return "";
52 |
53 | var re = /<\/?\w+[^>]*(\s|$|>)/g;
54 | // convert everything to lower case; this makes
55 | // our case insensitive comparisons easier
56 | var tags = html.toLowerCase().match(re);
57 |
58 | // no HTML tags present? nothing to do; exit now
59 | var tagcount = (tags || []).length;
60 | if (tagcount == 0)
61 | return html;
62 |
63 | var tagname, tag;
64 | var ignoredtags = "![]()
38 | An extension of Pagedown to neatly integrate into Twitter Bootstrap for a WYSIWYG Markdown editor. This project is a fork of the work of Sam Willis. 39 |
40 |41 | This project is hosted on GitHub where issues can be reported. It is released under the MIT License. 42 |
43 |44 | 45 | Fork on GitHub 46 | 47 |
48 || Name | 70 |Type | 71 |Default | 72 |Options/Description | 73 |
|---|---|---|---|
| sanatize | 78 |boolean | 79 |true | 80 |true or false, enables or disables sanatization of HTML | 81 |
| help | 84 |function | 85 |null | 86 |A function to be called when the help button it clicked. Null precludes the rendering of the help button. | 87 |
| hooks | 90 |array | 91 |[] | 92 |93 | An array of objects that define hooks for the converter. Each object should define a string, 'event', and a function, 'callback'. A full list of events can be found here. 94 | | 95 |
103 | All of the created elements are wrapped in nice helpful HTML tags as defined: 104 |
105 |'+T+"\n";return"\n\n"+T+"\n\n"+U});P=P.replace(/~0/,"");return P}function N(P){P=P.replace(/(^\n+|\n+$)/g,"");return"\n\n~K"+(e.push(P)-1)+"K\n\n"}function r(P){P=P.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(S,U,T,R,Q){var V=R;V=V.replace(/^([ \t]*)/g,"");V=V.replace(/[ \t]*$/g,"");V=B(V);V=V.replace(/:\/\//g,"~P");return U+""+V+""});return P}function B(P){P=P.replace(/&/g,"&");P=P.replace(//g,">");P=y(P,"*_{}[]\\",false);return P}function x(P){P=P.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,"$1$3$4");P=P.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,"$1$3$4");return P}function g(P){P=P.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(Q,R){var S=R;S=S.replace(/^[ \t]*>[ \t]?/gm,"~0");S=S.replace(/~0/g,"");S=S.replace(/^[ \t]+$/gm,"");S=f(S);S=S.replace(/(^|\n)/g,"$1 ");S=S.replace(/(\s*[^\r]+?<\/pre>)/gm,function(T,U){var V=U;V=V.replace(/^ /mg,"~0");V=V.replace(/~0/g,"");return V});return N("\n"+S+"\n
")});return P}function I(W,P){W=W.replace(/^\n+/g,"");W=W.replace(/\n+$/g,"");var X=W.split(/\n{2,}/g);var U=[];var Q=/~K(\d+)K/;var R=X.length;for(var S=0;S");T+="";U.push(T)}}}if(!P){R=U.length;for(var S=0;S#+-.!])/g,s);return P}function K(R){R=R.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi,"$1<$2$3>$4");var Q=function(T,S){return''+j.plainLinkText(S)+""};R=R.replace(/<((https?|ftp):[^'">\s]+)>/gi,Q);var P=function(W,U){var S="mailto:";var V;var T;if(U.substring(0,S.length)!=S){V=S+U;T=U}else{V=U;T=U.substring(S.length,U.length)}return''+j.plainLinkText(T)+""};R=R.replace(/<((?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+))>/gi,P);return R}function M(P){P=P.replace(/~E(\d+)E/g,function(Q,S){var R=parseInt(S);return String.fromCharCode(R)});return P}function t(P){P=P.replace(/^(\t|[ ]{1,4})/gm,"~0");P=P.replace(/~0/g,"");return P}function J(S){if(!/\t/.test(S)){return S}var R=[" "," "," "," "],Q=0,P;return S.replace(/[\n\t]/g,function(T,U){if(T==="\n"){Q=U+1;return T}P=(U-Q)%4;Q=U+1;return R[P]})}var u=/(?:["'*()[\]:]|~D)/g;function A(Q){if(!Q){return""}var P=Q.length;return Q.replace(u,function(R,S){if(R=="~D"){return"%24"}if(R==":"){if(S==P-1||/[0-9\/]/.test(Q.charAt(S+1))){return":"}if(Q.substring(0,"mailto:".length)==="mailto:"){return":"}if(Q.substring(0,"magnet:".length)==="magnet:"){return":"}}return"%"+R.charCodeAt(0).toString(16)})}function y(T,Q,R){var P="(["+Q.replace(/([\[\]\\])/g,"\\$1")+"])";if(R){P="\\\\"+P}var S=new RegExp(P,"g");T=T.replace(S,s);return T}function s(P,R){var Q=R.charCodeAt(0);return"~E"+Q+"E"}}})();(function(){var a={},s={},l={},u=window.document,m=window.RegExp,g=window.navigator,o={lineLength:72},t={isIE:/msie/.test(g.userAgent.toLowerCase()),isIE_5or6:/msie 6/.test(g.userAgent.toLowerCase())||/msie 5/.test(g.userAgent.toLowerCase()),isOpera:/opera/.test(g.userAgent.toLowerCase())};var v='http://example.com/ "optional title"
';var p='http://example.com/images/diagram.jpg "optional title"
';var c="http://";var d="http://";var j="Markdown Editing Help";Markdown.Editor=function(z,B,y){B=B||"";var w=this.hooks=new Markdown.HookCollection();w.addNoop("onPreviewRefresh");w.addNoop("postBlockquoteCreation");w.addFalse("insertImageDialog");this.getConverter=function(){return z};var A=this,x;this.run=function(){if(x){return}x=new i(B);var E=new k(w);var C=new h(z,x,function(){w.onPreviewRefresh()});var F,D;if(!/\?noundo/.test(u.location.href)){F=new e(function(){C.refresh();if(D){D.setUndoRedoButtonStates()}},x);this.textOperation=function(H){F.setCommandMode();H();A.refreshPreview()}}D=new q(B,x,F,C,E,y);D.setUndoRedoButtonStates();var G=A.refreshPreview=function(){C.refresh(true)};G()}};function r(){}r.prototype.findTags=function(x,z){var w=this;var y;if(x){y=a.extendRegExp(x,"","$");this.before=this.before.replace(y,function(A){w.startTag=w.startTag+A;return""});y=a.extendRegExp(x,"^","");this.selection=this.selection.replace(y,function(A){w.startTag=w.startTag+A;return""})}if(z){y=a.extendRegExp(z,"","$");this.selection=this.selection.replace(y,function(A){w.endTag=A+w.endTag;return""});y=a.extendRegExp(z,"^","");this.after=this.after.replace(y,function(A){w.endTag=A+w.endTag;return""})}};r.prototype.trimWhitespace=function(x){var w,z,y=this;if(x){w=z=""}else{w=function(A){y.before+=A;return""};z=function(A){y.after=A+y.after;return""}}this.selection=this.selection.replace(/^(\s*)/,w).replace(/(\s*)$/,z)};r.prototype.skipLines=function(y,x,w){if(y===undefined){y=1}if(x===undefined){x=1}y++;x++;var z;var A;if(navigator.userAgent.match(/Chrome/)){"X".match(/()./)}this.selection=this.selection.replace(/(^\n*)/,"");this.startTag=this.startTag+m.$1;this.selection=this.selection.replace(/(\n*$)/,"");this.endTag=this.endTag+m.$1;this.startTag=this.startTag.replace(/(^\n*)/,"");this.before=this.before+m.$1;this.endTag=this.endTag.replace(/(\n*$)/,"");this.after=this.after+m.$1;if(this.before){z=A="";while(y--){z+="\\n?";A+="\n"}if(w){z="\\n*"}this.before=this.before.replace(new m(z+"$",""),A)}if(this.after){z=A="";while(x--){z+="\\n?";A+="\n"}if(w){z="\\n*"}this.after=this.after.replace(new m(z,""),A)}};function i(w){this.buttonBar=u.getElementById("wmd-button-bar"+w);this.preview=u.getElementById("wmd-preview"+w);this.input=u.getElementById("wmd-input"+w)}a.isVisible=function(w){if(window.getComputedStyle){return window.getComputedStyle(w,null).getPropertyValue("display")!=="none"}else{if(w.currentStyle){return w.currentStyle.display!=="none"}}};a.addEvent=function(x,w,y){if(x.attachEvent){x.attachEvent("on"+w,y)}else{x.addEventListener(w,y,false)}};a.removeEvent=function(x,w,y){if(x.detachEvent){x.detachEvent("on"+w,y)}else{x.removeEventListener(w,y,false)}};a.fixEolChars=function(w){w=w.replace(/\r\n/g,"\n");w=w.replace(/\r/g,"\n");return w};a.extendRegExp=function(y,A,x){if(A===null||A===undefined){A=""}if(x===null||x===undefined){x=""}var z=y.toString();var w;z=z.replace(/\/([gim]*)$/,function(B,C){w=C;return""});z=z.replace(/(^\/|\/$)/g,"");z=A+z+x;return new m(z,w)};s.getTop=function(y,x){var w=y.offsetTop;if(!x){while(y=y.offsetParent){w+=y.offsetTop}}return w};s.getHeight=function(w){return w.offsetHeight||w.scrollHeight};s.getWidth=function(w){return w.offsetWidth||w.scrollWidth};s.getPageSize=function(){var x,y;var w,B;if(self.innerHeight&&self.scrollMaxY){x=u.body.scrollWidth;y=self.innerHeight+self.scrollMaxY}else{if(u.body.scrollHeight>u.body.offsetHeight){x=u.body.scrollWidth;y=u.body.scrollHeight}else{x=u.body.offsetWidth;y=u.body.offsetHeight}}if(self.innerHeight){w=self.innerWidth;B=self.innerHeight}else{if(u.documentElement&&u.documentElement.clientHeight){w=u.documentElement.clientWidth;B=u.documentElement.clientHeight}else{if(u.body){w=u.body.clientWidth;B=u.body.clientHeight}}}var A=Math.max(x,w);var z=Math.max(y,B);return[A,z,w,B]};function e(I,F){var L=this;var G=[];var D=0;var C="none";var x;var y;var B;var w=function(N,M){if(C!=N){C=N;if(!M){z()}}if(!t.isIE||C!="moving"){y=setTimeout(E,1)}else{B=null}};var E=function(M){B=new b(F,M);y=undefined};this.setCommandMode=function(){C="command";z();y=setTimeout(E,0)};this.canUndo=function(){return D>1};this.canRedo=function(){if(G[D+1]){return true}return false};this.undo=function(){if(L.canUndo()){if(x){x.restore();x=null}else{G[D]=new b(F);G[--D].restore();if(I){I()}}}C="none";F.input.focus();E()};this.redo=function(){if(L.canRedo()){G[++D].restore();if(I){I()}}C="none";F.input.focus();E()};var z=function(){var M=B||new b(F);if(!M){return false}if(C=="moving"){if(!x){x=M}return}if(x){if(G[D-1].text!=x.text){G[D++]=x}x=null}G[D++]=M;G[D+1]=null;if(I){I()}};var H=function(M){var O=false;if(M.ctrlKey||M.metaKey){var N=M.charCode||M.keyCode;var P=String.fromCharCode(N);switch(P){case"y":L.redo();O=true;break;case"z":if(!M.shiftKey){L.undo()}else{L.redo()}O=true;break}}if(O){if(M.preventDefault){M.preventDefault()}if(window.event){window.event.returnValue=false}return}};var K=function(M){if(!M.ctrlKey&&!M.metaKey){var N=M.keyCode;if((N>=33&&N<=40)||(N>=63232&&N<=63235)){w("moving")}else{if(N==8||N==46||N==127){w("deleting")}else{if(N==13){w("newlines")}else{if(N==27){w("escape")}else{if((N<16||N>20)&&N!=91){w("typing")}}}}}}};var A=function(){a.addEvent(F.input,"keypress",function(N){if((N.ctrlKey||N.metaKey)&&(N.keyCode==89||N.keyCode==90)){N.preventDefault()}});var M=function(){if(t.isIE||(B&&B.text!=F.input.value)){if(y==undefined){C="paste";z();E()}}};a.addEvent(F.input,"keydown",H);a.addEvent(F.input,"keydown",K);a.addEvent(F.input,"mousedown",function(){w("moving")});F.input.onpaste=M;F.input.ondrop=M};var J=function(){A();E(true);z()};J()}function b(x,z){var w=this;var y=x.input;this.init=function(){if(!a.isVisible(y)){return}if(!z&&u.activeElement&&u.activeElement!==y){return}this.setInputAreaSelectionStartEnd();this.scrollTop=y.scrollTop;if(!this.text&&y.selectionStart||y.selectionStart===0){this.text=y.value}};this.setInputAreaSelection=function(){if(!a.isVisible(y)){return}if(y.selectionStart!==undefined&&!t.isOpera){y.focus();y.selectionStart=w.start;y.selectionEnd=w.end;y.scrollTop=w.scrollTop}else{if(u.selection){if(u.activeElement&&u.activeElement!==y){return}y.focus();var A=y.createTextRange();A.moveStart("character",-y.value.length);A.moveEnd("character",-y.value.length);A.moveEnd("character",w.end);A.moveStart("character",w.start);A.select()}}};this.setInputAreaSelectionStartEnd=function(){if(!x.ieCachedRange&&(y.selectionStart||y.selectionStart===0)){w.start=y.selectionStart;w.end=y.selectionEnd}else{if(u.selection){w.text=a.fixEolChars(y.value);var D=x.ieCachedRange||u.selection.createRange();var E=a.fixEolChars(D.text);var C="\x07";var B=C+E+C;D.text=B;var F=a.fixEolChars(y.value);D.moveStart("character",-B.length);D.text=E;w.start=F.indexOf(C);w.end=F.lastIndexOf(C)-C.length;var A=w.text.length-a.fixEolChars(y.value).length;if(A){D.moveStart("character",-E.length);while(A--){E+="\n";w.end+=1}D.text=E}if(x.ieCachedRange){w.scrollTop=x.ieCachedScrollTop}x.ieCachedRange=null;this.setInputAreaSelection()}}};this.restore=function(){if(w.text!=undefined&&w.text!=y.value){y.value=w.text}this.setInputAreaSelection();y.scrollTop=w.scrollTop};this.getChunks=function(){var A=new r();A.before=a.fixEolChars(w.text.substring(0,w.start));A.startTag="";A.selection=a.fixEolChars(w.text.substring(w.start,w.end));A.endTag="";A.after=a.fixEolChars(w.text.substring(w.end));A.scrollTop=w.scrollTop;return A};this.setChunks=function(A){A.before=A.before+A.startTag;A.after=A.endTag+A.after;this.start=A.before.length;this.end=A.before.length+A.selection.length;this.text=A.before+A.selection+A.after;this.scrollTop=A.scrollTop};this.init()}function h(Q,z,K){var x=this;var E;var D;var N;var y=3000;var F="delayed";var B=function(S,T){a.addEvent(S,"input",T);S.onpaste=T;S.ondrop=T;a.addEvent(S,"keypress",T);a.addEvent(S,"keydown",T)};var J=function(){var S=0;if(window.innerHeight){S=window.pageYOffset}else{if(u.documentElement&&u.documentElement.scrollTop){S=u.documentElement.scrollTop}else{if(u.body){S=u.body.scrollTop}}}return S};var C=function(){if(!z.preview){return}var U=z.input.value;if(U&&U==N){return}else{N=U}var T=new Date().getTime();U=Q.makeHtml(U);var S=new Date().getTime();D=S-T;w(U)};var P=function(){if(E){clearTimeout(E);E=undefined}if(F!=="manual"){var S=0;if(F==="delayed"){S=D}if(S>y){S=y}E=setTimeout(C,S)}};var A=function(S){if(S.scrollHeight<=S.clientHeight){return 1}return S.scrollTop/(S.scrollHeight-S.clientHeight)};var R=function(){if(z.preview){z.preview.scrollTop=(z.preview.scrollHeight-z.preview.clientHeight)*A(z.preview)}};this.refresh=function(S){if(S){N="";C()}else{P()}};this.processingTime=function(){return D};var G=true;var H=function(V){var U=z.preview;var T=U.parentNode;var S=U.nextSibling;T.removeChild(U);U.innerHTML=V;if(!S){T.appendChild(U)}else{T.insertBefore(U,S)}};var M=function(S){z.preview.innerHTML=S};var I;var L=function(T){if(I){return I(T)}try{M(T);I=M}catch(S){I=H;I(T)}};var w=function(U){var S=s.getTop(z.input)-J();if(z.preview){L(U);K()}R();if(G){G=false;return}var T=s.getTop(z.input)-J();if(t.isIE){setTimeout(function(){window.scrollBy(0,T-S)},0)}else{window.scrollBy(0,T-S)}};var O=function(){B(z.input,P);C();if(z.preview){z.preview.scrollTop=0}};O()}l.prompt=function(x,B,E,z){var w;var y;if(E===undefined){E=""}var A=function(F){var G=(F.charCode||F.keyCode);if(G===27){C(true)}};var C=function(F){a.removeEvent(u.body,"keydown",A);var G=y.value;if(F){G=null}else{G=G.replace(/^http:\/\/(https?|ftp):\/\//,"$1://");if(!/^(?:https?|ftp):\/\//.test(G)){G="http://"+G}}$(w).modal("hide");z(G);return false};var D=function(){w=u.createElement("div");w.className="modal fade";var J=u.createElement("div");J.className="modal-dialog";w.appendChild(J);var M=u.createElement("div");M.className="modal-content";J.appendChild(M);var K=u.createElement("div");K.className="modal-header";K.innerHTML='× '+x+"
";M.appendChild(K);var L=u.createElement("div");L.className="modal-body";M.appendChild(L);var O=u.createElement("div");O.className="modal-footer";M.appendChild(O);var I=u.createElement("p");I.innerHTML=B;I.style.padding="5px";L.appendChild(I);var G=u.createElement("form"),F=G.style;G.onsubmit=function(){return C(false)};F.padding="0";F.margin="0";L.appendChild(G);y=u.createElement("input");y.type="text";y.value=E;y.className="form-control";F=y.style;F.display="block";F.width="80%";F.marginLeft=F.marginRight="auto";G.appendChild(y);var N=u.createElement("button");N.className="btn btn-primary";N.type="button";N.onclick=function(){return C(false)};N.innerHTML="OK";var H=u.createElement("button");H.className="btn btn-primary";H.type="button";H.onclick=function(){return C(true)};H.innerHTML="Cancel";O.appendChild(N);O.appendChild(H);a.addEvent(u.body,"keydown",A);u.body.appendChild(w)};setTimeout(function(){D();var G=E.length;if(y.selectionStart!==undefined){y.selectionStart=0;y.selectionEnd=G}else{if(y.createTextRange){var F=y.createTextRange();F.collapse(false);F.moveStart("character",-G);F.moveEnd("character",G);F.select()}}$(w).on("shown",function(){y.focus()});$(w).on("hidden",function(){w.parentNode.removeChild(w)});$(w).modal()},0)};function q(H,C,J,y,G,B){var A=C.input,E={};z();var D="keydown";if(t.isOpera){D="keypress"}a.addEvent(A,D,function(L){if((L.ctrlKey||L.metaKey)&&!L.altKey&&!L.shiftKey){var M=L.charCode||L.keyCode;var K=String.fromCharCode(M).toLowerCase();switch(K){case"b":I(E.bold);break;case"i":I(E.italic);break;case"l":I(E.link);break;case"q":I(E.quote);break;case"k":I(E.code);break;case"g":I(E.image);break;case"o":I(E.olist);break;case"u":I(E.ulist);break;case"h":I(E.heading);break;case"r":I(E.hr);break;case"y":I(E.redo);break;case"z":if(L.shiftKey){I(E.redo)}else{I(E.undo)}break;default:return}if(L.preventDefault){L.preventDefault()}if(window.event){window.event.returnValue=false}}});a.addEvent(A,"keyup",function(L){if(L.shiftKey&&!L.ctrlKey&&!L.metaKey){var M=L.charCode||L.keyCode;if(M===13){var K={};K.textOp=x("doAutoindent");I(K)}}});if(t.isIE){a.addEvent(A,"keydown",function(K){var L=K.keyCode;if(L===27){return false}})}function I(L){A.focus();if(L.textOp){if(J){J.setCommandMode()}var N=new b(C);if(!N){return}var O=N.getChunks();var K=function(){A.focus();if(O){N.setChunks(O)}N.restore();y.refresh()};var M=L.textOp(O,K);if(!M){K()}}if(L.execute){L.execute(J)}}function w(K,L){if(L){K.disabled=false;if(!K.isHelp){K.onclick=function(){if(this.onmouseout){this.onmouseout()}I(this);return false}}}else{K.disabled=true}}function x(K){if(typeof K==="string"){K=G[K]}return function(){K.apply(G,arguments)}}function z(){var L=C.buttonBar;var N=document.createElement("div");N.id="wmd-button-row"+H;N.className="btn-toolbar";N=L.appendChild(N);var M=function(X,V,T,W,U){var S=document.createElement("button");S.className="btn";var R=document.createElement("i");R.className=T;S.id=X+H;S.appendChild(R);S.title=V;$(S).tooltip({placement:"bottom",container:"body"});if(W){S.textOp=W}w(S,true);if(U){U.appendChild(S)}else{N.appendChild(S)}return S};var P=function(R){var S=document.createElement("div");S.className="btn-group wmd-button-group"+R;S.id="wmd-button-group"+R+H;N.appendChild(S);return S};group1=P(1);E.bold=M("wmd-bold-button","Bold - Ctrl+B","fa fa-bold",x("doBold"),group1);E.italic=M("wmd-italic-button","Italic - Ctrl+I","fa fa-italic",x("doItalic"),group1);group2=P(2);E.link=M("wmd-link-button","Link - Ctrl+L","fa fa-link",x(function(R,S){return this.doLinkOrImage(R,S,false)}),group2);E.quote=M("wmd-quote-button","Blockquote - Ctrl+Q","fa fa-quote-left",x("doBlockquote"),group2);E.code=M("wmd-code-button","Code Sample - Ctrl+K","fa fa-code",x("doCode"),group2);E.image=M("wmd-image-button","Image - Ctrl+G","fa fa-picture-o",x(function(R,S){return this.doLinkOrImage(R,S,true)}),group2);group3=P(3);E.olist=M("wmd-olist-button","Numbered List - Ctrl+O","fa fa-list-ol",x(function(R,S){this.doList(R,S,true)}),group3);E.ulist=M("wmd-ulist-button","Bulleted List - Ctrl+U","fa fa-list-ul",x(function(R,S){this.doList(R,S,false)}),group3);E.heading=M("wmd-heading-button","Heading - Ctrl+H","fa fa-header",x("doHeading"),group3);E.hr=M("wmd-hr-button","Horizontal Rule - Ctrl+R","fa fa-ellipsis-h",x("doHorizontalRule"),group3);group4=P(4);E.undo=M("wmd-undo-button","Undo - Ctrl+Z","fa fa-undo",null,group4);E.undo.execute=function(R){if(R){R.undo()}};var O=/win/.test(g.platform.toLowerCase())?"Redo - Ctrl+Y":"Redo - Ctrl+Shift+Z";E.redo=M("wmd-redo-button",O,"fa fa-rotate-right",null,group4);E.redo.execute=function(R){if(R){R.redo()}};if(B){group5=P(5);group5.className=group5.className+" pull-right";var Q=document.createElement("button");var K=document.createElement("i");K.className="fa fa-question";Q.appendChild(K);Q.className="btn";Q.id="wmd-help-button"+H;Q.isHelp=true;Q.title=B.title||j;$(Q).tooltip({placement:"bottom",container:"body"});Q.onclick=B.handler;w(Q,true);group5.appendChild(Q);E.help=Q}F()}function F(){if(J){w(E.undo,J.canUndo());w(E.redo,J.canRedo())}}this.setUndoRedoButtonStates=F}function k(w){this.hooks=w}var f=k.prototype;f.prefixes="(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";f.unwrap=function(x){var w=new m("([^\\n])\\n(?!(\\n|"+this.prefixes+"))","g");x.selection=x.selection.replace(w,"$1 $2")};f.wrap=function(x,w){this.unwrap(x);var z=new m("(.{1,"+w+"})( +|$\\n?)","gm"),y=this;x.selection=x.selection.replace(z,function(A,B){if(new m("^"+y.prefixes,"").test(A)){return A}return B+"\n"});x.selection=x.selection.replace(/\s+$/,"")};f.doBold=function(w,x){return this.doBorI(w,x,2,"strong text")};f.doItalic=function(w,x){return this.doBorI(w,x,1,"emphasized text")};f.doBorI=function(C,A,B,w){C.trimWhitespace();C.selection=C.selection.replace(/\n{2,}/g,"\n");var z=/(\**$)/.exec(C.before)[0];var x=/(^\**)/.exec(C.after)[0];var D=Math.min(z.length,x.length);if((D>=B)&&(D!=2||B!=1)){C.before=C.before.replace(m("[*]{"+B+"}$",""),"");C.after=C.after.replace(m("^[*]{"+B+"}",""),"")}else{if(!C.selection&&x){C.after=C.after.replace(/^([*_]*)/,"");C.before=C.before.replace(/(\s?)$/,"");var y=m.$1;C.before=C.before+x+y}else{if(!C.selection&&!x){C.selection=w}var E=B<=1?"*":"**";C.before=C.before+E;C.after=E+C.after}}return};f.stripLinkDefs=function(x,w){x=x.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,function(B,C,y,z,A){w[C]=B.replace(/\s*$/,"");if(z){w[C]=B.replace(/["(](.+?)[")]$/,"");return z+A}return""});return x};f.addLinkDef=function(D,z){var w=0;var y={};D.before=this.stripLinkDefs(D.before,y);D.selection=this.stripLinkDefs(D.selection,y);D.after=this.stripLinkDefs(D.after,y);var x="";var C=/(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;var B=function(F){w++;F=F.replace(/^[ ]{0,3}\[(\d+)\]:/," ["+w+"]:");x+="\n"+F};var A=function(G,J,H,I,K,F){H=H.replace(C,A);if(y[K]){B(y[K]);return J+H+I+w+F}return G};D.before=D.before.replace(C,A);if(z){B(z)}else{D.selection=D.selection.replace(C,A)}var E=w;D.after=D.after.replace(C,A);if(D.after){D.after=D.after.replace(/\n*$/,"")}if(!D.after){D.selection=D.selection.replace(/\n*$/,"")}D.after+="\n\n"+x;return E};function n(w){return w.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/,function(y,x,z){x=x.replace(/\?.*$/,function(A){return A.replace(/\+/g," ")});x=decodeURIComponent(x);x=encodeURI(x).replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29");x=x.replace(/\?.*$/,function(A){return A.replace(/\+/g,"%2b")});if(z){z=z.trim?z.trim():z.replace(/^\s*/,"").replace(/\s*$/,"");z=$.trim(z).replace(/"/g,"quot;").replace(/\(/g,"(").replace(/\)/g,")").replace(//g,">")}return z?x+' "'+z+'"':x})}f.doLinkOrImage=function(w,y,B){w.trimWhitespace();w.findTags(/\s*!?\[/,/\][ ]?(?:\n[ ]*)?(\[.*?\])?/);var x;if(w.endTag.length>1&&w.startTag.length>0){w.startTag=w.startTag.replace(/!?\[/,"");w.endTag="";this.addLinkDef(w,null)}else{w.selection=w.startTag+w.selection+w.endTag;w.startTag=w.endTag="";if(/\n\n/.test(w.selection)){this.addLinkDef(w,null);return}var z=this;var A=function(E){if(E!==null){w.selection=(" "+w.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g,"$1\\").substr(1);var D=" [999]: "+n(E);var C=z.addLinkDef(w,D);w.startTag=B?"![":"[";w.endTag="]["+C+"]";if(!w.selection){if(B){w.selection="enter image description here"}else{w.selection="enter link description here"}}}y()};if(B){if(!this.hooks.insertImageDialog(A)){l.prompt("Insert Image",p,c,A)}}else{l.prompt("Insert Link",v,d,A)}return true}};f.doAutoindent=function(y,z){var x=this,w=false;y.before=y.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/,"\n\n");y.before=y.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/,"\n\n");y.before=y.before.replace(/(\n|^)[ \t]+\n$/,"\n\n");if(!y.selection&&!/^[ \t]*(?:\n|$)/.test(y.after)){y.after=y.after.replace(/^[^\n]*/,function(A){y.selection=A;return""});w=true}if(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(y.before)){if(x.doList){x.doList(y)}}if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(y.before)){if(x.doBlockquote){x.doBlockquote(y)}}if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(y.before)){if(x.doCode){x.doCode(y)}}if(w){y.after=y.selection+y.after;y.selection=""}};f.doBlockquote=function(D,y){D.selection=D.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,function(J,I,H,G){D.before+=I;D.after=G+D.after;return H});D.before=D.before.replace(/(>[ \t]*)$/,function(H,G){D.selection=G+D.selection;return""});D.selection=D.selection.replace(/^(\s|>)+$/,"");D.selection=D.selection||"Blockquote";var C="",B="",E;if(D.before){var F=D.before.replace(/\n$/,"").split("\n");var A=false;for(var z=0;z0;if(/^>/.test(E)){x=true;if(!A&&E.length>1){A=true}}else{if(/^[ \t]*$/.test(E)){x=true}else{x=A}}if(x){C+=E+"\n"}else{B+=C+E;C="\n"}}if(!/(^|\n)>/.test(C)){B+=C;C=""}}D.startTag=C;D.before=B;if(D.after){D.after=D.after.replace(/^\n?/,"\n")}D.after=D.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,function(G){D.endTag=G;return""});var w=function(H){var G=H?"> ":"";if(D.startTag){D.startTag=D.startTag.replace(/\n((>|\s)*)\n$/,function(J,I){return"\n"+I.replace(/^[ ]{0,3}>?[ \t]*$/gm,G)+"\n"})}if(D.endTag){D.endTag=D.endTag.replace(/^\n((>|\s)*)\n/,function(J,I){return"\n"+I.replace(/^[ ]{0,3}>?[ \t]*$/gm,G)+"\n"})}};if(/^(?![ ]{0,3}>)/m.test(D.selection)){this.wrap(D,o.lineLength-2);D.selection=D.selection.replace(/^/gm,"> ");w(true);D.skipLines()}else{D.selection=D.selection.replace(/^[ ]{0,3}> ?/gm,"");this.unwrap(D);w(false);if(!/^(\n|^)[ ]{0,3}>/.test(D.selection)&&D.startTag){D.startTag=D.startTag.replace(/\n{0,2}$/,"\n\n")}if(!/(\n|^)[ ]{0,3}>.*$/.test(D.selection)&&D.endTag){D.endTag=D.endTag.replace(/^\n{0,2}/,"\n\n")}}D.selection=this.hooks.postBlockquoteCreation(D.selection);if(!/\n/.test(D.selection)){D.selection=D.selection.replace(/^(> *)/,function(G,H){D.startTag+=H;return""})}};f.doCode=function(w,x){var z=/\S[ ]*$/.test(w.before);var B=/^[ ]*\S/.test(w.after);if((!B&&!z)||/\n/.test(w.selection)){w.before=w.before.replace(/[ ]{4}$/,function(C){w.selection=C+w.selection;return""});var A=1;var y=1;if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(w.before)){A=0}if(/^\n(\t|[ ]{4,})/.test(w.after)){y=0}w.skipLines(A,y);if(!w.selection){w.startTag=" ";w.selection="enter code here"}else{if(/^[ ]{0,3}\S/m.test(w.selection)){if(/\n/.test(w.selection)){w.selection=w.selection.replace(/^/gm," ")}else{w.before+=" "}}else{w.selection=w.selection.replace(/^[ ]{4}/gm,"")}}}else{w.trimWhitespace();w.findTags(/`/,/`/);if(!w.startTag&&!w.endTag){w.startTag=w.endTag="`";if(!w.selection){w.selection="enter code here"}}else{if(w.endTag&&!w.startTag){w.before+=w.endTag;w.endTag=""}else{w.startTag=w.endTag=""}}}};f.doList=function(H,A,z){var J=/(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;var I=/^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;var w="-";var E=1;var C=function(){var K;if(z){K=" "+E+". ";E++}else{K=" "+w+" "}return K};var D=function(K){if(z===undefined){z=/^\s*\d/.test(K)}K=K.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,function(L){return C()});return K};H.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/,null);if(H.before&&!/\n$/.test(H.before)&&!/^\n/.test(H.startTag)){H.before+=H.startTag;H.startTag=""}if(H.startTag){var y=/\d+[.]/.test(H.startTag);H.startTag="";H.selection=H.selection.replace(/\n[ ]{4}/g,"\n");this.unwrap(H);H.skipLines();if(y){H.after=H.after.replace(I,D)}if(z==y){return}}var B=1;H.before=H.before.replace(J,function(K){if(/^\s*([*+-])/.test(K)){w=m.$1}B=/[^\n]\n\n[^\n]/.test(K)?1:0;return D(K)});if(!H.selection){H.selection="List item"}var F=C();var x=1;H.after=H.after.replace(I,function(K){x=/[^\n]\n\n[^\n]/.test(K)?1:0;return D(K)});H.trimWhitespace(true);H.skipLines(B,x,true);H.startTag=F;var G=F.replace(/./g," ");this.wrap(H,o.lineLength-G.length);H.selection=H.selection.replace(/\n/g,"\n"+G)};f.doHeading=function(y,z){y.selection=y.selection.replace(/\s+/g," ");y.selection=y.selection.replace(/(^\s+|\s+$)/g,"");if(!y.selection){y.startTag="## ";y.selection="Heading";y.endTag=" ##";return}var A=0;y.findTags(/#+[ ]*/,/[ ]*#+/);if(/#+/.test(y.startTag)){A=m.lastMatch.length}y.startTag=y.endTag="";y.findTags(null,/\s?(-+|=+)/);if(/=+/.test(y.endTag)){A=1}if(/-+/.test(y.endTag)){A=2}y.startTag=y.endTag="";y.skipLines(1,1);var B=A==0?2:A-1;if(B>0){var x=B>=2?"-":"=";var w=y.selection.length;if(w>o.lineLength){w=o.lineLength}y.endTag="\n";while(w--){y.endTag+=x}}};f.doHorizontalRule=function(w,x){w.startTag="----------\n";w.selection="";w.skipLines(2,1,true)}})();(function(){var b,e;if(typeof exports==="object"&&typeof require==="function"){b=exports;e=require("./Markdown.Converter").Converter}else{b=window.Markdown;e=b.Converter}b.getSanitizingConverter=function(){var j=new e();j.hooks.chain("postConversion",h);j.hooks.chain("postConversion",a);return j};function h(j){return j.replace(/<[^>]*>?/gi,g)}var i=/^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i;var d=/^(]+")?\s?>|<\/a>)$/i;var c=/^(
]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i;var f=/^(|<\/pre>)$/i;function g(j){if(j.match(i)||j.match(d)||j.match(c)||j.match(f)){return j}else{return""}}function a(n){if(n==""){return""}var t=/<\/?\w+[^>]*(\s|$|>)/g;var u=n.toLowerCase().match(t);var s=(u||[]).length;if(s==0){return n}var r,v;var q="![]()
";var o;var p=[];var k=[];var m=false;for(var j=0;j")>-1){continue}v=u[j];o=-1;if(!/^<\//.test(v)){for(var l=j+1;l"){o=l;break}}}if(o==-1){m=k[j]=true}else{p[o]=true}}if(!m){return n}var j=0;n=n.replace(t,function(w){var x=k[j]?"":w;j++;return x});return n}})();(function(a){a.fn.pagedownBootstrap=function(b){var c=a.extend({sanatize:true,help:null,hooks:Array()},b);return this.each(function(){var f=null;if(c.sanatize){f=Markdown.getSanitizingConverter()}else{f=new Markdown.Converter()}for(var d in c.hooks){var g=c.hooks[d];if(typeof g!=="object"||typeof g.event==="underfined"||typeof g.callback!=="function"){continue}f.hooks.chain(g.event,g.callback)}var j="wmd-input";var h=0;while(a("#"+j+"-"+h.toString()).length>0){h++}a(this).attr("id",j+"-"+h.toString());a(this).wrap('');a(this).before('');a(this).after('');a(this).addClass("wmd-input");help=null;if(a.isFunction(c.help)){help={handler:c.help}}var e=new Markdown.Editor(f,"-"+h.toString(),help);e.run()})}})(jQuery);
--------------------------------------------------------------------------------
/static/markdown/js/Markdown.Converter.js:
--------------------------------------------------------------------------------
1 | var Markdown;
2 |
3 | if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module
4 | Markdown = exports;
5 | else
6 | Markdown = {};
7 |
8 | // The following text is included for historical reasons, but should
9 | // be taken with a pinch of salt; it's not all true anymore.
10 |
11 | //
12 | // Wherever possible, Showdown is a straight, line-by-line port
13 | // of the Perl version of Markdown.
14 | //
15 | // This is not a normal parser design; it's basically just a
16 | // series of string substitutions. It's hard to read and
17 | // maintain this way, but keeping Showdown close to the original
18 | // design makes it easier to port new features.
19 | //
20 | // More importantly, Showdown behaves like markdown.pl in most
21 | // edge cases. So web applications can do client-side preview
22 | // in Javascript, and then build identical HTML on the server.
23 | //
24 | // This port needs the new RegExp functionality of ECMA 262,
25 | // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers
26 | // should do fine. Even with the new regular expression features,
27 | // We do a lot of work to emulate Perl's regex functionality.
28 | // The tricky changes in this file mostly have the "attacklab:"
29 | // label. Major or self-explanatory changes don't.
30 | //
31 | // Smart diff tools like Araxis Merge will be able to match up
32 | // this file with markdown.pl in a useful way. A little tweaking
33 | // helps: in a copy of markdown.pl, replace "#" with "//" and
34 | // replace "$text" with "text". Be sure to ignore whitespace
35 | // and line endings.
36 | //
37 |
38 |
39 | //
40 | // Usage:
41 | //
42 | // var text = "Markdown *rocks*.";
43 | //
44 | // var converter = new Markdown.Converter();
45 | // var html = converter.makeHtml(text);
46 | //
47 | // alert(html);
48 | //
49 | // Note: move the sample code to the bottom of this
50 | // file before uncommenting it.
51 | //
52 |
53 | (function () {
54 |
55 | function identity(x) { return x; }
56 | function returnFalse(x) { return false; }
57 |
58 | function HookCollection() { }
59 |
60 | HookCollection.prototype = {
61 |
62 | chain: function (hookname, func) {
63 | var original = this[hookname];
64 | if (!original)
65 | throw new Error("unknown hook " + hookname);
66 |
67 | if (original === identity)
68 | this[hookname] = func;
69 | else
70 | this[hookname] = function (x) { return func(original(x)); }
71 | },
72 | set: function (hookname, func) {
73 | if (!this[hookname])
74 | throw new Error("unknown hook " + hookname);
75 | this[hookname] = func;
76 | },
77 | addNoop: function (hookname) {
78 | this[hookname] = identity;
79 | },
80 | addFalse: function (hookname) {
81 | this[hookname] = returnFalse;
82 | }
83 | };
84 |
85 | Markdown.HookCollection = HookCollection;
86 |
87 | // g_urls and g_titles allow arbitrary user-entered strings as keys. This
88 | // caused an exception (and hence stopped the rendering) when the user entered
89 | // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this
90 | // (since no builtin property starts with "s_"). See
91 | // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug
92 | // (granted, switching from Array() to Object() alone would have left only __proto__
93 | // to be a problem)
94 | function SaveHash() { }
95 | SaveHash.prototype = {
96 | set: function (key, value) {
97 | this["s_" + key] = value;
98 | },
99 | get: function (key) {
100 | return this["s_" + key];
101 | }
102 | };
103 |
104 | Markdown.Converter = function () {
105 | var pluginHooks = this.hooks = new HookCollection();
106 | pluginHooks.addNoop("plainLinkText"); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
107 | pluginHooks.addNoop("preConversion"); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked
108 | pluginHooks.addNoop("postConversion"); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml
109 |
110 | //
111 | // Private state of the converter instance:
112 | //
113 |
114 | // Global hashes, used by various utility routines
115 | var g_urls;
116 | var g_titles;
117 | var g_html_blocks;
118 |
119 | // Used to track when we're inside an ordered or unordered list
120 | // (see _ProcessListItems() for details):
121 | var g_list_level;
122 |
123 | this.makeHtml = function (text) {
124 |
125 | //
126 | // Main function. The order in which other subs are called here is
127 | // essential. Link and image substitutions need to happen before
128 | // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the
129 | // and
tags get encoded.
130 | //
131 |
132 | // This will only happen if makeHtml on the same converter instance is called from a plugin hook.
133 | // Don't do that.
134 | if (g_urls)
135 | throw new Error("Recursive call to converter.makeHtml");
136 |
137 | // Create the private state objects.
138 | g_urls = new SaveHash();
139 | g_titles = new SaveHash();
140 | g_html_blocks = [];
141 | g_list_level = 0;
142 |
143 | text = pluginHooks.preConversion(text);
144 |
145 | // attacklab: Replace ~ with ~T
146 | // This lets us use tilde as an escape char to avoid md5 hashes
147 | // The choice of character is arbitray; anything that isn't
148 | // magic in Markdown will work.
149 | text = text.replace(/~/g, "~T");
150 |
151 | // attacklab: Replace $ with ~D
152 | // RegExp interprets $ as a special character
153 | // when it's in a replacement string
154 | text = text.replace(/\$/g, "~D");
155 |
156 | // Standardize line endings
157 | text = text.replace(/\r\n/g, "\n"); // DOS to Unix
158 | text = text.replace(/\r/g, "\n"); // Mac to Unix
159 |
160 | // Make sure text begins and ends with a couple of newlines:
161 | text = "\n\n" + text + "\n\n";
162 |
163 | // Convert all tabs to spaces.
164 | text = _Detab(text);
165 |
166 | // Strip any lines consisting only of spaces and tabs.
167 | // This makes subsequent regexen easier to write, because we can
168 | // match consecutive blank lines with /\n+/ instead of something
169 | // contorted like /[ \t]*\n+/ .
170 | text = text.replace(/^[ \t]+$/mg, "");
171 |
172 | // Turn block-level HTML blocks into hash entries
173 | text = _HashHTMLBlocks(text);
174 |
175 | // Strip link definitions, store in hashes.
176 | text = _StripLinkDefinitions(text);
177 |
178 | text = _RunBlockGamut(text);
179 |
180 | text = _UnescapeSpecialChars(text);
181 |
182 | // attacklab: Restore dollar signs
183 | text = text.replace(/~D/g, "$$");
184 |
185 | // attacklab: Restore tildes
186 | text = text.replace(/~T/g, "~");
187 |
188 | text = pluginHooks.postConversion(text);
189 |
190 | g_html_blocks = g_titles = g_urls = null;
191 |
192 | return text;
193 | };
194 |
195 | function _StripLinkDefinitions(text) {
196 | //
197 | // Strips link definitions from text, stores the URLs and titles in
198 | // hash references.
199 | //
200 |
201 | // Link defs are in the form: ^[id]: url "optional title"
202 |
203 | /*
204 | text = text.replace(/
205 | ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
206 | [ \t]*
207 | \n? // maybe *one* newline
208 | [ \t]*
209 | (\S+?)>? // url = $2
210 | (?=\s|$) // lookahead for whitespace instead of the lookbehind removed below
211 | [ \t]*
212 | \n? // maybe one newline
213 | [ \t]*
214 | ( // (potential) title = $3
215 | (\n*) // any lines skipped = $4 attacklab: lookbehind removed
216 | [ \t]+
217 | ["(]
218 | (.+?) // title = $5
219 | [")]
220 | [ \t]*
221 | )? // title is optional
222 | (?:\n+|$)
223 | /gm, function(){...});
224 | */
225 |
226 | text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm,
227 | function (wholeMatch, m1, m2, m3, m4, m5) {
228 | m1 = m1.toLowerCase();
229 | g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive
230 | if (m4) {
231 | // Oops, found blank lines, so it's not a title.
232 | // Put back the parenthetical statement we stole.
233 | return m3;
234 | } else if (m5) {
235 | g_titles.set(m1, m5.replace(/"/g, """));
236 | }
237 |
238 | // Completely remove the definition from the text
239 | return "";
240 | }
241 | );
242 |
243 | return text;
244 | }
245 |
246 | function _HashHTMLBlocks(text) {
247 |
248 | // Hashify HTML blocks:
249 | // We only want to do this for block-level HTML tags, such as headers,
250 | // lists, and tables. That's because we still want to wrap s around
251 | // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
252 | // phrase emphasis, and spans. The list of tags we're looking for is
253 | // hard-coded:
254 | var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
255 | var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
256 |
257 | // First, look for nested blocks, e.g.:
258 | //
259 | //
260 | // tags for inner block must be indented.
261 | //
262 | //
263 | //
264 | // The outermost tags must start at the left margin for this to match, and
265 | // the inner nested divs must be indented.
266 | // We need to do this before the next, more liberal match, because the next
267 | // match will start at the first `` and stop at the first ``.
268 |
269 | // attacklab: This regex can be expensive when it fails.
270 |
271 | /*
272 | text = text.replace(/
273 | ( // save in $1
274 | ^ // start of line (with /m)
275 | <($block_tags_a) // start tag = $2
276 | \b // word break
277 | // attacklab: hack around khtml/pcre bug...
278 | [^\r]*?\n // any number of lines, minimally matching
279 | \2> // the matching end tag
280 | [ \t]* // trailing spaces/tabs
281 | (?=\n+) // followed by a newline
282 | ) // attacklab: there are sentinel newlines at end of document
283 | /gm,function(){...}};
284 | */
285 | 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);
286 |
287 | //
288 | // Now match more liberally, simply from `\n` to ` \n`
289 | //
290 |
291 | /*
292 | text = text.replace(/
293 | ( // save in $1
294 | ^ // start of line (with /m)
295 | <($block_tags_b) // start tag = $2
296 | \b // word break
297 | // attacklab: hack around khtml/pcre bug...
298 | [^\r]*? // any number of lines, minimally matching
299 | .*\2> // the matching end tag
300 | [ \t]* // trailing spaces/tabs
301 | (?=\n+) // followed by a newline
302 | ) // attacklab: there are sentinel newlines at end of document
303 | /gm,function(){...}};
304 | */
305 | 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);
306 |
307 | // Special case just for
. It was easier to make a special case than
308 | // to make the other regex more complicated.
309 |
310 | /*
311 | text = text.replace(/
312 | \n // Starting after a blank line
313 | [ ]{0,3}
314 | ( // save in $1
315 | (<(hr) // start tag = $2
316 | \b // word break
317 | ([^<>])*?
318 | \/?>) // the matching end tag
319 | [ \t]*
320 | (?=\n{2,}) // followed by a blank line
321 | )
322 | /g,hashElement);
323 | */
324 | text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement);
325 |
326 | // Special case for standalone HTML comments:
327 |
328 | /*
329 | text = text.replace(/
330 | \n\n // Starting after a blank line
331 | [ ]{0,3} // attacklab: g_tab_width - 1
332 | ( // save in $1
333 | -]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackoverflow.com/q/95256
335 | >
336 | [ \t]*
337 | (?=\n{2,}) // followed by a blank line
338 | )
339 | /g,hashElement);
340 | */
341 | text = text.replace(/\n\n[ ]{0,3}(-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashElement);
342 |
343 | // PHP and ASP-style processor instructions (...?> and <%...%>)
344 |
345 | /*
346 | text = text.replace(/
347 | (?:
348 | \n\n // Starting after a blank line
349 | )
350 | ( // save in $1
351 | [ ]{0,3} // attacklab: g_tab_width - 1
352 | (?:
353 | <([?%]) // $2
354 | [^\r]*?
355 | \2>
356 | )
357 | [ \t]*
358 | (?=\n{2,}) // followed by a blank line
359 | )
360 | /g,hashElement);
361 | */
362 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement);
363 |
364 | return text;
365 | }
366 |
367 | function hashElement(wholeMatch, m1) {
368 | var blockText = m1;
369 |
370 | // Undo double lines
371 | blockText = blockText.replace(/^\n+/, "");
372 |
373 | // strip trailing blank lines
374 | blockText = blockText.replace(/\n+$/g, "");
375 |
376 | // Replace the element text with a marker ("~KxK" where x is its key)
377 | blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n";
378 |
379 | return blockText;
380 | }
381 |
382 | function _RunBlockGamut(text, doNotUnhash) {
383 | //
384 | // These are all the transformations that form block-level
385 | // tags like paragraphs, headers, and list items.
386 | //
387 | text = _DoHeaders(text);
388 |
389 | // Do Horizontal Rules:
390 | var replacement = "
\n";
391 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement);
392 | text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement);
393 | text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement);
394 |
395 | text = _DoLists(text);
396 | text = _DoCodeBlocks(text);
397 | text = _DoBlockQuotes(text);
398 |
399 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that
400 | // was to escape raw HTML in the original Markdown source. This time,
401 | // we're escaping the markup we've just created, so that we don't wrap
402 | // tags around block-level tags.
403 | text = _HashHTMLBlocks(text);
404 | text = _FormParagraphs(text, doNotUnhash);
405 |
406 | return text;
407 | }
408 |
409 | function _RunSpanGamut(text) {
410 | //
411 | // These are all the transformations that occur *within* block-level
412 | // tags like paragraphs, headers, and list items.
413 | //
414 |
415 | text = _DoCodeSpans(text);
416 | text = _EscapeSpecialCharsWithinTagAttributes(text);
417 | text = _EncodeBackslashEscapes(text);
418 |
419 | // Process anchor and image tags. Images must come first,
420 | // because ![foo][f] looks like an anchor.
421 | text = _DoImages(text);
422 | text = _DoAnchors(text);
423 |
424 | // Make links out of things like ` `
425 | // Must come after _DoAnchors(), because you can use < and >
426 | // delimiters in inline links like [this]().
427 | text = _DoAutoLinks(text);
428 |
429 | text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now
430 |
431 | text = _EncodeAmpsAndAngles(text);
432 | text = _DoItalicsAndBold(text);
433 |
434 | // Do hard breaks:
435 | text = text.replace(/ +\n/g, "
\n");
436 |
437 | return text;
438 | }
439 |
440 | function _EscapeSpecialCharsWithinTagAttributes(text) {
441 | //
442 | // Within tags -- meaning between < and > -- encode [\ ` * _] so they
443 | // don't conflict with their use in Markdown for code, italics and strong.
444 | //
445 |
446 | // Build a regex to find HTML tags and comments. See Friedl's
447 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
448 |
449 | // SE: changed the comment part of the regex
450 |
451 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi;
452 |
453 | text = text.replace(regex, function (wholeMatch) {
454 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`");
455 | tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987
456 | return tag;
457 | });
458 |
459 | return text;
460 | }
461 |
462 | function _DoAnchors(text) {
463 | //
464 | // Turn Markdown link shortcuts into XHTML tags.
465 | //
466 | //
467 | // First, handle reference-style links: [link text] [id]
468 | //
469 |
470 | /*
471 | text = text.replace(/
472 | ( // wrap whole match in $1
473 | \[
474 | (
475 | (?:
476 | \[[^\]]*\] // allow brackets nested one level
477 | |
478 | [^\[] // or anything else
479 | )*
480 | )
481 | \]
482 |
483 | [ ]? // one optional space
484 | (?:\n[ ]*)? // one optional newline followed by spaces
485 |
486 | \[
487 | (.*?) // id = $3
488 | \]
489 | )
490 | ()()()() // pad remaining backreferences
491 | /g, writeAnchorTag);
492 | */
493 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag);
494 |
495 | //
496 | // Next, inline-style links: [link text](url "optional title")
497 | //
498 |
499 | /*
500 | text = text.replace(/
501 | ( // wrap whole match in $1
502 | \[
503 | (
504 | (?:
505 | \[[^\]]*\] // allow brackets nested one level
506 | |
507 | [^\[\]] // or anything else
508 | )*
509 | )
510 | \]
511 | \( // literal paren
512 | [ \t]*
513 | () // no id, so leave $3 empty
514 | ( // href = $4
515 | (?:
516 | \([^)]*\) // allow one level of (correctly nested) parens (think MSDN)
517 | |
518 | [^()]
519 | )*?
520 | )>?
521 | [ \t]*
522 | ( // $5
523 | (['"]) // quote char = $6
524 | (.*?) // Title = $7
525 | \6 // matching quote
526 | [ \t]* // ignore any spaces/tabs between closing quote and )
527 | )? // title is optional
528 | \)
529 | )
530 | /g, writeAnchorTag);
531 | */
532 |
533 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()((?:\([^)]*\)|[^()])*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag);
534 |
535 | //
536 | // Last, handle reference-style shortcuts: [link text]
537 | // These must come last in case you've also got [link test][1]
538 | // or [link test](/foo)
539 | //
540 |
541 | /*
542 | text = text.replace(/
543 | ( // wrap whole match in $1
544 | \[
545 | ([^\[\]]+) // link text = $2; can't contain '[' or ']'
546 | \]
547 | )
548 | ()()()()() // pad rest of backreferences
549 | /g, writeAnchorTag);
550 | */
551 | text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
552 |
553 | return text;
554 | }
555 |
556 | function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
557 | if (m7 == undefined) m7 = "";
558 | var whole_match = m1;
559 | var link_text = m2.replace(/:\/\//g, "~P"); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs
560 | var link_id = m3.toLowerCase();
561 | var url = m4;
562 | var title = m7;
563 |
564 | if (url == "") {
565 | if (link_id == "") {
566 | // lower-case and turn embedded newlines into spaces
567 | link_id = link_text.toLowerCase().replace(/ ?\n/g, " ");
568 | }
569 | url = "#" + link_id;
570 |
571 | if (g_urls.get(link_id) != undefined) {
572 | url = g_urls.get(link_id);
573 | if (g_titles.get(link_id) != undefined) {
574 | title = g_titles.get(link_id);
575 | }
576 | }
577 | else {
578 | if (whole_match.search(/\(\s*\)$/m) > -1) {
579 | // Special case for explicit empty url
580 | url = "";
581 | } else {
582 | return whole_match;
583 | }
584 | }
585 | }
586 | url = encodeProblemUrlChars(url);
587 | url = escapeCharacters(url, "*_");
588 | var result = "" + link_text + "";
597 |
598 | return result;
599 | }
600 |
601 | function _DoImages(text) {
602 | //
603 | // Turn Markdown image shortcuts into
tags.
604 | //
605 |
606 | //
607 | // First, handle reference-style labeled images: ![alt text][id]
608 | //
609 |
610 | /*
611 | text = text.replace(/
612 | ( // wrap whole match in $1
613 | !\[
614 | (.*?) // alt text = $2
615 | \]
616 |
617 | [ ]? // one optional space
618 | (?:\n[ ]*)? // one optional newline followed by spaces
619 |
620 | \[
621 | (.*?) // id = $3
622 | \]
623 | )
624 | ()()()() // pad rest of backreferences
625 | /g, writeImageTag);
626 | */
627 | text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag);
628 |
629 | //
630 | // Next, handle inline images: 
631 | // Don't forget: encode * and _
632 |
633 | /*
634 | text = text.replace(/
635 | ( // wrap whole match in $1
636 | !\[
637 | (.*?) // alt text = $2
638 | \]
639 | \s? // One optional whitespace character
640 | \( // literal paren
641 | [ \t]*
642 | () // no id, so leave $3 empty
643 | (\S+?)>? // src url = $4
644 | [ \t]*
645 | ( // $5
646 | (['"]) // quote char = $6
647 | (.*?) // title = $7
648 | \6 // matching quote
649 | [ \t]*
650 | )? // title is optional
651 | \)
652 | )
653 | /g, writeImageTag);
654 | */
655 | text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
656 |
657 | return text;
658 | }
659 |
660 | function attributeEncode(text) {
661 | // unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title)
662 | // never makes sense to have verbatim HTML in it (and the sanitizer would totally break it)
663 | return text.replace(/>/g, ">").replace(/";
707 |
708 | return result;
709 | }
710 |
711 | function _DoHeaders(text) {
712 |
713 | // Setext-style headers:
714 | // Header 1
715 | // ========
716 | //
717 | // Header 2
718 | // --------
719 | //
720 | text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
721 | function (wholeMatch, m1) { return "" + _RunSpanGamut(m1) + "
\n\n"; }
722 | );
723 |
724 | text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
725 | function (matchFound, m1) { return "" + _RunSpanGamut(m1) + "
\n\n"; }
726 | );
727 |
728 | // atx-style headers:
729 | // # Header 1
730 | // ## Header 2
731 | // ## Header 2 with closing hashes ##
732 | // ...
733 | // ###### Header 6
734 | //
735 |
736 | /*
737 | text = text.replace(/
738 | ^(\#{1,6}) // $1 = string of #'s
739 | [ \t]*
740 | (.+?) // $2 = Header text
741 | [ \t]*
742 | \#* // optional closing #'s (not counted)
743 | \n+
744 | /gm, function() {...});
745 | */
746 |
747 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
748 | function (wholeMatch, m1, m2) {
749 | var h_level = m1.length;
750 | return "" + _RunSpanGamut(m2) + " \n\n";
751 | }
752 | );
753 |
754 | return text;
755 | }
756 |
757 | function _DoLists(text) {
758 | //
759 | // Form HTML ordered (numbered) and unordered (bulleted) lists.
760 | //
761 |
762 | // attacklab: add sentinel to hack around khtml/safari bug:
763 | // http://bugs.webkit.org/show_bug.cgi?id=11231
764 | text += "~0";
765 |
766 | // Re-usable pattern to match any entirel ul or ol list:
767 |
768 | /*
769 | var whole_list = /
770 | ( // $1 = whole list
771 | ( // $2
772 | [ ]{0,3} // attacklab: g_tab_width - 1
773 | ([*+-]|\d+[.]) // $3 = first list item marker
774 | [ \t]+
775 | )
776 | [^\r]+?
777 | ( // $4
778 | ~0 // sentinel for workaround; should be $
779 | |
780 | \n{2,}
781 | (?=\S)
782 | (?! // Negative lookahead for another list item marker
783 | [ \t]*
784 | (?:[*+-]|\d+[.])[ \t]+
785 | )
786 | )
787 | )
788 | /g
789 | */
790 | var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
791 |
792 | if (g_list_level) {
793 | text = text.replace(whole_list, function (wholeMatch, m1, m2) {
794 | var list = m1;
795 | var list_type = (m2.search(/[*+-]/g) > -1) ? "ul" : "ol";
796 |
797 | var result = _ProcessListItems(list, list_type);
798 |
799 | // Trim any trailing whitespace, to put the closing `$list_type>`
800 | // up on the preceding line, to get it past the current stupid
801 | // HTML block parser. This is a hack to work around the terrible
802 | // hack that is the HTML block parser.
803 | result = result.replace(/\s+$/, "");
804 | result = "<" + list_type + ">" + result + "" + list_type + ">\n";
805 | return result;
806 | });
807 | } else {
808 | whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
809 | text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) {
810 | var runup = m1;
811 | var list = m2;
812 |
813 | var list_type = (m3.search(/[*+-]/g) > -1) ? "ul" : "ol";
814 | var result = _ProcessListItems(list, list_type);
815 | result = runup + "<" + list_type + ">\n" + result + "" + list_type + ">\n";
816 | return result;
817 | });
818 | }
819 |
820 | // attacklab: strip sentinel
821 | text = text.replace(/~0/, "");
822 |
823 | return text;
824 | }
825 |
826 | var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" };
827 |
828 | function _ProcessListItems(list_str, list_type) {
829 | //
830 | // Process the contents of a single ordered or unordered list, splitting it
831 | // into individual list items.
832 | //
833 | // list_type is either "ul" or "ol".
834 |
835 | // The $g_list_level global keeps track of when we're inside a list.
836 | // Each time we enter a list, we increment it; when we leave a list,
837 | // we decrement. If it's zero, we're not in a list anymore.
838 | //
839 | // We do this because when we're not inside a list, we want to treat
840 | // something like this:
841 | //
842 | // I recommend upgrading to version
843 | // 8. Oops, now this line is treated
844 | // as a sub-list.
845 | //
846 | // As a single paragraph, despite the fact that the second line starts
847 | // with a digit-period-space sequence.
848 | //
849 | // Whereas when we're inside a list (or sub-list), that line will be
850 | // treated as the start of a sub-list. What a kludge, huh? This is
851 | // an aspect of Markdown's syntax that's hard to parse perfectly
852 | // without resorting to mind-reading. Perhaps the solution is to
853 | // change the syntax rules such that sub-lists must start with a
854 | // starting cardinal number; e.g. "1." or "a.".
855 |
856 | g_list_level++;
857 |
858 | // trim trailing blank lines:
859 | list_str = list_str.replace(/\n{2,}$/, "\n");
860 |
861 | // attacklab: add sentinel to emulate \z
862 | list_str += "~0";
863 |
864 | // In the original attacklab showdown, list_type was not given to this function, and anything
865 | // that matched /[*+-]|\d+[.]/ would just create the next , causing this mismatch:
866 | //
867 | // Markdown rendered by WMD rendered by MarkdownSharp
868 | // ------------------------------------------------------------------
869 | // 1. first 1. first 1. first
870 | // 2. second 2. second 2. second
871 | // - third 3. third * third
872 | //
873 | // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx,
874 | // with {MARKER} being one of \d+[.] or [*+-], depending on list_type:
875 |
876 | /*
877 | list_str = list_str.replace(/
878 | (^[ \t]*) // leading whitespace = $1
879 | ({MARKER}) [ \t]+ // list marker = $2
880 | ([^\r]+? // list item text = $3
881 | (\n+)
882 | )
883 | (?=
884 | (~0 | \2 ({MARKER}) [ \t]+)
885 | )
886 | /gm, function(){...});
887 | */
888 |
889 | var marker = _listItemMarkers[list_type];
890 | var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm");
891 | var last_item_had_a_double_newline = false;
892 | list_str = list_str.replace(re,
893 | function (wholeMatch, m1, m2, m3) {
894 | var item = m3;
895 | var leading_space = m1;
896 | var ends_with_double_newline = /\n\n$/.test(item);
897 | var contains_double_newline = ends_with_double_newline || item.search(/\n{2,}/) > -1;
898 |
899 | if (contains_double_newline || last_item_had_a_double_newline) {
900 | item = _RunBlockGamut(_Outdent(item), /* doNotUnhash = */true);
901 | }
902 | else {
903 | // Recursion for sub-lists:
904 | item = _DoLists(_Outdent(item));
905 | item = item.replace(/\n$/, ""); // chomp(item)
906 | item = _RunSpanGamut(item);
907 | }
908 | last_item_had_a_double_newline = ends_with_double_newline;
909 | return " " + item + " \n";
910 | }
911 | );
912 |
913 | // attacklab: strip sentinel
914 | list_str = list_str.replace(/~0/g, "");
915 |
916 | g_list_level--;
917 | return list_str;
918 | }
919 |
920 | function _DoCodeBlocks(text) {
921 | //
922 | // Process Markdown `` blocks.
923 | //
924 |
925 | /*
926 | text = text.replace(/
927 | (?:\n\n|^)
928 | ( // $1 = the code block -- one or more lines, starting with a space/tab
929 | (?:
930 | (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
931 | .*\n+
932 | )+
933 | )
934 | (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
935 | /g ,function(){...});
936 | */
937 |
938 | // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
939 | text += "~0";
940 |
941 | text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
942 | function (wholeMatch, m1, m2) {
943 | var codeblock = m1;
944 | var nextChar = m2;
945 |
946 | codeblock = _EncodeCode(_Outdent(codeblock));
947 | codeblock = _Detab(codeblock);
948 | codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
949 | codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
950 |
951 | codeblock = '' + codeblock + '\n
';
952 |
953 | return "\n\n" + codeblock + "\n\n" + nextChar;
954 | }
955 | );
956 |
957 | // attacklab: strip sentinel
958 | text = text.replace(/~0/, "");
959 |
960 | return text;
961 | }
962 |
963 | function hashBlock(text) {
964 | text = text.replace(/(^\n+|\n+$)/g, "");
965 | return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n";
966 | }
967 |
968 | function _DoCodeSpans(text) {
969 | //
970 | // * Backtick quotes are used for spans.
971 | //
972 | // * You can use multiple backticks as the delimiters if you want to
973 | // include literal backticks in the code span. So, this input:
974 | //
975 | // Just type ``foo `bar` baz`` at the prompt.
976 | //
977 | // Will translate to:
978 | //
979 | // Just type foo `bar` baz at the prompt.
980 | //
981 | // There's no arbitrary limit to the number of backticks you
982 | // can use as delimters. If you need three consecutive backticks
983 | // in your code, use four for delimiters, etc.
984 | //
985 | // * You can use spaces to get literal backticks at the edges:
986 | //
987 | // ... type `` `bar` `` ...
988 | //
989 | // Turns to:
990 | //
991 | // ... type `bar` ...
992 | //
993 |
994 | /*
995 | text = text.replace(/
996 | (^|[^\\]) // Character before opening ` can't be a backslash
997 | (`+) // $2 = Opening run of `
998 | ( // $3 = The code block
999 | [^\r]*?
1000 | [^`] // attacklab: work around lack of lookbehind
1001 | )
1002 | \2 // Matching closer
1003 | (?!`)
1004 | /gm, function(){...});
1005 | */
1006 |
1007 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1008 | function (wholeMatch, m1, m2, m3, m4) {
1009 | var c = m3;
1010 | c = c.replace(/^([ \t]*)/g, ""); // leading whitespace
1011 | c = c.replace(/[ \t]*$/g, ""); // trailing whitespace
1012 | c = _EncodeCode(c);
1013 | c = c.replace(/:\/\//g, "~P"); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs.
1014 | return m1 + "" + c + "";
1015 | }
1016 | );
1017 |
1018 | return text;
1019 | }
1020 |
1021 | function _EncodeCode(text) {
1022 | //
1023 | // Encode/escape certain characters inside Markdown code runs.
1024 | // The point is that in code, these characters are literals,
1025 | // and lose their special Markdown meanings.
1026 | //
1027 | // Encode all ampersands; HTML entities are not
1028 | // entities within a Markdown code span.
1029 | text = text.replace(/&/g, "&");
1030 |
1031 | // Do the angle bracket song and dance:
1032 | text = text.replace(//g, ">");
1034 |
1035 | // Now, escape characters that are magic in Markdown:
1036 | text = escapeCharacters(text, "\*_{}[]\\", false);
1037 |
1038 | // jj the line above breaks this:
1039 | //---
1040 |
1041 | //* Item
1042 |
1043 | // 1. Subitem
1044 |
1045 | // special char: *
1046 | //---
1047 |
1048 | return text;
1049 | }
1050 |
1051 | function _DoItalicsAndBold(text) {
1052 |
1053 | // must go first:
1054 | text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,
1055 | "$1$3$4");
1056 |
1057 | text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,
1058 | "$1$3$4");
1059 |
1060 | return text;
1061 | }
1062 |
1063 | function _DoBlockQuotes(text) {
1064 |
1065 | /*
1066 | text = text.replace(/
1067 | ( // Wrap whole match in $1
1068 | (
1069 | ^[ \t]*>[ \t]? // '>' at the start of a line
1070 | .+\n // rest of the first line
1071 | (.+\n)* // subsequent consecutive lines
1072 | \n* // blanks
1073 | )+
1074 | )
1075 | /gm, function(){...});
1076 | */
1077 |
1078 | text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1079 | function (wholeMatch, m1) {
1080 | var bq = m1;
1081 |
1082 | // attacklab: hack around Konqueror 3.5.4 bug:
1083 | // "----------bug".replace(/^-/g,"") == "bug"
1084 |
1085 | bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting
1086 |
1087 | // attacklab: clean up hack
1088 | bq = bq.replace(/~0/g, "");
1089 |
1090 | bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines
1091 | bq = _RunBlockGamut(bq); // recurse
1092 |
1093 | bq = bq.replace(/(^|\n)/g, "$1 ");
1094 | // These leading spaces screw with content, so we need to fix that:
1095 | bq = bq.replace(
1096 | /(\s*[^\r]+?<\/pre>)/gm,
1097 | function (wholeMatch, m1) {
1098 | var pre = m1;
1099 | // attacklab: hack around Konqueror 3.5.4 bug:
1100 | pre = pre.replace(/^ /mg, "~0");
1101 | pre = pre.replace(/~0/g, "");
1102 | return pre;
1103 | });
1104 |
1105 | return hashBlock("\n" + bq + "\n
");
1106 | }
1107 | );
1108 | return text;
1109 | }
1110 |
1111 | function _FormParagraphs(text, doNotUnhash) {
1112 | //
1113 | // Params:
1114 | // $text - string to process with html tags
1115 | //
1116 |
1117 | // Strip leading and trailing lines:
1118 | text = text.replace(/^\n+/g, "");
1119 | text = text.replace(/\n+$/g, "");
1120 |
1121 | var grafs = text.split(/\n{2,}/g);
1122 | var grafsOut = [];
1123 |
1124 | var markerRe = /~K(\d+)K/;
1125 |
1126 | //
1127 | // Wrap
tags.
1128 | //
1129 | var end = grafs.length;
1130 | for (var i = 0; i < end; i++) {
1131 | var str = grafs[i];
1132 |
1133 | // if this is an HTML marker, copy it
1134 | if (markerRe.test(str)) {
1135 | grafsOut.push(str);
1136 | }
1137 | else if (/\S/.test(str)) {
1138 | str = _RunSpanGamut(str);
1139 | str = str.replace(/^([ \t]*)/g, "
");
1140 | str += "
"
1141 | grafsOut.push(str);
1142 | }
1143 |
1144 | }
1145 | //
1146 | // Unhashify HTML blocks
1147 | //
1148 | if (!doNotUnhash) {
1149 | end = grafsOut.length;
1150 | for (var i = 0; i < end; i++) {
1151 | var foundAny = true;
1152 | while (foundAny) { // we may need several runs, since the data may be nested
1153 | foundAny = false;
1154 | grafsOut[i] = grafsOut[i].replace(/~K(\d+)K/g, function (wholeMatch, id) {
1155 | foundAny = true;
1156 | return g_html_blocks[id];
1157 | });
1158 | }
1159 | }
1160 | }
1161 | return grafsOut.join("\n\n");
1162 | }
1163 |
1164 | function _EncodeAmpsAndAngles(text) {
1165 | // Smart processing for ampersands and angle brackets that need to be encoded.
1166 |
1167 | // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1168 | // http://bumppo.net/projects/amputator/
1169 | text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&");
1170 |
1171 | // Encode naked <'s
1172 | text = text.replace(/<(?![a-z\/?\$!])/gi, "<");
1173 |
1174 | return text;
1175 | }
1176 |
1177 | function _EncodeBackslashEscapes(text) {
1178 | //
1179 | // Parameter: String.
1180 | // Returns: The string, with after processing the following backslash
1181 | // escape sequences.
1182 | //
1183 |
1184 | // attacklab: The polite way to do this is with the new
1185 | // escapeCharacters() function:
1186 | //
1187 | // text = escapeCharacters(text,"\\",true);
1188 | // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1189 | //
1190 | // ...but we're sidestepping its use of the (slow) RegExp constructor
1191 | // as an optimization for Firefox. This function gets called a LOT.
1192 |
1193 | text = text.replace(/\\(\\)/g, escapeCharacters_callback);
1194 | text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback);
1195 | return text;
1196 | }
1197 |
1198 | function _DoAutoLinks(text) {
1199 |
1200 | // note that at this point, all other URL in the text are already hyperlinked as
1201 | // *except* for the case
1202 |
1203 | // automatically add < and > around unadorned raw hyperlinks
1204 | // must be preceded by space/BOF and followed by non-word/EOF character
1205 | text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4");
1206 |
1207 | // autolink anything like
1208 |
1209 | var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; }
1210 | text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer);
1211 |
1212 | // Email addresses:
1213 | /*
1214 | text = text.replace(/
1215 | <
1216 | (?:mailto:)?
1217 | (
1218 | [-.\w]+
1219 | \@
1220 | [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1221 | )
1222 | >
1223 | /gi, _DoAutoLinks_callback());
1224 | */
1225 |
1226 | var email_replacer = function(wholematch, m1) {
1227 | var mailto = 'mailto:'
1228 | var link
1229 | var email
1230 | if (m1.substring(0, mailto.length) != mailto){
1231 | link = mailto + m1;
1232 | email = m1;
1233 | } else {
1234 | link = m1;
1235 | email = m1.substring(mailto.length, m1.length);
1236 | }
1237 | return "" + pluginHooks.plainLinkText(email) + "";
1238 | }
1239 | text = text.replace(/<((?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+))>/gi, email_replacer);
1240 |
1241 | return text;
1242 | }
1243 |
1244 | function _UnescapeSpecialChars(text) {
1245 | //
1246 | // Swap back in all the special characters we've hidden.
1247 | //
1248 | text = text.replace(/~E(\d+)E/g,
1249 | function (wholeMatch, m1) {
1250 | var charCodeToReplace = parseInt(m1);
1251 | return String.fromCharCode(charCodeToReplace);
1252 | }
1253 | );
1254 | return text;
1255 | }
1256 |
1257 | function _Outdent(text) {
1258 | //
1259 | // Remove one level of line-leading tabs or spaces
1260 | //
1261 |
1262 | // attacklab: hack around Konqueror 3.5.4 bug:
1263 | // "----------bug".replace(/^-/g,"") == "bug"
1264 |
1265 | text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width
1266 |
1267 | // attacklab: clean up hack
1268 | text = text.replace(/~0/g, "")
1269 |
1270 | return text;
1271 | }
1272 |
1273 | function _Detab(text) {
1274 | if (!/\t/.test(text))
1275 | return text;
1276 |
1277 | var spaces = [" ", " ", " ", " "],
1278 | skew = 0,
1279 | v;
1280 |
1281 | return text.replace(/[\n\t]/g, function (match, offset) {
1282 | if (match === "\n") {
1283 | skew = offset + 1;
1284 | return match;
1285 | }
1286 | v = (offset - skew) % 4;
1287 | skew = offset + 1;
1288 | return spaces[v];
1289 | });
1290 | }
1291 |
1292 | //
1293 | // attacklab: Utility functions
1294 | //
1295 |
1296 | var _problemUrlChars = /(?:["'*()[\]:]|~D)/g;
1297 |
1298 | // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems
1299 | function encodeProblemUrlChars(url) {
1300 | if (!url)
1301 | return "";
1302 |
1303 | var len = url.length;
1304 |
1305 | return url.replace(_problemUrlChars, function (match, offset) {
1306 | if (match == "~D") // escape for dollar
1307 | return "%24";
1308 | if (match == ":") {
1309 | if (offset == len - 1 || /[0-9\/]/.test(url.charAt(offset + 1)))
1310 | return ":";
1311 | if (url.substring(0, 'mailto:'.length) === 'mailto:')
1312 | return ":";
1313 | if (url.substring(0, 'magnet:'.length) === 'magnet:')
1314 | return ":";
1315 | }
1316 | return "%" + match.charCodeAt(0).toString(16);
1317 | });
1318 | }
1319 |
1320 |
1321 | function escapeCharacters(text, charsToEscape, afterBackslash) {
1322 | // First we have to escape the escape characters so that
1323 | // we can build a character class out of them
1324 | var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])";
1325 |
1326 | if (afterBackslash) {
1327 | regexString = "\\\\" + regexString;
1328 | }
1329 |
1330 | var regex = new RegExp(regexString, "g");
1331 | text = text.replace(regex, escapeCharacters_callback);
1332 |
1333 | return text;
1334 | }
1335 |
1336 |
1337 | function escapeCharacters_callback(wholeMatch, m1) {
1338 | var charCodeToEscape = m1.charCodeAt(0);
1339 | return "~E" + charCodeToEscape + "E";
1340 | }
1341 |
1342 | }; // end of the Markdown.Converter constructor
1343 |
1344 | })();
1345 |
--------------------------------------------------------------------------------