]> git.openstreetmap.org Git - osqa.git/commitdiff
OSQA-720, changing the WMD markdown editor, applying patches that resolve issues...
authorjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Wed, 13 Jul 2011 16:00:24 +0000 (16:00 +0000)
committerjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Wed, 13 Jul 2011 16:00:24 +0000 (16:00 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@1118 0cfe37f9-358a-4d5e-be75-b63607b5c754

forum/skins/default/media/js/osqa.main.js
forum/skins/default/media/js/wmd/showdown-min.js [deleted file]
forum/skins/default/media/js/wmd/showdown.js
forum/skins/default/media/js/wmd/wmd-min.js [deleted file]
forum/skins/default/media/js/wmd/wmd.css
forum/skins/default/media/js/wmd/wmd.js
forum/views/writers.py

index 18bd941ab36f9a9a59b5053311487973a11407cd..8f785e30a294593747f900bbf79de0c3c5736413 100644 (file)
@@ -19,10 +19,12 @@ $('html').ajaxSend(function(event, xhr, settings) {
         }\r
         return cookieValue;\r
     }\r
-    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {\r
-        // Only send the token to relative URLs i.e. locally.\r
-        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));\r
-    }\r
+    try {\r
+        if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {\r
+            // Only send the token to relative URLs i.e. locally.\r
+            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));\r
+        }\r
+    } catch (e) {}\r
 });\r
 \r
 var response_commands = {\r
diff --git a/forum/skins/default/media/js/wmd/showdown-min.js b/forum/skins/default/media/js/wmd/showdown-min.js
deleted file mode 100644 (file)
index 073613b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-var Attacklab=Attacklab||{};Attacklab.showdown=Attacklab.showdown||{};Attacklab.showdown.converter=function(){var a;var j;var A;var i=0;this.makeHtml=function(H){a=new Array();j=new Array();A=new Array();H=H.replace(/~/g,"~T");H=H.replace(/\$/g,"~D");H=H.replace(/\r\n/g,"\n");H=H.replace(/\r/g,"\n");H="\n\n"+H+"\n\n";H=z(H);H=H.replace(/^[ \t]+$/mg,"");H=m(H);H=d(H);H=G(H);H=q(H);H=H.replace(/~D/g,"$$");H=H.replace(/~T/g,"~");return H};var d=function(H){var H=H.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,function(K,M,L,J,I){M=M.toLowerCase();a[M]=h(L);if(J){return J+I}else{if(I){j[M]=I.replace(/"/g,"&quot;")}}return""});return H};var m=function(J){J=J.replace(/\n/g,"\n\n");var I="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";var H="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";J=J.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,x);J=J.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,x);J=J.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,x);J=J.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,x);J=J.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,x);J=J.replace(/\n\n/g,"\n");return J};var x=function(H,I){var J=I;J=J.replace(/\n\n/g,"\n");J=J.replace(/^\n/,"");J=J.replace(/\n+$/g,"");J="\n\n~K"+(A.push(J)-1)+"K\n\n";return J};var G=function(I){I=f(I);var H=o("<hr />");I=I.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,H);I=I.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm,H);I=I.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm,H);I=E(I);I=b(I);I=u(I);I=m(I);I=g(I);return I};var r=function(H){H=C(H);H=l(H);H=e(H);H=F(H);H=y(H);H=n(H);H=h(H);H=c(H);H=H.replace(/  +\n/g," <br />\n");return H};var l=function(I){var H=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;I=I.replace(H,function(K){var J=K.replace(/(.)<\/?code>(?=.)/g,"$1`");J=w(J,"\\`*_");return J});return I};var y=function(H){H=H.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,D);H=H.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,D);H=H.replace(/(\[([^\[\]]+)\])()()()()()/g,D);return H};var D=function(N,T,S,R,Q,P,M,L){if(L==undefined){L=""}var K=T;var I=S;var J=R.toLowerCase();var H=Q;var O=L;if(H==""){if(J==""){J=I.toLowerCase().replace(/ ?\n/g," ")}H="#"+J;if(a[J]!=undefined){H=a[J];if(j[J]!=undefined){O=j[J]}}else{if(K.search(/\(\s*\)$/m)>-1){H=""}else{return K}}}H=w(H,"*_");var U='<a href="'+H+'"';if(O!=""){O=O.replace(/"/g,"&quot;");O=w(O,"*_");U+=' title="'+O+'"'}U+=">"+I+"</a>";return U};var F=function(H){H=H.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,t);H=H.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,t);return H};var t=function(N,T,S,R,Q,P,M,L){var K=T;var J=S;var I=R.toLowerCase();var H=Q;var O=L;if(!O){O=""}if(H==""){if(I==""){I=J.toLowerCase().replace(/ ?\n/g," ")}H="#"+I;if(a[I]!=undefined){H=a[I];if(j[I]!=undefined){O=j[I]}}else{return K}}J=J.replace(/"/g,"&quot;");H=w(H,"*_");var U='<img src="'+H+'" alt="'+J+'"';O=O.replace(/"/g,"&quot;");O=w(O,"*_");U+=' title="'+O+'"';U+=" />";return U};var f=function(H){H=H.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(I,J){return o("<h1>"+r(J)+"</h1>")});H=H.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(J,I){return o("<h2>"+r(I)+"</h2>")});H=H.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(I,L,K){var J=L.length;return o("<h"+J+">"+r(K)+"</h"+J+">")});return H};var p;var E=function(I){I+="~0";var H=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;if(i){I=I.replace(H,function(K,N,M){var O=N;var L=(M.search(/[*+-]/g)>-1)?"ul":"ol";O=O.replace(/\n{2,}/g,"\n\n\n");var J=p(O);J=J.replace(/\s+$/,"");J="<"+L+">"+J+"</"+L+">\n";return J})}else{H=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;I=I.replace(H,function(L,P,N,K){var O=P;var Q=N;var M=(K.search(/[*+-]/g)>-1)?"ul":"ol";var Q=Q.replace(/\n{2,}/g,"\n\n\n");var J=p(Q);J=O+"<"+M+">\n"+J+"</"+M+">\n";return J})}I=I.replace(/~0/,"");return I};p=function(H){i++;H=H.replace(/\n{2,}$/,"\n");H+="~0";H=H.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(K,M,L,J,I){var O=I;var N=M;var P=L;if(N||(O.search(/\n{2,}/)>-1)){O=G(s(O))}else{O=E(s(O));O=O.replace(/\n$/,"");O=r(O)}return"<li>"+O+"</li>\n"});H=H.replace(/~0/g,"");i--;return H};var b=function(H){H+="~0";H=H.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(I,K,J){var L=K;var M=J;L=v(s(L));L=z(L);L=L.replace(/^\n+/g,"");L=L.replace(/\n+$/g,"");L="<pre><code>"+L+"\n</code></pre>";return o(L)+M});H=H.replace(/~0/,"");return H};var o=function(H){H=H.replace(/(^\n+|\n+$)/g,"");return"\n\n~K"+(A.push(H)-1)+"K\n\n"};var C=function(H){H=H.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(K,M,L,J,I){var N=J;N=N.replace(/^([ \t]*)/g,"");N=N.replace(/[ \t]*$/g,"");N=v(N);return M+"<code>"+N+"</code>"});return H};var v=function(H){H=H.replace(/&/g,"&amp;");H=H.replace(/</g,"&lt;");H=H.replace(/>/g,"&gt;");H=w(H,"*_{}[]\\",false);return H};var c=function(H){H=H.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,"<strong>$2</strong>");H=H.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"<em>$2</em>");return H};var u=function(H){H=H.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(I,J){var K=J;K=K.replace(/^[ \t]*>[ \t]?/gm,"~0");K=K.replace(/~0/g,"");K=K.replace(/^[ \t]+$/gm,"");K=G(K);K=K.replace(/(^|\n)/g,"$1  ");K=K.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(L,M){var N=M;N=N.replace(/^  /mg,"~0");N=N.replace(/~0/g,"");return N});return o("<blockquote>\n"+K+"\n</blockquote>")});return H};var g=function(N){N=N.replace(/^\n+/g,"");N=N.replace(/\n+$/g,"");var M=N.split(/\n{2,}/g);var J=new Array();var H=M.length;for(var I=0;I<H;I++){var L=M[I];if(L.search(/~K(\d+)K/g)>=0){J.push(L)}else{if(L.search(/\S/)>=0){L=r(L);L=L.replace(/^([ \t]*)/g,"<p>");L+="</p>";J.push(L)}}}H=J.length;for(var I=0;I<H;I++){while(J[I].search(/~K(\d+)K/)>=0){var K=A[RegExp.$1];K=K.replace(/\$/g,"$$$$");J[I]=J[I].replace(/~K\d+K/,K)}}return J.join("\n\n")};var h=function(H){H=H.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");H=H.replace(/<(?![a-z\/?\$!])/gi,"&lt;");return H};var e=function(H){H=H.replace(/\\(\\)/g,k);H=H.replace(/\\([`*_{}\[\]()>#+-.!])/g,k);return H};var n=function(H){H=H.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,'<a href="$1">$1</a>');H=H.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(I,J){return B(q(J))});return H};var B=function(J){function I(L){var K="0123456789ABCDEF";var M=L.charCodeAt(0);return(K.charAt(M>>4)+K.charAt(M&15))}var H=[function(K){return"&#"+K.charCodeAt(0)+";"},function(K){return"&#x"+I(K)+";"},function(K){return K}];J="mailto:"+J;J=J.replace(/./g,function(K){if(K=="@"){K=H[Math.floor(Math.random()*2)](K)}else{if(K!=":"){var L=Math.random();K=(L>0.9?H[2](K):L>0.45?H[1](K):H[0](K))}}return K});J='<a href="'+J+'">'+J+"</a>";J=J.replace(/">.+:/g,'">');return J};var q=function(H){H=H.replace(/~E(\d+)E/g,function(I,K){var J=parseInt(K);return String.fromCharCode(J)});return H};var s=function(H){H=H.replace(/^(\t|[ ]{1,4})/gm,"~0");H=H.replace(/~0/g,"");return H};var z=function(H){H=H.replace(/\t(?=\t)/g,"    ");H=H.replace(/\t/g,"~A~B");H=H.replace(/~B(.+?)~A/g,function(I,L,K){var N=L;var J=4-N.length%4;for(var M=0;M<J;M++){N+=" "}return N});H=H.replace(/~A/g,"    ");H=H.replace(/~B/g,"");return H};var w=function(L,I,J){var H="(["+I.replace(/([\[\]\\])/g,"\\$1")+"])";if(J){H="\\\\"+H}var K=new RegExp(H,"g");L=L.replace(K,k);return L};var k=function(H,J){var I=J.charCodeAt(0);return"~E"+I+"E"}};var Showdown=Attacklab.showdown;if(Attacklab.fileLoaded){Attacklab.fileLoaded("showdown.js")};
\ No newline at end of file
index b890fa8d3735803dcba6e55cc5cc167bb8bbf082..73a12f2d89a7ea490dc525e988aca0b6a1dcb626 100644 (file)
@@ -76,6 +76,23 @@ Attacklab.showdown = Attacklab.showdown || {}
 //
 Attacklab.showdown.converter = function() {
 
+
+// g_urls and g_titles allow arbitrary user-entered strings as keys. This
+// caused an exception (and hence stopped the rendering) when the user entered
+// e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this
+// (since no builtin property starts with "s_"). See
+// http://meta.stackoverflow.com/questions/64655/strange-wmd-bug
+// (granted, switching from Array() to Object() alone would have left only __proto__
+// to be a problem)
+var SaveHash = function () {
+    this.set = function (key, value) {
+        this["s_" + key] = value;
+    }
+    this.get = function (key) {
+        return this["s_" + key];
+    }
+}
+
 //
 // Globals:
 //
@@ -102,8 +119,8 @@ this.makeHtml = function(text) {
        // from other articles when generating a page which contains more than
        // one article (e.g. an index page that shows the N most recent
        // articles):
-       g_urls = new Array();
-       g_titles = new Array();
+    g_urls = new SaveHash();
+    g_titles = new SaveHash();
        g_html_blocks = new Array();
 
        // attacklab: Replace ~ with ~T
@@ -167,13 +184,15 @@ var _StripLinkDefinitions = function(text) {
                                  \n?                           // maybe *one* newline
                                  [ \t]*
                                <?(\S+?)>?                      // url = $2
+                (?=\s|$)            // lookahead for whitespace instead of the lookbehind removed below
                                  [ \t]*
                                  \n?                           // maybe one newline
                                  [ \t]*
-                               (?:
-                                 (\n*)                         // any lines skipped = $3 attacklab: lookbehind removed
+                               (                   // (potential) title = $3
+                                 (\n*)                         // any lines skipped = $4 attacklab: lookbehind removed
+                  [ \t]+
                                  ["(]
-                                 (.+?)                         // title = $4
+                                 (.+?)                         // title = $5
                                  [")]
                                  [ \t]*
                                )?                                      // title is optional
@@ -181,18 +200,18 @@ var _StripLinkDefinitions = function(text) {
                          /gm,
                          function(){...});
        */
-       var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm,
-               function (wholeMatch,m1,m2,m3,m4) {
+       var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm,
+               function (wholeMatch,m1,m2,m3,m4,m5) {
                        m1 = m1.toLowerCase();
-                       g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive
-                       if (m3) {
+                       g_urls.set(m1, _EncodeAmpsAndAngles(m2));  // Link IDs are case-insensitive
+                       if (m4) {
                                // Oops, found blank lines, so it's not a title.
                                // Put back the parenthetical statement we stole.
-                               return m3+m4;
-                       } else if (m4) {
-                               g_titles[m1] = m4.replace(/"/g,"&quot;");
+                               return m3;
+                       } else if (m5) {
+                               g_titles.set(m1, m5.replace(/"/g,"&quot;"));
                        }
-
+                       
                        // Completely remove the definition from the text
                        return "";
                }
@@ -202,8 +221,6 @@ var _StripLinkDefinitions = function(text) {
 }
 
 var _HashHTMLBlocks = function(text) {
-       // attacklab: Double up blank lines to reduce lookaround
-       text = text.replace(/\n/g,"\n\n");
 
        // Hashify HTML blocks:
        // We only want to do this for block-level HTML tags, such as headers,
@@ -264,40 +281,40 @@ var _HashHTMLBlocks = function(text) {
        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);
 
        // Special case just for <hr />. It was easier to make a special case than
-       // to make the other regex more complicated.
+       // to make the other regex more complicated.  
 
        /*
                text = text.replace(/
+               \n                                  // Starting after a blank line
+               [ ]{0,3}
                (                                               // save in $1
-                       \n\n                            // Starting after a blank line
-                       [ ]{0,3}
                        (<(hr)                          // start tag = $2
                        \b                                      // word break
-                       ([^<>])*?                       //
+                       ([^<>])*?                       // 
                        \/?>)                           // the matching end tag
                        [ \t]*
                        (?=\n{2,})                      // followed by a blank line
                )
                /g,hashElement);
        */
-       text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
+       text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
 
        // Special case for standalone HTML comments:
 
        /*
                text = text.replace(/
+               \n\n                            // Starting after a blank line
+               [ ]{0,3}                        // attacklab: g_tab_width - 1
                (                                               // save in $1
-                       \n\n                            // Starting after a blank line
-                       [ ]{0,3}                        // attacklab: g_tab_width - 1
                        <!
-                       (--[^\r]*?--\s*)+
+                       (--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)    // see http://www.w3.org/TR/html-markup/syntax.html#comments
                        >
                        [ \t]*
                        (?=\n{2,})                      // followed by a blank line
                )
                /g,hashElement);
        */
-       text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
+       text = text.replace(/\n\n[ ]{0,3}(<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashElement);
 
        // PHP and ASP-style processor instructions (<?...?> and <%...%>)
 
@@ -320,8 +337,6 @@ var _HashHTMLBlocks = function(text) {
        */
        text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
 
-       // attacklab: Undo double lines (see comment at top of this function)
-       text = text.replace(/\n\n/g,"\n");
        return text;
 }
 
@@ -329,19 +344,18 @@ var hashElement = function(wholeMatch,m1) {
        var blockText = m1;
 
        // Undo double lines
-       blockText = blockText.replace(/\n\n/g,"\n");
-       blockText = blockText.replace(/^\n/,"");
-
+       blockText = blockText.replace(/^\n+/,"");
+       
        // strip trailing blank lines
        blockText = blockText.replace(/\n+$/g,"");
-
+       
        // Replace the element text with a marker ("~KxK" where x is its key)
        blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
-
+       
        return blockText;
 };
 
-var _RunBlockGamut = function(text) {
+var _RunBlockGamut = function(text, doNotUnhash) {
 //
 // These are all the transformations that form block-level
 // tags like paragraphs, headers, and list items.
@@ -363,7 +377,7 @@ var _RunBlockGamut = function(text) {
        // we're escaping the markup we've just created, so that we don't wrap
        // <p> tags around block-level tags.
        text = _HashHTMLBlocks(text);
-       text = _FormParagraphs(text);
+    text = _FormParagraphs(text, doNotUnhash);
 
        return text;
 }
@@ -403,9 +417,12 @@ var _EscapeSpecialCharsWithinTagAttributes = function(text) {
 // don't conflict with their use in Markdown for code, italics and strong.
 //
 
-       // Build a regex to find HTML tags and comments.  See Friedl's
-       // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
-       var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
+       // Build a regex to find HTML tags and comments.  See Friedl's 
+    // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
+    
+    // SE: changed the comment part of the regex
+
+    var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi;
 
        text = text.replace(regex, function(wholeMatch) {
                var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
@@ -454,20 +471,26 @@ var _DoAnchors = function(text) {
 
        /*
                text = text.replace(/
-                       (                                               // wrap whole match in $1
-                               \[
+               (                                               // wrap whole match in $1
+                       \[
                                (
                                        (?:
                                                \[[^\]]*\]      // allow brackets nested one level
-                                       |
-                                       [^\[\]]                 // or anything else
-                               )
-                       )
+                                           |
+                                           [^\[\]]             // or anything else
+                                   )*
+                           )
                        \]
                        \(                                              // literal paren
                        [ \t]*
                        ()                                              // no id, so leave $3 empty
-                       <?(.*?)>?                               // href = $4
+                       <?(                     // href = $4
+                (?:
+                    \([^)]*\)       // allow one level of (correctly nested) parens (think MSDN)
+                    |
+                    [^()]
+                )*?
+            )>?                                
                        [ \t]*
                        (                                               // $5
                                (['"])                          // quote char = $6
@@ -479,7 +502,8 @@ var _DoAnchors = function(text) {
                )
                /g,writeAnchorTag);
        */
-       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
+    
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?((?:\([^)]*\)|[^()])*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
 
        //
        // Last, handle reference-style shortcuts: [link text]
@@ -498,11 +522,6 @@ var _DoAnchors = function(text) {
        */
        text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
 
-    // Prevent executing JavaScript from the Anchor href.
-    text = text.replace(/(<a.*href=[\"|\']javascript\:([^"]+)[\"|\'].*>([^<]+)<\/a>)/g, function() {
-        return arguments[3];
-    });
-
        return text;
 }
 
@@ -513,18 +532,18 @@ var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
        var link_id      = m3.toLowerCase();
        var url         = m4;
        var title       = m7;
-
+       
        if (url == "") {
                if (link_id == "") {
                        // lower-case and turn embedded newlines into spaces
                        link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
                }
                url = "#"+link_id;
-
-               if (g_urls[link_id] != undefined) {
-                       url = g_urls[link_id];
-                       if (g_titles[link_id] != undefined) {
-                               title = g_titles[link_id];
+               
+               if (g_urls.get(link_id) != undefined) {
+                       url = g_urls.get(link_id);
+                       if (g_titles.get(link_id) != undefined) {
+                               title = g_titles.get(link_id);
                        }
                }
                else {
@@ -535,19 +554,19 @@ var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
                                return whole_match;
                        }
                }
-       }
-
+       }       
+       
        url = escapeCharacters(url,"*_");
        var result = "<a href=\"" + url + "\"";
-
+       
        if (title != "") {
                title = title.replace(/"/g,"&quot;");
                title = escapeCharacters(title,"*_");
                result +=  " title=\"" + title + "\"";
        }
-
+       
        result += ">" + link_text + "</a>";
-
+       
        return result;
 }
 
@@ -618,30 +637,27 @@ var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
        var title       = m7;
 
        if (!title) title = "";
-
+       
        if (url == "") {
                if (link_id == "") {
                        // lower-case and turn embedded newlines into spaces
                        link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
                }
                url = "#"+link_id;
-
-               if (g_urls[link_id] != undefined) {
-                       url = g_urls[link_id];
-                       if (g_titles[link_id] != undefined) {
-                               title = g_titles[link_id];
+               
+               if (g_urls.get(link_id) != undefined) {
+                       url = g_urls.get(link_id);
+                       if (g_titles.get(link_id) != undefined) {
+                               title = g_titles.get(link_id);
                        }
                }
                else {
                        return whole_match;
                }
-       }
-
+       }       
+       
        alt_text = alt_text.replace(/"/g,"&quot;");
        url = escapeCharacters(url,"*_");
-    if (url.toString().indexOf('http://') != 0 && url.toString().indexOf('https://') != 0) {
-        url = scriptUrl + url
-    }
        var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
 
        // attacklab: Markdown.pl adds empty title attributes to images.
@@ -652,9 +668,9 @@ var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
                title = escapeCharacters(title,"*_");
                result +=  " title=\"" + title + "\"";
        //}
-
+       
        result += " />";
-
+       
        return result;
 }
 
@@ -664,15 +680,15 @@ var _DoHeaders = function(text) {
        // Setext-style headers:
        //      Header 1
        //      ========
-       //
+       //  
        //      Header 2
        //      --------
        //
        text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
-               function(wholeMatch,m1){return hashBlock("<h1>" + _RunSpanGamut(m1) + "</h1>");});
+               function(wholeMatch,m1){return "<h1>" + _RunSpanGamut(m1) + "</h1>\n\n";});
 
        text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
-               function(matchFound,m1){return hashBlock("<h2>" + _RunSpanGamut(m1) + "</h2>");});
+               function(matchFound,m1){return "<h2>" + _RunSpanGamut(m1) + "</h2>\n\n";});
 
        // atx-style headers:
        //  # Header 1
@@ -696,7 +712,7 @@ var _DoHeaders = function(text) {
        text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
                function(wholeMatch,m1,m2) {
                        var h_level = m1.length;
-                       return hashBlock("<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">");
+                       return "<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">\n\n";
                });
 
        return text;
@@ -744,11 +760,8 @@ var _DoLists = function(text) {
                        var list = m1;
                        var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
 
-                       // Turn double returns into triple returns, so that we can make a
-                       // paragraph for the last item in a list, if necessary:
-                       list = list.replace(/\n{2,}/g,"\n\n\n");;
-                       var result = _ProcessListItems(list);
-
+                       var result = _ProcessListItems(list, list_type);
+       
                        // Trim any trailing whitespace, to put the closing `</$list_type>`
                        // up on the preceding line, to get it past the current stupid
                        // HTML block parser. This is a hack to work around the terrible
@@ -764,11 +777,8 @@ var _DoLists = function(text) {
                        var list = m2;
 
                        var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
-                       // Turn double returns into triple returns, so that we can make a
-                       // paragraph for the last item in a list, if necessary:
-                       var list = list.replace(/\n{2,}/g,"\n\n\n");;
-                       var result = _ProcessListItems(list);
-                       result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
+                       var result = _ProcessListItems(list, list_type);
+                       result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";   
                        return result;
                });
        }
@@ -779,11 +789,15 @@ var _DoLists = function(text) {
        return text;
 }
 
-_ProcessListItems = function(list_str) {
+var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" };
+
+_ProcessListItems = function(list_str, list_type) {
 //
 //  Process the contents of a single ordered or unordered list, splitting it
 //  into individual list items.
 //
+//  list_type is either "ul" or "ol".
+
        // The $g_list_level global keeps track of when we're inside a list.
        // Each time we enter a list, we increment it; when we leave a list,
        // we decrement. If it's zero, we're not in a list anymore.
@@ -813,32 +827,47 @@ _ProcessListItems = function(list_str) {
        // attacklab: add sentinel to emulate \z
        list_str += "~0";
 
+       // In the original attacklab WMD, list_type was not given to this function, and anything
+       // that matched /[*+-]|\d+[.]/ would just create the next <li>, causing this mismatch:
+       //
+    //  Markdown          rendered by WMD        rendered by MarkdownSharp
+       //  ------------------------------------------------------------------
+       //  1. first          1. first               1. first
+       //  2. second         2. second              2. second
+       //  - third           3. third                   * third
+       //
+       // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx,
+    // with {MARKER} being one of \d+[.] or [*+-], depending on list_type:
        /*
                list_str = list_str.replace(/
-                       (\n)?                                                   // leading line = $1
-                       (^[ \t]*)                                               // leading whitespace = $2
-                       ([*+-]|\d+[.]) [ \t]+                   // list marker = $3
-                       ([^\r]+?                                                // list item text   = $4
-                       (\n{1,2}))
-                       (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
+                       (^[ \t]*)                                               // leading whitespace = $1
+                       ({MARKER}) [ \t]+                       // list marker = $2
+                       ([^\r]+?                                                // list item text   = $3
+                       (\n+))
+                       (?= (~0 | \2 ({MARKER}) [ \t]+))
                /gm, function(){...});
        */
-       list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
-               function(wholeMatch,m1,m2,m3,m4){
-                       var item = m4;
-                       var leading_line = m1;
-                       var leading_space = m2;
-
-                       if (leading_line || (item.search(/\n{2,}/)>-1)) {
-                               item = _RunBlockGamut(_Outdent(item));
+    
+    var marker = _listItemMarkers[list_type];
+    var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm");
+    var last_item_had_a_double_newline = false;
+       list_str = list_str.replace(re,
+               function(wholeMatch,m1,m2,m3){
+                       var item = m3;
+                       var leading_space = m1;
+            var ends_with_double_newline = /\n\n$/.test(item);
+                       var contains_double_newline = ends_with_double_newline || item.search(/\n{2,}/)>-1;
+
+                       if (contains_double_newline || last_item_had_a_double_newline) {
+                               item =  _RunBlockGamut(_Outdent(item), /* doNotUnhash = */ true);
                        }
                        else {
                                // Recursion for sub-lists:
                                item = _DoLists(_Outdent(item));
                                item = item.replace(/\n$/,""); // chomp(item)
                                item = _RunSpanGamut(item);
-                       }
-
+            }
+            last_item_had_a_double_newline = ends_with_double_newline;
                        return  "<li>" + item + "</li>\n";
                }
        );
@@ -854,7 +883,7 @@ _ProcessListItems = function(list_str) {
 var _DoCodeBlocks = function(text) {
 //
 //  Process Markdown `<pre><code>` blocks.
-//
+//  
 
        /*
                text = text.replace(text,
@@ -871,12 +900,12 @@ var _DoCodeBlocks = function(text) {
 
        // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
        text += "~0";
-
+       
        text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
                function(wholeMatch,m1,m2) {
                        var codeblock = m1;
                        var nextChar = m2;
-
+               
                        codeblock = _EncodeCode( _Outdent(codeblock));
                        codeblock = _Detab(codeblock);
                        codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
@@ -884,7 +913,7 @@ var _DoCodeBlocks = function(text) {
 
                        codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
 
-                       return hashBlock(codeblock) + nextChar;
+                       return "\n\n" + codeblock + "\n\n" + nextChar;
                }
        );
 
@@ -903,26 +932,26 @@ var hashBlock = function(text) {
 var _DoCodeSpans = function(text) {
 //
 //   *  Backtick quotes are used for <code></code> spans.
-//
+// 
 //   *  You can use multiple backticks as the delimiters if you want to
 //      include literal backticks in the code span. So, this input:
-//
+//      
 //              Just type ``foo `bar` baz`` at the prompt.
-//
+//      
 //        Will translate to:
-//
+//      
 //              <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
-//
+//      
 //     There's no arbitrary limit to the number of backticks you
 //     can use as delimters. If you need three consecutive backticks
 //     in your code, use four for delimiters, etc.
 //
 //  *  You can use spaces to get literal backticks at the edges:
-//
+//      
 //              ... type `` `bar` `` ...
-//
+//      
 //        Turns to:
-//
+//      
 //              ... type <code>`bar`</code> ...
 //
 
@@ -1025,7 +1054,7 @@ var _DoBlockQuotes = function(text) {
 
                        bq = bq.replace(/^[ \t]+$/gm,"");               // trim whitespace-only lines
                        bq = _RunBlockGamut(bq);                                // recurse
-
+                       
                        bq = bq.replace(/(^|\n)/g,"$1  ");
                        // These leading spaces screw with <pre> content, so we need to fix that:
                        bq = bq.replace(
@@ -1037,14 +1066,14 @@ var _DoBlockQuotes = function(text) {
                                        pre = pre.replace(/~0/g,"");
                                        return pre;
                                });
-
+                       
                        return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
                });
        return text;
 }
 
 
-var _FormParagraphs = function(text) {
+var _FormParagraphs = function(text, doNotUnhash) {
 //
 //  Params:
 //    $text - string to process with html <p> tags
@@ -1076,34 +1105,34 @@ var _FormParagraphs = function(text) {
                }
 
        }
-
        //
        // Unhashify HTML blocks
        //
-       end = grafsOut.length;
-       for (var i=0; i<end; i++) {
-               // if this is a marker for an html block...
-               while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
-                       var blockText = g_html_blocks[RegExp.$1];
-                       blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
-                       grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
-               }
-       }
-
+    if (!doNotUnhash) {
+        end = grafsOut.length;
+           for (var i=0; i<end; i++) {
+                   // if this is a marker for an html block...
+                   while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
+                           var blockText = g_html_blocks[RegExp.$1];
+                           blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
+                           grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
+                   }
+           }
+    }
        return grafsOut.join("\n\n");
 }
 
 
 var _EncodeAmpsAndAngles = function(text) {
 // Smart processing for ampersands and angle brackets that need to be encoded.
-
+       
        // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
        //   http://bumppo.net/projects/amputator/
        text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
-
+       
        // Encode naked <'s
        text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
-
+       
        return text;
 }
 
@@ -1243,38 +1272,25 @@ var _Outdent = function(text) {
        return text;
 }
 
-var _Detab = function(text) {
-// attacklab: Detab's completely rewritten for speed.
-// In perl we could fix it by anchoring the regexp with \G.
-// In javascript we're less fortunate.
-
-       // expand first n-1 tabs
-       text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width
+var _Detab = function (text) {
+       if (!/\t/.test(text))
+               return text;
 
-       // replace the nth with two sentinels
-       text = text.replace(/\t/g,"~A~B");
+       var spaces = ["    ", "   ", "  ", " "],
+               skew = 0,
+               v;
 
-       // use the sentinel to anchor our regex so it doesn't explode
-       text = text.replace(/~B(.+?)~A/g,
-               function(wholeMatch,m1,m2) {
-                       var leadingText = m1;
-                       var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width
-
-                       // there *must* be a better way to do this:
-                       for (var i=0; i<numSpaces; i++) leadingText+=" ";
-
-                       return leadingText;
+       return text.replace(/[\n\t]/g, function (match, offset) {
+               if (match === "\n") {
+                       skew = offset + 1;
+                       return match;
                }
-       );
-
-       // clean up sentinels
-       text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width
-       text = text.replace(/~B/g,"");
-
-       return text;
+               v = (offset - skew) % 4;
+               skew = offset + 1;
+               return spaces[v];
+       });
 }
 
-
 //
 //  attacklab: Utility functions
 //
diff --git a/forum/skins/default/media/js/wmd/wmd-min.js b/forum/skins/default/media/js/wmd/wmd-min.js
deleted file mode 100644 (file)
index aa643f1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-var Attacklab=Attacklab||{};Attacklab.wmdBase=function(){var y=top.Attacklab;var E=top.document;var s=top.RegExp;var l=top.navigator;y.Util={};y.Position={};y.Command={};y.Global={};var a=y.Util;var C=y.Position;var h=y.Command;var v=y.Global;v.isIE=/msie/.test(l.userAgent.toLowerCase());v.isIE_5or6=/msie 6/.test(l.userAgent.toLowerCase())||/msie 5/.test(l.userAgent.toLowerCase());v.isIE_7plus=v.isIE&&!v.isIE_5or6;v.isOpera=/opera/.test(l.userAgent.toLowerCase());v.isKonqueror=/konqueror/.test(l.userAgent.toLowerCase());var c="粗体 <strong> Ctrl-B";var f="斜体 <em> Ctrl-I";var z="超链接 <a> Ctrl-L";var u="引用 <blockquote> Ctrl-.";var e="代码 <pre><code> Ctrl-K";var d="图片 <img> Ctrl-G";var q="数字编号列表 <ol> Ctrl-O";var t="项目符号列表 <ul> Ctrl-U";var i="标题 <h1>/<h2> Ctrl-H";var p="水平线 <hr> Ctrl-R";var m="撤销 Ctrl-Z";var j="重做 Ctrl-Y";var B="<p style='margin-top: 0px'><b>输入图片地址</b></p><p>示例:<br />http://www.cnprog.com/images/temp.jpg   \"我的截图\"</p>";var D="<p style='margin-top: 0px'><b>输入Web地址</b></p><p>示例:<br />http://www.cnprog.com/   \"我的网站\"</p>";var n='<div>或者上传本地图片:</div><input type="file" name="file-upload" id="file-upload" size="26" onchange="return ajaxFileUpload($(\'#image-url\'));"/><br><img id="loading" src="/media/images/indicator.gif" style="display:none;"/>';var b="http://";var g="http://";var o="images/";var A=500;var x=100;var k="http://wmd-editor.com/";var r="WMD website";var w="_blank";y.PanelCollection=function(){this.buttonBar=E.getElementById("wmd-button-bar");this.preview=E.getElementById("previewer");this.output=E.getElementById("wmd-output");this.input=E.getElementById("editor")};y.panels=undefined;y.ieCachedRange=null;y.ieRetardedClick=false;a.isVisible=function(F){if(window.getComputedStyle){return window.getComputedStyle(F,null).getPropertyValue("display")!=="none"}else{if(F.currentStyle){return F.currentStyle.display!=="none"}}};a.addEvent=function(G,F,H){if(G.attachEvent){G.attachEvent("on"+F,H)}else{G.addEventListener(F,H,false)}};a.removeEvent=function(G,F,H){if(G.detachEvent){G.detachEvent("on"+F,H)}else{G.removeEventListener(F,H,false)}};a.fixEolChars=function(F){F=F.replace(/\r\n/g,"\n");F=F.replace(/\r/g,"\n");return F};a.extendRegExp=function(H,J,G){if(J===null||J===undefined){J=""}if(G===null||G===undefined){G=""}var I=H.toString();var F;I=I.replace(/\/([gim]*)$/,"");F=s.$1;I=I.replace(/(^\/|\/$)/g,"");I=J+I+G;return new s(I,F)};a.createImage=function(F){var H=o+F;var G=E.createElement("img");G.className="wmd-button";G.src=H;return G};a.prompt=function(M,P,H){var I;var F;var K;var J=0;if(arguments.length==4){J=arguments[3]}if(P===undefined){P=""}var L=function(Q){var R=(Q.charCode||Q.keyCode);if(R===27){N(true)}};var N=function(Q){a.removeEvent(E.body,"keydown",L);var R=K.value;if(Q){R=null}else{R=R.replace("http://http://","http://");R=R.replace("http://https://","https://");R=R.replace("http://ftp://","ftp://");if(R.indexOf("http://")===-1&&R.indexOf("ftp://")===-1){R="http://"+R}}I.parentNode.removeChild(I);F.parentNode.removeChild(F);H(R);return false};var G=function(){F=E.createElement("div");F.className="wmd-prompt-background";style=F.style;style.position="absolute";style.top="0";style.zIndex="1000";if(v.isKonqueror){style.backgroundColor="transparent"}else{if(v.isIE){style.filter="alpha(opacity=50)"}else{style.opacity="0.5"}}var Q=C.getPageSize();style.height=Q[1]+"px";if(v.isIE){style.left=E.documentElement.scrollLeft;style.width=E.documentElement.clientWidth}else{style.left="0";style.width="100%"}E.body.appendChild(F)};var O=function(){I=E.createElement("div");I.className="wmd-prompt-dialog";I.style.padding="10px;";I.style.position="fixed";I.style.width="400px";I.style.zIndex="1001";var Q=E.createElement("div");Q.innerHTML=M;Q.style.padding="5px";I.appendChild(Q);var S=E.createElement("form");S.onsubmit=function(){return N(false)};style=S.style;style.padding="0";style.margin="0";style.cssFloat="left";style.width="100%";style.textAlign="center";style.position="relative";I.appendChild(S);K=E.createElement("input");if(J==1){K.id="image-url"}K.type="text";K.value=P;style=K.style;style.display="block";style.width="80%";style.marginLeft=style.marginRight="auto";S.appendChild(K);if(J==1){var R=E.createElement("div");R.innerHTML=n;R.style.padding="5px";S.appendChild(R)}var U=E.createElement("input");U.type="button";U.onclick=function(){return N(false)};U.value="OK";style=U.style;style.margin="10px";style.display="inline";style.width="7em";var T=E.createElement("input");T.type="button";T.onclick=function(){return N(true)};T.value="Cancel";style=T.style;style.margin="10px";style.display="inline";style.width="7em";if(/mac/.test(l.platform.toLowerCase())){S.appendChild(T);S.appendChild(U)}else{S.appendChild(U);S.appendChild(T)}a.addEvent(E.body,"keydown",L);I.style.top="50%";I.style.left="50%";I.style.display="block";if(v.isIE_5or6){I.style.position="absolute";I.style.top=E.documentElement.scrollTop+200+"px";I.style.left="50%"}E.body.appendChild(I);I.style.marginTop=-(C.getHeight(I)/2)+"px";I.style.marginLeft=-(C.getWidth(I)/2)+"px"};G();top.setTimeout(function(){O();var R=P.length;if(K.selectionStart!==undefined){K.selectionStart=0;K.selectionEnd=R}else{if(K.createTextRange){var Q=K.createTextRange();Q.collapse(false);Q.moveStart("character",-R);Q.moveEnd("character",R);Q.select()}}K.focus()},0)};C.getTop=function(H,G){var F=H.offsetTop;if(!G){while(H=H.offsetParent){F+=H.offsetTop}}return F};C.getHeight=function(F){return F.offsetHeight||F.scrollHeight};C.getWidth=function(F){return F.offsetWidth||F.scrollWidth};C.getPageSize=function(){var G,H;var F,K;if(self.innerHeight&&self.scrollMaxY){G=E.body.scrollWidth;H=self.innerHeight+self.scrollMaxY}else{if(E.body.scrollHeight>E.body.offsetHeight){G=E.body.scrollWidth;H=E.body.scrollHeight}else{G=E.body.offsetWidth;H=E.body.offsetHeight}}if(self.innerHeight){F=self.innerWidth;K=self.innerHeight}else{if(E.documentElement&&E.documentElement.clientHeight){F=E.documentElement.clientWidth;K=E.documentElement.clientHeight}else{if(E.body){F=E.body.clientWidth;K=E.body.clientHeight}}}var J=Math.max(G,F);var I=Math.max(H,K);return[J,I,F,K]};y.inputPoller=function(O,H){var F=this;var K=y.panels.input;var G;var I;var L;var J;this.tick=function(){if(!a.isVisible(K)){return}if(K.selectionStart||K.selectionStart===0){var Q=K.selectionStart;var P=K.selectionEnd;if(Q!=G||P!=I){G=Q;I=P;if(L!=K.value){L=K.value;return true}}}return false};var N=function(){if(!a.isVisible(K)){return}if(F.tick()){O()}};var M=function(){J=top.setInterval(N,H)};this.destroy=function(){top.clearInterval(J)};M()};y.undoManager=function(Q){var U=this;var O=[];var M=0;var L="none";var G;var R;var H;var K;var F=function(W,V){if(L!=W){L=W;if(!V){I()}}if(!v.isIE||L!="moving"){H=top.setTimeout(N,1)}else{K=null}};var N=function(){K=new y.TextareaState();R.tick();H=undefined};this.setCommandMode=function(){L="command";I();H=top.setTimeout(N,0)};this.canUndo=function(){return M>1};this.canRedo=function(){if(O[M+1]){return true}return false};this.undo=function(){if(U.canUndo()){if(G){G.restore();G=null}else{O[M]=new y.TextareaState();O[--M].restore();if(Q){Q()}}}L="none";y.panels.input.focus();N()};this.redo=function(){if(U.canRedo()){O[++M].restore();if(Q){Q()}}L="none";y.panels.input.focus();N()};var I=function(){var V=K||new y.TextareaState();if(!V){return false}if(L=="moving"){if(!G){G=V}return}if(G){if(O[M-1].text!=G.text){O[M++]=G}G=null}O[M++]=V;O[M+1]=null;if(Q){Q()}};var P=function(V){var X=false;if(V.ctrlKey||V.metaKey){var W=V.charCode||V.keyCode;var Y=String.fromCharCode(W);switch(Y){case"y":U.redo();X=true;break;case"z":if(!V.shiftKey){U.undo()}else{U.redo()}X=true;break}}if(X){if(V.preventDefault){V.preventDefault()}if(top.event){top.event.returnValue=false}return}};var T=function(V){if(!V.ctrlKey&&!V.metaKey){var W=V.keyCode;if((W>=33&&W<=40)||(W>=63232&&W<=63235)){F("moving")}else{if(W==8||W==46||W==127){F("deleting")}else{if(W==13){F("newlines")}else{if(W==27){F("escape")}else{if((W<16||W>20)&&W!=91){F("typing")}}}}}}};var J=function(){a.addEvent(y.panels.input,"keypress",function(W){if((W.ctrlKey||W.metaKey)&&(W.keyCode==89||W.keyCode==90)){W.preventDefault()}});var V=function(){if(v.isIE||(K&&K.text!=y.panels.input.value)){if(H==undefined){L="paste";I();N()}}};R=new y.inputPoller(V,x);a.addEvent(y.panels.input,"keydown",P);a.addEvent(y.panels.input,"keydown",T);a.addEvent(y.panels.input,"mousedown",function(){F("moving")});y.panels.input.onpaste=V;y.panels.input.ondrop=V};var S=function(){J();N();I()};this.destroy=function(){if(R){R.destroy()}};S()};y.editor=function(O){if(!O){O=function(){}}var L=y.panels.input;var I=0;var P=this;var K;var R;var G;var M;var N;var U=function(W){L.focus();if(W.textOp){if(N){N.setCommandMode()}var Y=new y.TextareaState();if(!Y){return}var Z=Y.getChunks();var V=function(){L.focus();if(Z){Y.setChunks(Z)}Y.restore();O()};var X=W.textOp(Z,V);if(!X){V()}}if(W.execute){W.execute(P)}};var S=function(){if(N){F(document.getElementById("wmd-undo-button"),N.canUndo());F(document.getElementById("wmd-redo-button"),N.canRedo())}};var F=function(V,X){var Y="0px";var Z="-20px";var W="-40px";if(X){V.style.backgroundPosition=V.XShift+" "+Y;V.onmouseover=function(){this.style.backgroundPosition=this.XShift+" "+W};V.onmouseout=function(){this.style.backgroundPosition=this.XShift+" "+Y};if(v.isIE){V.onmousedown=function(){y.ieRetardedClick=true;y.ieCachedRange=document.selection.createRange()}}if(!V.isHelp){V.onclick=function(){if(this.onmouseout){this.onmouseout()}U(this);return false}}}else{V.style.backgroundPosition=V.XShift+" "+Z;V.onmouseover=V.onmouseout=V.onclick=function(){}}};var J=function(){var Z=document.getElementById("wmd-button-bar");var W="0px";var Y="-20px";var ae="-40px";var ak=document.createElement("ul");ak.id="wmd-button-row";ak=Z.appendChild(ak);var ad=document.createElement("li");ad.className="wmd-button";ad.id="wmd-bold-button";ad.title=c;ad.XShift="0px";ad.textOp=h.doBold;F(ad,true);ak.appendChild(ad);var ac=document.createElement("li");ac.className="wmd-button";ac.id="wmd-italic-button";ac.title=f;ac.XShift="-20px";ac.textOp=h.doItalic;F(ac,true);ak.appendChild(ac);var ah=document.createElement("li");ah.className="wmd-spacer";ah.id="wmd-spacer1";ak.appendChild(ah);var ai=document.createElement("li");ai.className="wmd-button";ai.id="wmd-link-button";ai.title=z;ai.XShift="-40px";ai.textOp=function(ap,aq){return h.doLinkOrImage(ap,aq,false)};F(ai,true);ak.appendChild(ai);var al=document.createElement("li");al.className="wmd-button";al.id="wmd-quote-button";al.title=u;al.XShift="-60px";al.textOp=h.doBlockquote;F(al,true);ak.appendChild(al);var am=document.createElement("li");am.className="wmd-button";am.id="wmd-code-button";am.title=e;am.XShift="-80px";am.textOp=h.doCode;F(am,true);ak.appendChild(am);var aa=document.createElement("li");aa.className="wmd-button";aa.id="wmd-image-button";aa.title=d;aa.XShift="-100px";aa.textOp=function(ap,aq){return h.doLinkOrImage(ap,aq,true)};F(aa,true);ak.appendChild(aa);var ag=document.createElement("li");ag.className="wmd-spacer";ag.id="wmd-spacer2";ak.appendChild(ag);var ab=document.createElement("li");ab.className="wmd-button";ab.id="wmd-olist-button";ab.title=q;ab.XShift="-120px";ab.textOp=function(ap,aq){h.doList(ap,aq,true)};F(ab,true);ak.appendChild(ab);var ao=document.createElement("li");ao.className="wmd-button";ao.id="wmd-ulist-button";ao.title=t;ao.XShift="-140px";ao.textOp=function(ap,aq){h.doList(ap,aq,false)};F(ao,true);ak.appendChild(ao);var aj=document.createElement("li");aj.className="wmd-button";aj.id="wmd-heading-button";aj.title=i;aj.XShift="-160px";aj.textOp=h.doHeading;F(aj,true);ak.appendChild(aj);var X=document.createElement("li");X.className="wmd-button";X.id="wmd-hr-button";X.title=p;X.XShift="-180px";X.textOp=h.doHorizontalRule;F(X,true);ak.appendChild(X);var af=document.createElement("li");af.className="wmd-spacer";af.id="wmd-spacer3";ak.appendChild(af);var V=document.createElement("li");V.className="wmd-button";V.id="wmd-undo-button";V.title=m;V.XShift="-200px";V.execute=function(ap){ap.undo()};F(V,true);ak.appendChild(V);var an=document.createElement("li");an.className="wmd-button";an.id="wmd-redo-button";an.title=j;if(/win/.test(l.platform.toLowerCase())){an.title=j}else{an.title="重做 - Ctrl+Shift+Z"}an.XShift="-220px";an.execute=function(ap){ap.redo()};F(an,true);ak.appendChild(an);S()};var H=function(){if(/\?noundo/.test(E.location.href)){y.nativeUndo=true}if(!y.nativeUndo){N=new y.undoManager(function(){O();S()})}J();var W="keydown";if(v.isOpera){W="keypress"}a.addEvent(L,W,function(Y){if(Y.ctrlKey||Y.metaKey){var Z=Y.charCode||Y.keyCode;var X=String.fromCharCode(Z).toLowerCase();if(Z===46){X=""}if(Z===190){X="."}switch(X){case"b":U(document.getElementById("wmd-bold-button"));break;case"i":U(document.getElementById("wmd-italic-button"));break;case"l":U(document.getElementById("wmd-link-button"));break;case".":U(document.getElementById("wmd-quote-button"));break;case"k":U(document.getElementById("wmd-code-button"));break;case"g":U(document.getElementById("wmd-image-button"));break;case"o":U(document.getElementById("wmd-olist-button"));break;case"u":U(document.getElementById("wmd-ulist-button"));break;case"h":U(document.getElementById("wmd-heading-button"));break;case"r":U(document.getElementById("wmd-hr-button"));break;case"y":U(document.getElementById("wmd-redo-button"));break;case"z":if(Y.shiftKey){U(document.getElementById("wmd-redo-button"))}else{U(document.getElementById("wmd-undo-button"))}break;default:return}if(Y.preventDefault){Y.preventDefault()}if(top.event){top.event.returnValue=false}}});a.addEvent(L,"keyup",function(X){if(X.shiftKey&&!X.ctrlKey&&!X.metaKey){var Y=X.charCode||X.keyCode;if(Y===13){fakeButton={};fakeButton.textOp=h.doAutoindent;U(fakeButton)}}});if(L.form){var V=L.form.onsubmit;L.form.onsubmit=function(){Q();if(V){return V.apply(this,arguments)}}}};var Q=function(){if(y.showdown){var V=new y.showdown.converter()}var W=L.value;var X=function(){L.value=W};if(!/markdown/.test(y.wmd_env.output.toLowerCase())){if(V){L.value=V.makeHtml(W);top.setTimeout(X,0)}}return true};this.undo=function(){if(N){N.undo()}};this.redo=function(){if(N){N.redo()}};var T=function(){H()};this.destroy=function(){if(N){N.destroy()}if(G.parentNode){G.parentNode.removeChild(G)}if(L){L.style.marginTop=""}top.clearInterval(M)};T()};y.TextareaState=function(){var F=this;var G=y.panels.input;this.init=function(){if(!a.isVisible(G)){return}this.setInputAreaSelectionStartEnd();this.scrollTop=G.scrollTop;if(!this.text&&G.selectionStart||G.selectionStart===0){this.text=G.value}};this.setInputAreaSelection=function(){if(!a.isVisible(G)){return}if(G.selectionStart!==undefined&&!v.isOpera){G.focus();G.selectionStart=F.start;G.selectionEnd=F.end;G.scrollTop=F.scrollTop}else{if(E.selection){if(E.activeElement&&E.activeElement!==G){return}G.focus();var H=G.createTextRange();H.moveStart("character",-G.value.length);H.moveEnd("character",-G.value.length);H.moveEnd("character",F.end);H.moveStart("character",F.start);H.select()}}};this.setInputAreaSelectionStartEnd=function(){if(G.selectionStart||G.selectionStart===0){F.start=G.selectionStart;F.end=G.selectionEnd}else{if(E.selection){F.text=a.fixEolChars(G.value);var K;if(y.ieRetardedClick&&y.ieCachedRange){K=y.ieCachedRange;y.ieRetardedClick=false}else{K=E.selection.createRange()}var L=a.fixEolChars(K.text);var J="\x07";var I=J+L+J;K.text=I;var M=a.fixEolChars(G.value);K.moveStart("character",-I.length);K.text=L;F.start=M.indexOf(J);F.end=M.lastIndexOf(J)-J.length;var H=F.text.length-a.fixEolChars(G.value).length;if(H){K.moveStart("character",-L.length);while(H--){L+="\n";F.end+=1}K.text=L}this.setInputAreaSelection()}}};this.restore=function(){if(F.text!=undefined&&F.text!=G.value){G.value=F.text}this.setInputAreaSelection();G.scrollTop=F.scrollTop};this.getChunks=function(){var H=new y.Chunks();H.before=a.fixEolChars(F.text.substring(0,F.start));H.startTag="";H.selection=a.fixEolChars(F.text.substring(F.start,F.end));H.endTag="";H.after=a.fixEolChars(F.text.substring(F.end));H.scrollTop=F.scrollTop;return H};this.setChunks=function(H){H.before=H.before+H.startTag;H.after=H.endTag+H.after;if(v.isOpera){H.before=H.before.replace(/\n/g,"\r\n");H.selection=H.selection.replace(/\n/g,"\r\n");H.after=H.after.replace(/\n/g,"\r\n")}this.start=H.before.length;this.end=H.before.length+H.selection.length;this.text=H.before+H.selection+H.after;this.scrollTop=H.scrollTop};this.init()};y.Chunks=function(){};y.Chunks.prototype.findTags=function(G,I){var F=this;var H;if(G){H=a.extendRegExp(G,"","$");this.before=this.before.replace(H,function(J){F.startTag=F.startTag+J;return""});H=a.extendRegExp(G,"^","");this.selection=this.selection.replace(H,function(J){F.startTag=F.startTag+J;return""})}if(I){H=a.extendRegExp(I,"","$");this.selection=this.selection.replace(H,function(J){F.endTag=J+F.endTag;return""});H=a.extendRegExp(I,"^","");this.after=this.after.replace(H,function(J){F.endTag=J+F.endTag;return""})}};y.Chunks.prototype.trimWhitespace=function(F){this.selection=this.selection.replace(/^(\s*)/,"");if(!F){this.before+=s.$1}this.selection=this.selection.replace(/(\s*)$/,"");if(!F){this.after=s.$1+this.after}};y.Chunks.prototype.skipLines=function(H,G,F){if(H===undefined){H=1}if(G===undefined){G=1}H++;G++;var I;var J;this.selection=this.selection.replace(/(^\n*)/,"");this.startTag=this.startTag+s.$1;this.selection=this.selection.replace(/(\n*$)/,"");this.endTag=this.endTag+s.$1;this.startTag=this.startTag.replace(/(^\n*)/,"");this.before=this.before+s.$1;this.endTag=this.endTag.replace(/(\n*$)/,"");this.after=this.after+s.$1;if(this.before){I=J="";while(H--){I+="\\n?";J+="\n"}if(F){I="\\n*"}this.before=this.before.replace(new s(I+"$",""),J)}if(this.after){I=J="";while(G--){I+="\\n?";J+="\n"}if(F){I="\\n*"}this.after=this.after.replace(new s(I,""),J)}};h.prefixes="(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";h.unwrap=function(G){var F=new s("([^\\n])\\n(?!(\\n|"+h.prefixes+"))","g");G.selection=G.selection.replace(F,"$1 $2")};h.wrap=function(G,F){h.unwrap(G);var H=new s("(.{1,"+F+"})( +|$\\n?)","gm");G.selection=G.selection.replace(H,function(I,J){if(new s("^"+h.prefixes,"").test(I)){return I}return J+"\n"});G.selection=G.selection.replace(/\s+$/,"")};h.doBold=function(F,G){return h.doBorI(F,G,2,"strong text")};h.doItalic=function(F,G){return h.doBorI(F,G,1,"emphasized text")};h.doBorI=function(L,J,K,F){L.trimWhitespace();L.selection=L.selection.replace(/\n{2,}/g,"\n");L.before.search(/(\**$)/);var I=s.$1;L.after.search(/(^\**)/);var G=s.$1;var M=Math.min(I.length,G.length);if((M>=K)&&(M!=2||K!=1)){L.before=L.before.replace(s("[*]{"+K+"}$",""),"");L.after=L.after.replace(s("^[*]{"+K+"}",""),"")}else{if(!L.selection&&G){L.after=L.after.replace(/^([*_]*)/,"");L.before=L.before.replace(/(\s?)$/,"");var H=s.$1;L.before=L.before+G+H}else{if(!L.selection&&!G){L.selection=F}var N=K<=1?"*":"**";L.before=L.before+N;L.after=N+L.after}}return};h.stripLinkDefs=function(G,F){G=G.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,function(K,L,H,I,J){F[L]=K.replace(/\s*$/,"");if(I){F[L]=K.replace(/["(](.+?)[")]$/,"");return I+J}return""});return G};h.addLinkDef=function(M,I){var F=0;var H={};M.before=h.stripLinkDefs(M.before,H);M.selection=h.stripLinkDefs(M.selection,H);M.after=h.stripLinkDefs(M.after,H);var G="";var L=/(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;var K=function(O){F++;O=O.replace(/^[ ]{0,3}\[(\d+)\]:/,"  ["+F+"]:");G+="\n"+O};var J=function(P,Q,R,O){if(H[R]){K(H[R]);return Q+F+O}return P};M.before=M.before.replace(L,J);if(I){K(I)}else{M.selection=M.selection.replace(L,J)}var N=F;M.after=M.after.replace(L,J);if(M.after){M.after=M.after.replace(/\n*$/,"")}if(!M.after){M.selection=M.selection.replace(/\n*$/,"")}M.after+="\n\n"+G;return N};h.doLinkOrImage=function(F,G,I){F.trimWhitespace();F.findTags(/\s*!?\[/,/\][ ]?(?:\n[ ]*)?(\[.*?\])?/);if(F.endTag.length>1){F.startTag=F.startTag.replace(/!?\[/,"");F.endTag="";h.addLinkDef(F,null)}else{if(/\n\n/.test(F.selection)){h.addLinkDef(F,null);return}var H=function(L){if(L!==null){F.startTag=F.endTag="";var K=" [999]: "+L;var J=h.addLinkDef(F,K);F.startTag=I?"![":"[";F.endTag="]["+J+"]";if(!F.selection){if(I){F.selection="alt text"}else{F.selection="link text"}}}G()};if(I){a.prompt(B,b,H,1)}else{a.prompt(D,g,H)}return true}};a.makeAPI=function(){y.wmd={};y.wmd.editor=y.editor;y.wmd.previewManager=y.previewManager};a.startEditor=function(){if(y.wmd_env.autostart===false){a.makeAPI();return}var G;var F;var H=function(){y.panels=new y.PanelCollection();F=new y.previewManager();var I=F.refresh;G=new y.editor(I);F.refresh(true)};a.addEvent(top,"load",H)};y.previewManager=function(){var H=this;var V;var F;var N;var M;var S;var O;var I=3000;var P="delayed";var K=function(X,Y){a.addEvent(X,"input",Y);X.onpaste=Y;X.ondrop=Y;a.addEvent(X,"keypress",Y);a.addEvent(X,"keydown",Y);F=new y.inputPoller(Y,A)};var R=function(){var X=0;if(top.innerHeight){X=top.pageYOffset}else{if(E.documentElement&&E.documentElement.scrollTop){X=E.documentElement.scrollTop}else{if(E.body){X=E.body.scrollTop}}}return X};var L=function(){if(!y.panels.preview&&!y.panels.output){return}var Z=y.panels.input.value;if(Z&&Z==S){return}else{S=Z}var Y=new Date().getTime();if(!V&&y.showdown){V=new y.showdown.converter()}if(V){Z=V.makeHtml(Z)}var X=new Date().getTime();M=X-Y;G(Z);O=Z};var U=function(){if(N){top.clearTimeout(N);N=undefined}if(P!=="manual"){var X=0;if(P==="delayed"){X=M}if(X>I){X=I}N=top.setTimeout(L,X)}};var J=function(X){if(X.scrollHeight<=X.clientHeight){return 1}return X.scrollTop/(X.scrollHeight-X.clientHeight)};var W=function(){if(y.panels.preview){y.panels.preview.scrollTop=(y.panels.preview.scrollHeight-y.panels.preview.clientHeight)*J(y.panels.preview)}if(y.panels.output){y.panels.output.scrollTop=(y.panels.output.scrollHeight-y.panels.output.clientHeight)*J(y.panels.output)}};this.refresh=function(X){if(X){S="";L()}else{U()}};this.processingTime=function(){return M};this.output=function(){return O};this.setUpdateMode=function(X){P=X;H.refresh()};var Q=true;var G=function(aa){var X=C.getTop(y.panels.input)-R();if(y.panels.output){if(y.panels.output.value!==undefined){y.panels.output.value=aa;y.panels.output.readOnly=true}else{var Z=aa.replace(/&/g,"&amp;");Z=Z.replace(/</g,"&lt;");y.panels.output.innerHTML="<pre><code>"+Z+"</code></pre>"}}if(y.panels.preview){y.panels.preview.innerHTML=aa}W();if(Q){Q=false;return}var Y=C.getTop(y.panels.input)-R();if(v.isIE){top.setTimeout(function(){top.scrollBy(0,Y-X)},0)}else{top.scrollBy(0,Y-X)}};var T=function(){K(y.panels.input,U);L();if(y.panels.preview){y.panels.preview.scrollTop=0}if(y.panels.output){y.panels.output.scrollTop=0}};this.destroy=function(){if(F){F.destroy()}};T()};h.doAutoindent=function(F,G){F.before=F.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/,"\n\n");F.before=F.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/,"\n\n");F.before=F.before.replace(/(\n|^)[ \t]+\n$/,"\n\n");if(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(F.before)){if(h.doList){h.doList(F)}}if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(F.before)){if(h.doBlockquote){h.doBlockquote(F)}}if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(F.before)){if(h.doCode){h.doCode(F)}}};h.doBlockquote=function(F,G){F.selection=F.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,function(L,K,J,I){F.before+=K;F.after=I+F.after;return J});F.before=F.before.replace(/(>[ \t]*)$/,function(J,I){F.selection=I+F.selection;return""});F.selection=F.selection.replace(/^(\s|>)+$/,"");F.selection=F.selection||"Blockquote";if(F.before){F.before=F.before.replace(/\n?$/,"\n")}if(F.after){F.after=F.after.replace(/^\n?/,"\n")}F.before=F.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,function(I){F.startTag=I;return""});F.after=F.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,function(I){F.endTag=I;return""});var H=function(J){var I=J?"> ":"";if(F.startTag){F.startTag=F.startTag.replace(/\n((>|\s)*)\n$/,function(L,K){return"\n"+K.replace(/^[ ]{0,3}>?[ \t]*$/gm,I)+"\n"})}if(F.endTag){F.endTag=F.endTag.replace(/^\n((>|\s)*)\n/,function(L,K){return"\n"+K.replace(/^[ ]{0,3}>?[ \t]*$/gm,I)+"\n"})}};if(/^(?![ ]{0,3}>)/m.test(F.selection)){h.wrap(F,y.wmd_env.lineLength-2);F.selection=F.selection.replace(/^/gm,"> ");H(true);F.skipLines()}else{F.selection=F.selection.replace(/^[ ]{0,3}> ?/gm,"");h.unwrap(F);H(false);if(!/^(\n|^)[ ]{0,3}>/.test(F.selection)&&F.startTag){F.startTag=F.startTag.replace(/\n{0,2}$/,"\n\n")}if(!/(\n|^)[ ]{0,3}>.*$/.test(F.selection)&&F.endTag){F.endTag=F.endTag.replace(/^\n{0,2}/,"\n\n")}}if(!/\n/.test(F.selection)){F.selection=F.selection.replace(/^(> *)/,function(I,J){F.startTag+=J;return""})}};h.doCode=function(F,G){var I=/\S[ ]*$/.test(F.before);var K=/^[ ]*\S/.test(F.after);if((!K&&!I)||/\n/.test(F.selection)){F.before=F.before.replace(/[ ]{4}$/,function(L){F.selection=L+F.selection;return""});var J=1;var H=1;if(/\n(\t|[ ]{4,}).*\n$/.test(F.before)){J=0}if(/^\n(\t|[ ]{4,})/.test(F.after)){H=0}F.skipLines(J,H);if(!F.selection){F.startTag="    ";F.selection="enter code here"}else{if(/^[ ]{0,3}\S/m.test(F.selection)){F.selection=F.selection.replace(/^/gm,"    ")}else{F.selection=F.selection.replace(/^[ ]{4}/gm,"")}}}else{F.trimWhitespace();F.findTags(/`/,/`/);if(!F.startTag&&!F.endTag){F.startTag=F.endTag="`";if(!F.selection){F.selection="enter code here"}}else{if(F.endTag&&!F.startTag){F.before+=F.endTag;F.endTag=""}else{F.startTag=F.endTag=""}}}};h.doList=function(Q,J,I){var S=/(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;var R=/^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;var F="-";var N=1;var L=function(){var T;if(I){T=" "+N+". ";N++}else{T=" "+F+" "}return T};var M=function(T){if(I===undefined){I=/^\s*\d/.test(T)}T=T.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,function(U){return L()});return T};Q.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/,null);if(Q.before&&!/\n$/.test(Q.before)&&!/^\n/.test(Q.startTag)){Q.before+=Q.startTag;Q.startTag=""}if(Q.startTag){var H=/\d+[.]/.test(Q.startTag);Q.startTag="";Q.selection=Q.selection.replace(/\n[ ]{4}/g,"\n");h.unwrap(Q);Q.skipLines();if(H){Q.after=Q.after.replace(R,M)}if(I==H){return}}var K=1;Q.before=Q.before.replace(S,function(T){if(/^\s*([*+-])/.test(T)){F=s.$1}K=/[^\n]\n\n[^\n]/.test(T)?1:0;return M(T)});if(!Q.selection){Q.selection="List item"}var O=L();var G=1;Q.after=Q.after.replace(R,function(T){G=/[^\n]\n\n[^\n]/.test(T)?1:0;return M(T)});Q.trimWhitespace(true);Q.skipLines(K,G,true);Q.startTag=O;var P=O.replace(/./g," ");h.wrap(Q,y.wmd_env.lineLength-P.length);Q.selection=Q.selection.replace(/\n/g,"\n"+P)};h.doHeading=function(H,I){H.selection=H.selection.replace(/\s+/g," ");H.selection=H.selection.replace(/(^\s+|\s+$)/g,"");if(!H.selection){H.startTag="## ";H.selection="Heading";H.endTag=" ##";return}var J=0;H.findTags(/#+[ ]*/,/[ ]*#+/);if(/#+/.test(H.startTag)){J=s.lastMatch.length}H.startTag=H.endTag="";H.findTags(null,/\s?(-+|=+)/);if(/=+/.test(H.endTag)){J=1}if(/-+/.test(H.endTag)){J=2}H.startTag=H.endTag="";H.skipLines(1,1);var K=J==0?2:J-1;if(K>0){var G=K>=2?"-":"=";var F=H.selection.length;if(F>y.wmd_env.lineLength){F=y.wmd_env.lineLength}H.endTag="\n";while(F--){H.endTag+=G}}};h.doHorizontalRule=function(F,G){F.startTag="----------\n";F.selection="";F.skipLines(2,1,true)}};Attacklab.wmd_env={};Attacklab.account_options={};Attacklab.wmd_defaults={version:1,output:"Markdown",lineLength:40,delayLoad:false};if(!Attacklab.wmd){Attacklab.wmd=function(){Attacklab.loadEnv=function(){var b=function(d){if(!d){return}for(var c in d){Attacklab.wmd_env[c]=d[c]}};b(Attacklab.wmd_defaults);b(Attacklab.account_options);b(top.wmd_options);Attacklab.full=true;var a="bold italic link blockquote code image ol ul heading hr";Attacklab.wmd_env.buttons=Attacklab.wmd_env.buttons||a};Attacklab.loadEnv()};Attacklab.wmd();Attacklab.wmdBase();Attacklab.Util.startEditor()};
\ No newline at end of file
index c71ba6e431c6f358a02f3c1b2c0a3c6cf455a00a..2508c7417f94e97f4b087bca83b11e42e14232ae 100644 (file)
@@ -3,31 +3,11 @@ body
        background-color: White
 }
 
-.wmd-panel
-{
-       width: 50%;
-       min-width: 500px;
-}
-
 #wmd-editor
 {
        background-color: Aquamarine;
 }
 
-#wmd-button-bar 
-{
-       width: 100%;
-       background-color: #ffffff;
-}
-
-#wmd-input 
-{ 
-       height: 500px;
-       width: 100%;
-       background-color: Gainsboro;
-       border: 1px solid DarkGray;
-}
-
 #wmd-preview 
 { 
        background-color: LightSkyBlue; 
@@ -47,7 +27,6 @@ body
        margin-top: 10px;
        padding: 0px;  
        height: 20px;
-    background-color:#ffffff;
 }
 
 .wmd-spacer
@@ -57,8 +36,8 @@ body
        margin-left: 14px;
        
        position: absolute;
-    background-color:#aaaaaa;
-       display: inline-block;
+       background-color: Silver;
+       display: inline-block; 
        list-style: none;
 }
 
@@ -68,7 +47,7 @@ body
        height: 20px; 
        margin-left: 5px;
        margin-right: 5px;
-    background-color:#ffffff;
+       
        position: absolute;
        background-image: url(images/wmd-buttons.png);
        background-repeat: no-repeat;
@@ -83,7 +62,7 @@ body
        height: 20px; 
        margin-left: 5px;
        margin-right: 5px;
-
+       
        position: absolute;
        display: inline-block; 
 }
index a0770119679bf0ea0c26f40bb96d5a599f1261d3..678776d1e2a1c9241763eaa869202773ecfab570 100644 (file)
@@ -51,77 +51,64 @@ Attacklab.wmdBase = function(){
        var doc  = top.document;
        var re   = top.RegExp;
        var nav  = top.navigator;
-
+       
        // Some namespaces.
        wmd.Util = {};
        wmd.Position = {};
        wmd.Command = {};
        wmd.Global = {};
-
+       
        var util = wmd.Util;
        var position = wmd.Position;
        var command = wmd.Command;
        var global = wmd.Global;
-
-
+       
+       
        // Used to work around some browser bugs where we can't use feature testing.
        global.isIE             = /msie/.test(nav.userAgent.toLowerCase());
        global.isIE_5or6        = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
        global.isIE_7plus       = global.isIE && !global.isIE_5or6;
        global.isOpera          = /opera/.test(nav.userAgent.toLowerCase());
        global.isKonqueror      = /konqueror/.test(nav.userAgent.toLowerCase());
-
-       var toolbar_strong_label = $.i18n._('bold') + " <strong> Ctrl-B";
-    var toolbar_emphasis_label = $.i18n._('italic') + " <em> Ctrl-I";
-    var toolbar_hyperlink_label = $.i18n._('link') + " <a> Ctrl-L";
-    var toolbar_blockquote_label = $.i18n._('quote') + " <blockquote> Ctrl-.";
-    var toolbar_code_label = $.i18n._('preformatted text') + " <pre><code> Ctrl-K";
-    var toolbar_image_label = $.i18n._('image') + " <img> Ctrl-G";
-    var toolbar_numbered_label = $.i18n._('numbered list') + " <ol> Ctrl-O";
-    var toolbar_bulleted_label = $.i18n._('bulleted list') + " <ul> Ctrl-U";
-    var toolbar_heading_label = $.i18n._('heading') + " <h1>/<h2> Ctrl-H";
-    var toolbar_horizontal_label = $.i18n._('horizontal bar') + " <hr> Ctrl-R";
-    var toolbar_undo_label = $.i18n._('undo') + " Ctrl-Z";
-    var toolbar_redo_label = $.i18n._('redo') + " Ctrl-Y";
-
+       
+       
        // -------------------------------------------------------------------
        //  YOUR CHANGES GO HERE
        //
-       // I've tried to localize the things you are likely to change to
+       // I've tried to localize the things you are likely to change to 
        // this area.
        // -------------------------------------------------------------------
-
+       
        // The text that appears on the upper part of the dialog box when
        // entering links.
-       var imageDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter image url') + "</p>";
-       var linkDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter url') + "</p>";
+    var imageDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter image url') + "</p>";
+    var linkDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter url') + "</p>";
     var uploadImageHTML ="<div>" + $.i18n._('upload image') + "</div>" +
             "<input type=\"file\" name=\"file-upload\" id=\"file-upload\" size=\"26\" "+
             "onchange=\"return ajaxFileUpload($('#image-url'));\"/><br>" +
             "<img id=\"loading\" src=\"" + mediaUrl("media/images/indicator.gif") + "\" style=\"display:none;\"/>";
 
-
        // The default text that appears in the dialog input box when entering
        // links.
        var imageDefaultText = "http://";
        var linkDefaultText = "http://";
-
+       
        // The location of your button images relative to the base directory.
        var imageDirectory = "images/";
-
+       
        // Some intervals in ms.  These can be adjusted to reduce the control's load.
        var previewPollInterval = 500;
        var pastePollInterval = 100;
-
+       
        // The link and title for the help button
        var helpLink = "http://wmd-editor.com/";
        var helpHoverTitle = "WMD website";
        var helpTarget = "_blank";
-
+       
        // -------------------------------------------------------------------
        //  END OF YOUR CHANGES
        // -------------------------------------------------------------------
-
+       
        // A collection of the important regions on the page.
        // Cached so we don't have to keep traversing the DOM.
        wmd.PanelCollection = function(){
@@ -130,13 +117,13 @@ Attacklab.wmdBase = function(){
                this.output = doc.getElementById("wmd-output");
                this.input = doc.getElementById("editor");
        };
-
+       
        // This PanelCollection object can't be filled until after the page
        // has loaded.
        wmd.panels = undefined;
-
+       
        // Internet explorer has problems with CSS sprite buttons that use HTML
-       // lists.  When you click on the background image "button", IE will
+       // lists.  When you click on the background image "button", IE will 
        // select the non-existent link text and discard the selection in the
        // textarea.  The solution to this is to cache the textarea selection
        // on the button's mousedown event and set a flag.  In the part of the
@@ -149,11 +136,11 @@ Attacklab.wmdBase = function(){
        // normally since the focus never leaves the textarea.
        wmd.ieCachedRange = null;               // cached textarea selection
        wmd.ieRetardedClick = false;    // flag
-
+       
        // Returns true if the DOM element is visible, false if it's hidden.
        // Checks if display is anything other than none.
        util.isVisible = function (elem) {
-
+       
            if (window.getComputedStyle) {
                // Most browsers
                        return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none";
@@ -163,8 +150,8 @@ Attacklab.wmdBase = function(){
                        return elem.currentStyle["display"] !== "none";
                }
        };
-
-
+       
+       
        // Adds a listener callback to a DOM element which is fired on a specified
        // event.
        util.addEvent = function(elem, event, listener){
@@ -178,7 +165,7 @@ Attacklab.wmdBase = function(){
                }
        };
 
-
+       
        // Removes a listener callback from a DOM element which is fired on a specified
        // event.
        util.removeEvent = function(elem, event, listener){
@@ -208,7 +195,7 @@ Attacklab.wmdBase = function(){
        //
        // regex is a RegExp, pre and post are strings.
        util.extendRegExp = function(regex, pre, post){
-
+               
                if (pre === null || pre === undefined)
                {
                        pre = "";
@@ -217,10 +204,10 @@ Attacklab.wmdBase = function(){
                {
                        post = "";
                }
-
+               
                var pattern = regex.toString();
                var flags = "";
-
+               
                // Replace the flags with empty space and store them.
                // Technically, this can match incorrect flags like "gmm".
                var result = pattern.match(/\/([gim]*)$/);
@@ -230,29 +217,29 @@ Attacklab.wmdBase = function(){
                else {
                        flags = "";
                }
-
+               
                // Remove the flags and slash delimiters from the regular expression.
                pattern = pattern.replace(/(^\/|\/[gim]*$)/g, "");
                pattern = pre + pattern + post;
-
+               
                return new RegExp(pattern, flags);
        }
 
-
+       
        // Sets the image for a button passed to the WMD editor.
        // Returns a new element with the image attached.
        // Adds several style properties to the image.
        util.createImage = function(img){
-
+               
                var imgPath = imageDirectory + img;
-
+               
                var elem = doc.createElement("img");
                elem.className = "wmd-button";
                elem.src = imgPath;
 
                return elem;
        };
-
+       
 
        // This simulates a modal dialog box and asks for the URL when you
        // click the hyperlink or image buttons.
@@ -261,7 +248,7 @@ Attacklab.wmdBase = function(){
        // defaultInputText: The default value that appears in the input box.
        // makeLinkMarkdown: The function which is executed when the prompt is dismissed, either via OK or Cancel
        util.prompt = function(text, defaultInputText, makeLinkMarkdown){
-
+       
                // These variables need to be declared at this level since they are used
                // in multiple functions.
                var dialog;                     // The dialog box.
@@ -276,7 +263,7 @@ Attacklab.wmdBase = function(){
                if (defaultInputText === undefined) {
                        defaultInputText = "";
                }
-
+               
                // Used as a keydown event handler. Esc dismisses the prompt.
                // Key code 27 is ESC.
                var checkEscape = function(key){
@@ -285,7 +272,7 @@ Attacklab.wmdBase = function(){
                                close(true);
                        }
                };
-
+               
                // Dismisses the hyperlink input box.
                // isCancel is true if we don't care about the input text.
                // isCancel is false if we are going to keep the text.
@@ -301,31 +288,31 @@ Attacklab.wmdBase = function(){
                                text = text.replace('http://http://', 'http://');
                                text = text.replace('http://https://', 'https://');
                                text = text.replace('http://ftp://', 'ftp://');
-
-                               if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1 && text.indexOf('/') !== 0) {
+                               
+                               if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1) {
                                        text = 'http://' + text;
                                }
                        }
-
+                       
                        dialog.parentNode.removeChild(dialog);
                        background.parentNode.removeChild(background);
                        makeLinkMarkdown(text);
                        return false;
                };
-
+               
                // Creates the background behind the hyperlink text entry box.
                // Most of this has been moved to CSS but the div creation and
                // browser-specific hacks remain here.
                var createBackground = function(){
-
+               
                        background = doc.createElement("div");
                        background.className = "wmd-prompt-background";
                        style = background.style;
                        style.position = "absolute";
                        style.top = "0";
-
+                       
                        style.zIndex = "1000";
-
+                       
                        // Some versions of Konqueror don't support transparent colors
                        // so we make the whole window transparent.
                        //
@@ -339,10 +326,10 @@ Attacklab.wmdBase = function(){
                        else {
                                style.opacity = "0.5";
                        }
-
+                       
                        var pageSize = position.getPageSize();
                        style.height = pageSize[1] + "px";
-
+                       
                        if(global.isIE){
                                style.left = doc.documentElement.scrollLeft;
                                style.width = doc.documentElement.clientWidth;
@@ -351,13 +338,13 @@ Attacklab.wmdBase = function(){
                                style.left = "0";
                                style.width = "100%";
                        }
-
+                       
                        doc.body.appendChild(background);
                };
-
+               
                // Create the text input box form/window.
                var createDialog = function(){
-
+               
                        // The main dialog box.
                        dialog = doc.createElement("div");
                        dialog.className = "wmd-prompt-dialog";
@@ -365,13 +352,13 @@ Attacklab.wmdBase = function(){
                        dialog.style.position = "fixed";
                        dialog.style.width = "400px";
                        dialog.style.zIndex = "1001";
-
+                       
                        // The dialog text.
                        var question = doc.createElement("div");
                        question.innerHTML = text;
                        question.style.padding = "5px";
                        dialog.appendChild(question);
-
+                       
                        // The web form container for the text box and buttons.
                        var form = doc.createElement("form");
                        form.onsubmit = function(){ return close(false); };
@@ -383,12 +370,9 @@ Attacklab.wmdBase = function(){
                        style.textAlign = "center";
                        style.position = "relative";
                        dialog.appendChild(form);
-
+                       
                        // The input text box
                        input = doc.createElement("input");
-            if(type == 1){
-                input.id = "image-url";
-            }
                        input.type = "text";
                        input.value = defaultInputText;
                        style = input.style;
@@ -398,13 +382,11 @@ Attacklab.wmdBase = function(){
                        form.appendChild(input);
 
             // The upload file input
-            if(type == 1){
-                var upload = doc.createElement("div");
-                upload.innerHTML = uploadImageHTML;
-                upload.style.padding = "5px";
-                form.appendChild(upload);
-            }
-
+            var upload = doc.createElement("div");
+            upload.innerHTML = uploadImageHTML;
+            upload.style.padding = "5px";
+            form.appendChild(upload);
+                       
                        // The ok button
                        var okButton = doc.createElement("input");
                        okButton.type = "button";
@@ -415,7 +397,7 @@ Attacklab.wmdBase = function(){
                        style.display = "inline";
                        style.width = "7em";
 
-
+                       
                        // The cancel button
                        var cancelButton = doc.createElement("input");
                        cancelButton.type = "button";
@@ -446,20 +428,20 @@ Attacklab.wmdBase = function(){
                                dialog.style.left = "50%";
                        }
                        doc.body.appendChild(dialog);
-
+                       
                        // This has to be done AFTER adding the dialog to the form if you
                        // want it to be centered.
                        dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px";
                        dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px";
-
+                       
                };
-
+               
                createBackground();
-
+               
                // Why is this in a zero-length timeout?
                // Is it working around a browser bug?
                top.setTimeout(function(){
-
+               
                        createDialog();
 
                        var defTextLen = defaultInputText.length;
@@ -474,12 +456,12 @@ Attacklab.wmdBase = function(){
                                range.moveEnd("character", defTextLen);
                                range.select();
                        }
-
+                       
                        input.focus();
                }, 0);
        };
-
-
+       
+       
        // UNFINISHED
        // The assignment in the while loop makes jslint cranky.
        // I'll change it to a better loop later.
@@ -492,7 +474,7 @@ Attacklab.wmdBase = function(){
                }
                return result;
        };
-
+       
        position.getHeight = function (elem) {
                return elem.offsetHeight || elem.scrollHeight;
        };
@@ -502,10 +484,10 @@ Attacklab.wmdBase = function(){
        };
 
        position.getPageSize = function(){
-
+               
                var scrollWidth, scrollHeight;
                var innerWidth, innerHeight;
-
+               
                // It's not very clear which blocks work with which browsers.
                if(self.innerHeight && self.scrollMaxY){
                        scrollWidth = doc.body.scrollWidth;
@@ -519,7 +501,7 @@ Attacklab.wmdBase = function(){
                        scrollWidth = doc.body.offsetWidth;
                        scrollHeight = doc.body.offsetHeight;
                }
-
+               
                if(self.innerHeight){
                        // Non-IE browser
                        innerWidth = self.innerWidth;
@@ -535,33 +517,33 @@ Attacklab.wmdBase = function(){
                        innerWidth = doc.body.clientWidth;
                        innerHeight = doc.body.clientHeight;
                }
-
+               
         var maxWidth = Math.max(scrollWidth, innerWidth);
         var maxHeight = Math.max(scrollHeight, innerHeight);
         return [maxWidth, maxHeight, innerWidth, innerHeight];
        };
-
+       
        // Watches the input textarea, polling at an interval and runs
        // a callback function if anything has changed.
        wmd.inputPoller = function(callback, interval){
-
+       
                var pollerObj = this;
                var inputArea = wmd.panels.input;
-
+               
                // Stored start, end and text.  Used to see if there are changes to the input.
                var lastStart;
                var lastEnd;
                var markdown;
-
+               
                var killHandle; // Used to cancel monitoring on destruction.
                // Checks to see if anything has changed in the textarea.
                // If so, it runs the callback.
                this.tick = function(){
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                       
                        // Update the selection start and end, text.
                        if (inputArea.selectionStart || inputArea.selectionStart === 0) {
                                var start = inputArea.selectionStart;
@@ -569,7 +551,7 @@ Attacklab.wmdBase = function(){
                                if (start != lastStart || end != lastEnd) {
                                        lastStart = start;
                                        lastEnd = end;
-
+                                       
                                        if (markdown != inputArea.value) {
                                                markdown = inputArea.value;
                                                return true;
@@ -578,37 +560,37 @@ Attacklab.wmdBase = function(){
                        }
                        return false;
                };
-
-
+               
+               
                var doTickCallback = function(){
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                       
                        // If anything has changed, call the function.
                        if (pollerObj.tick()) {
                                callback();
                        }
                };
-
+               
                // Set how often we poll the textarea for changes.
                var assignInterval = function(){
                        // previewPollInterval is set at the top of the namespace.
                        killHandle = top.setInterval(doTickCallback, interval);
                };
-
+               
                this.destroy = function(){
                        top.clearInterval(killHandle);
                };
-
+               
                assignInterval();
        };
-
+       
        // Handles pushing and popping TextareaStates for undo/redo commands.
        // I should rename the stack variables to list.
        wmd.undoManager = function(callback){
-
+       
                var undoObj = this;
                var undoStack = []; // A stack of undo states
                var stackPtr = 0; // The index of the current state
@@ -617,17 +599,17 @@ Attacklab.wmdBase = function(){
                var poller;
                var timer; // The setTimeout handle for cancelling the timer
                var inputStateObj;
-
+               
                // Set the mode for later logic steps.
                var setMode = function(newMode, noSave){
-
+               
                        if (mode != newMode) {
                                mode = newMode;
                                if (!noSave) {
                                        saveState();
                                }
                        }
-
+                       
                        if (!global.isIE || mode != "moving") {
                                timer = top.setTimeout(refreshState, 1);
                        }
@@ -635,33 +617,33 @@ Attacklab.wmdBase = function(){
                                inputStateObj = null;
                        }
                };
-
+               
                var refreshState = function(){
                        inputStateObj = new wmd.TextareaState();
                        poller.tick();
                        timer = undefined;
                };
-
+               
                this.setCommandMode = function(){
                        mode = "command";
                        saveState();
                        timer = top.setTimeout(refreshState, 0);
                };
-
+               
                this.canUndo = function(){
                        return stackPtr > 1;
                };
-
+               
                this.canRedo = function(){
                        if (undoStack[stackPtr + 1]) {
                                return true;
                        }
                        return false;
                };
-
+               
                // Removes the last state and restores it.
                this.undo = function(){
-
+               
                        if (undoObj.canUndo()) {
                                if (lastState) {
                                        // What about setting state -1 to null or checking for undefined?
@@ -671,40 +653,40 @@ Attacklab.wmdBase = function(){
                                else {
                                        undoStack[stackPtr] = new wmd.TextareaState();
                                        undoStack[--stackPtr].restore();
-
+                                       
                                        if (callback) {
                                                callback();
                                        }
                                }
                        }
-
+                       
                        mode = "none";
                        wmd.panels.input.focus();
                        refreshState();
                };
-
+               
                // Redo an action.
                this.redo = function(){
-
+               
                        if (undoObj.canRedo()) {
-
+                       
                                undoStack[++stackPtr].restore();
-
+                               
                                if (callback) {
                                        callback();
                                }
                        }
-
+                       
                        mode = "none";
                        wmd.panels.input.focus();
                        refreshState();
                };
-
+               
                // Push the input area state to the stack.
                var saveState = function(){
-
+               
                        var currState = inputStateObj || new wmd.TextareaState();
-
+                       
                        if (!currState) {
                                return false;
                        }
@@ -726,24 +708,24 @@ Attacklab.wmdBase = function(){
                                callback();
                        }
                };
-
+               
                var handleCtrlYZ = function(event){
-
+               
                        var handled = false;
-
+                       
                        if (event.ctrlKey || event.metaKey) {
-
+                       
                                // IE and Opera do not support charCode.
                                var keyCode = event.charCode || event.keyCode;
                                var keyCodeChar = String.fromCharCode(keyCode);
-
+                               
                                switch (keyCodeChar) {
-
+                               
                                        case "y":
                                                undoObj.redo();
                                                handled = true;
                                                break;
-
+                                               
                                        case "z":
                                                if (!event.shiftKey) {
                                                        undoObj.undo();
@@ -755,7 +737,7 @@ Attacklab.wmdBase = function(){
                                                break;
                                }
                        }
-
+                       
                        if (handled) {
                                if (event.preventDefault) {
                                        event.preventDefault();
@@ -766,14 +748,14 @@ Attacklab.wmdBase = function(){
                                return;
                        }
                };
-
+               
                // Set the mode depending on what is going on in the input area.
                var handleModeChange = function(event){
-
+               
                        if (!event.ctrlKey && !event.metaKey) {
-
+                       
                                var keyCode = event.keyCode;
-
+                               
                                if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) {
                                        // 33 - 40: page up/dn and arrow keys
                                        // 63232 - 63235: page up/dn and arrow keys on safari
@@ -794,7 +776,7 @@ Attacklab.wmdBase = function(){
                                        setMode("escape");
                                }
                                else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) {
-                                       // 16-20 are shift, etc.
+                                       // 16-20 are shift, etc. 
                                        // 91: left window key
                                        // I think this might be a little messed up since there are
                                        // a lot of nonprinting keys above 20.
@@ -802,9 +784,9 @@ Attacklab.wmdBase = function(){
                                }
                        }
                };
-
+               
                var setEventHandlers = function(){
-
+               
                        util.addEvent(wmd.panels.input, "keypress", function(event){
                                // keyCode 89: y
                                // keyCode 90: z
@@ -812,7 +794,7 @@ Attacklab.wmdBase = function(){
                                        event.preventDefault();
                                }
                        });
-
+                       
                        var handlePaste = function(){
                                if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) {
                                        if (timer == undefined) {
@@ -822,77 +804,77 @@ Attacklab.wmdBase = function(){
                                        }
                                }
                        };
-
+                       
                        // pastePollInterval is specified at the beginning of this namespace.
                        poller = new wmd.inputPoller(handlePaste, pastePollInterval);
-
+                       
                        util.addEvent(wmd.panels.input, "keydown", handleCtrlYZ);
                        util.addEvent(wmd.panels.input, "keydown", handleModeChange);
-
+                       
                        util.addEvent(wmd.panels.input, "mousedown", function(){
                                setMode("moving");
                        });
                        wmd.panels.input.onpaste = handlePaste;
                        wmd.panels.input.ondrop = handlePaste;
                };
-
+               
                var init = function(){
                        setEventHandlers();
                        refreshState();
                        saveState();
                };
-
+               
                this.destroy = function(){
                        if (poller) {
                                poller.destroy();
                        }
                };
-
+               
                init();
        };
-
+       
        // I think my understanding of how the buttons and callbacks are stored in the array is incomplete.
        wmd.editor = function(previewRefreshCallback){
-
+       
                if (!previewRefreshCallback) {
                        previewRefreshCallback = function(){};
                }
-
+               
                var inputBox = wmd.panels.input;
-
+               
                var offsetHeight = 0;
-
+               
                var editObj = this;
-
+               
                var mainDiv;
                var mainSpan;
-
+               
                var div; // This name is pretty ambiguous.  I should rename this.
-
+               
                // Used to cancel recurring events from setInterval.
                var creationHandle;
-
+               
                var undoMgr; // The undo manager
-
+               
                // Perform the button's action.
                var doClick = function(button){
-
+               
                        inputBox.focus();
-
+                       
                        if (button.textOp) {
-
+                               
                                if (undoMgr) {
                                        undoMgr.setCommandMode();
                                }
-
+                               
                                var state = new wmd.TextareaState();
-
+                               
                                if (!state) {
                                        return;
                                }
-
+                               
                                var chunks = state.getChunks();
-
+                               
                                // Some commands launch a "modal" prompt dialog.  Javascript
                                // can't really make a modal dialog box and the WMD code
                                // will continue to execute while the dialog is displayed.
@@ -901,7 +883,7 @@ Attacklab.wmdBase = function(){
                                //
                                // var link = CreateLinkDialog();
                                // makeMarkdownLink(link);
-                               //
+                               // 
                                // Instead of this straightforward method of handling a
                                // dialog I have to pass any code which would execute
                                // after the dialog is dismissed (e.g. link creation)
@@ -911,64 +893,64 @@ Attacklab.wmdBase = function(){
                                // no real workaround.  Only the image and link code
                                // create dialogs and require the function pointers.
                                var fixupInputArea = function(){
-
+                               
                                        inputBox.focus();
-
+                                       
                                        if (chunks) {
                                                state.setChunks(chunks);
                                        }
-
+                                       
                                        state.restore();
                                        previewRefreshCallback();
                                };
-
+                               
                                var useDefaultText = true;
                                var noCleanup = button.textOp(chunks, fixupInputArea, useDefaultText);
-
+                               
                                if(!noCleanup) {
                                        fixupInputArea();
                                }
-
+                               
                        }
-
+                       
                        if (button.execute) {
                                button.execute(editObj);
                        }
                };
-
+                       
                var setUndoRedoButtonStates = function(){
                        if(undoMgr){
                                setupButton(document.getElementById("wmd-undo-button"), undoMgr.canUndo());
                                setupButton(document.getElementById("wmd-redo-button"), undoMgr.canRedo());
                        }
                };
-
+               
                var setupButton = function(button, isEnabled) {
-
+               
                        var normalYShift = "0px";
                        var disabledYShift = "-20px";
                        var highlightYShift = "-40px";
-
+                       
                        if(isEnabled) {
                                button.style.backgroundPosition = button.XShift + " " + normalYShift;
                                button.onmouseover = function(){
                                        this.style.backgroundPosition = this.XShift + " " + highlightYShift;
                                };
-
+                                                       
                                button.onmouseout = function(){
                                        this.style.backgroundPosition = this.XShift + " " + normalYShift;
                                };
-
+                               
                                // IE tries to select the background image "button" text (it's
                                // implemented in a list item) so we have to cache the selection
                                // on mousedown.
                                if(global.isIE) {
-                                       button.onmousedown =  function() {
+                                       button.onmousedown =  function() { 
                                                wmd.ieRetardedClick = true;
-                                               wmd.ieCachedRange = document.selection.createRange();
+                                               wmd.ieCachedRange = document.selection.createRange(); 
                                        };
                                }
-
+                               
                                if (!button.isHelp)
                                {
                                        button.onclick = function() {
@@ -985,33 +967,33 @@ Attacklab.wmdBase = function(){
                                button.onmouseover = button.onmouseout = button.onclick = function(){};
                        }
                }
-
+       
                var makeSpritedButtonRow = function(){
-
+                       
                        var buttonBar = document.getElementById("wmd-button-bar");
-
+       
                        var normalYShift = "0px";
                        var disabledYShift = "-20px";
                        var highlightYShift = "-40px";
-
+                       
                        var buttonRow = document.createElement("ul");
                        buttonRow.id = "wmd-button-row";
                        buttonRow = buttonBar.appendChild(buttonRow);
 
-
+                       
                        var boldButton = document.createElement("li");
                        boldButton.className = "wmd-button";
                        boldButton.id = "wmd-bold-button";
-                       boldButton.title = toolbar_strong_label;
+                       boldButton.title = "Strong <strong> Ctrl+B";
                        boldButton.XShift = "0px";
                        boldButton.textOp = command.doBold;
                        setupButton(boldButton, true);
                        buttonRow.appendChild(boldButton);
-
+                       
                        var italicButton = document.createElement("li");
                        italicButton.className = "wmd-button";
                        italicButton.id = "wmd-italic-button";
-                       italicButton.title = toolbar_emphasis_label;
+                       italicButton.title = "Emphasis <em> Ctrl+I";
                        italicButton.XShift = "-20px";
                        italicButton.textOp = command.doItalic;
                        setupButton(italicButton, true);
@@ -1020,12 +1002,12 @@ Attacklab.wmdBase = function(){
                        var spacer1 = document.createElement("li");
                        spacer1.className = "wmd-spacer";
                        spacer1.id = "wmd-spacer1";
-                       buttonRow.appendChild(spacer1);
+                       buttonRow.appendChild(spacer1); 
 
                        var linkButton = document.createElement("li");
                        linkButton.className = "wmd-button";
                        linkButton.id = "wmd-link-button";
-                       linkButton.title = toolbar_hyperlink_label;
+                       linkButton.title = "Hyperlink <a> Ctrl+L";
                        linkButton.XShift = "-40px";
                        linkButton.textOp = function(chunk, postProcessing, useDefaultText){
                                return command.doLinkOrImage(chunk, postProcessing, false);
@@ -1036,16 +1018,16 @@ Attacklab.wmdBase = function(){
                        var quoteButton = document.createElement("li");
                        quoteButton.className = "wmd-button";
                        quoteButton.id = "wmd-quote-button";
-                       quoteButton.title = toolbar_blockquote_label;
+                       quoteButton.title = "Blockquote <blockquote> Ctrl+Q";
                        quoteButton.XShift = "-60px";
                        quoteButton.textOp = command.doBlockquote;
                        setupButton(quoteButton, true);
                        buttonRow.appendChild(quoteButton);
-
+                       
                        var codeButton = document.createElement("li");
                        codeButton.className = "wmd-button";
                        codeButton.id = "wmd-code-button";
-                       codeButton.title = toolbar_code_label;
+                       codeButton.title = "Code Sample <pre><code> Ctrl+K";
                        codeButton.XShift = "-80px";
                        codeButton.textOp = command.doCode;
                        setupButton(codeButton, true);
@@ -1054,7 +1036,7 @@ Attacklab.wmdBase = function(){
                        var imageButton = document.createElement("li");
                        imageButton.className = "wmd-button";
                        imageButton.id = "wmd-image-button";
-                       imageButton.title = toolbar_image_label;
+                       imageButton.title = "Image <img> Ctrl+G";
                        imageButton.XShift = "-100px";
                        imageButton.textOp = function(chunk, postProcessing, useDefaultText){
                                return command.doLinkOrImage(chunk, postProcessing, true);
@@ -1065,122 +1047,129 @@ Attacklab.wmdBase = function(){
                        var spacer2 = document.createElement("li");
                        spacer2.className = "wmd-spacer";
                        spacer2.id = "wmd-spacer2";
-                       buttonRow.appendChild(spacer2);
+                       buttonRow.appendChild(spacer2); 
 
                        var olistButton = document.createElement("li");
                        olistButton.className = "wmd-button";
                        olistButton.id = "wmd-olist-button";
-                       olistButton.title = toolbar_numbered_label;
+                       olistButton.title = "Numbered List <ol> Ctrl+O";
                        olistButton.XShift = "-120px";
                        olistButton.textOp = function(chunk, postProcessing, useDefaultText){
                                command.doList(chunk, postProcessing, true, useDefaultText);
                        };
                        setupButton(olistButton, true);
                        buttonRow.appendChild(olistButton);
-
+                       
                        var ulistButton = document.createElement("li");
                        ulistButton.className = "wmd-button";
                        ulistButton.id = "wmd-ulist-button";
-                       ulistButton.title = toolbar_bulleted_label;
+                       ulistButton.title = "Bulleted List <ul> Ctrl+U";
                        ulistButton.XShift = "-140px";
                        ulistButton.textOp = function(chunk, postProcessing, useDefaultText){
                                command.doList(chunk, postProcessing, false, useDefaultText);
                        };
                        setupButton(ulistButton, true);
                        buttonRow.appendChild(ulistButton);
-
+                       
                        var headingButton = document.createElement("li");
                        headingButton.className = "wmd-button";
                        headingButton.id = "wmd-heading-button";
-                       headingButton.title = toolbar_heading_label;
+                       headingButton.title = "Heading <h1>/<h2> Ctrl+H";
                        headingButton.XShift = "-160px";
                        headingButton.textOp = command.doHeading;
                        setupButton(headingButton, true);
-                       buttonRow.appendChild(headingButton);
-
+                       buttonRow.appendChild(headingButton); 
+                       
                        var hrButton = document.createElement("li");
                        hrButton.className = "wmd-button";
                        hrButton.id = "wmd-hr-button";
-                       hrButton.title = toolbar_horizontal_label;
+                       hrButton.title = "Horizontal Rule <hr> Ctrl+R";
                        hrButton.XShift = "-180px";
                        hrButton.textOp = command.doHorizontalRule;
                        setupButton(hrButton, true);
-                       buttonRow.appendChild(hrButton);
-
+                       buttonRow.appendChild(hrButton); 
+                       
                        var spacer3 = document.createElement("li");
                        spacer3.className = "wmd-spacer";
                        spacer3.id = "wmd-spacer3";
-                       buttonRow.appendChild(spacer3);
-
+                       buttonRow.appendChild(spacer3); 
+                       
                        var undoButton = document.createElement("li");
                        undoButton.className = "wmd-button";
                        undoButton.id = "wmd-undo-button";
-                       undoButton.title = toolbar_undo_label;
+                       undoButton.title = "Undo - Ctrl+Z";
                        undoButton.XShift = "-200px";
                        undoButton.execute = function(manager){
                                manager.undo();
                        };
                        setupButton(undoButton, true);
-                       buttonRow.appendChild(undoButton);
-
+                       buttonRow.appendChild(undoButton); 
+                       
                        var redoButton = document.createElement("li");
                        redoButton.className = "wmd-button";
                        redoButton.id = "wmd-redo-button";
-                       redoButton.title = toolbar_redo_label;
+                       redoButton.title = "Redo - Ctrl+Y";
                        if (/win/.test(nav.platform.toLowerCase())) {
-                               redoButton.title = toolbar_redo_label;
+                               redoButton.title = "Redo - Ctrl+Y";
                        }
                        else {
                                // mac and other non-Windows platforms
-                               redoButton.title = $.i18n._('redo') + " - Ctrl+Shift+Z";
+                               redoButton.title = "Redo - Ctrl+Shift+Z";
                        }
                        redoButton.XShift = "-220px";
                        redoButton.execute = function(manager){
                                manager.redo();
                        };
                        setupButton(redoButton, true);
-                       buttonRow.appendChild(redoButton);
-
+                       buttonRow.appendChild(redoButton); 
+                       
+                       var helpButton = document.createElement("li");
+                       helpButton.className = "wmd-button";
+                       helpButton.id = "wmd-help-button";
+                       helpButton.XShift = "-240px";
+                       helpButton.isHelp = true;
+                       
+                       var helpAnchor = document.createElement("a");
+                       helpAnchor.href = helpLink;
+                       helpAnchor.target = helpTarget
+                       helpAnchor.title = helpHoverTitle;
+                       helpButton.appendChild(helpAnchor);
+                       
+                       setupButton(helpButton, true);
+                       buttonRow.appendChild(helpButton);
+                       
                        setUndoRedoButtonStates();
                }
-
+               
                var setupEditor = function(){
-
+               
                        if (/\?noundo/.test(doc.location.href)) {
                                wmd.nativeUndo = true;
                        }
-
+                       
                        if (!wmd.nativeUndo) {
                                undoMgr = new wmd.undoManager(function(){
                                        previewRefreshCallback();
                                        setUndoRedoButtonStates();
                                });
                        }
-
+                       
                        makeSpritedButtonRow();
-
-
+                       
+                       
                        var keyEvent = "keydown";
                        if (global.isOpera) {
                                keyEvent = "keypress";
                        }
-
+                       
                        util.addEvent(inputBox, keyEvent, function(key){
-
+                               
                                // Check to see if we have a button key and, if so execute the callback.
                                if (key.ctrlKey || key.metaKey) {
-
+                       
                                        var keyCode = key.charCode || key.keyCode;
                                        var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
-
-                                       // Bugfix for messed up DEL and .
-                                       if (keyCode === 46) {
-                                               keyCodeStr = "";
-                                       }
-                                       if (keyCode === 190) {
-                                               keyCodeStr = ".";
-                                       }
-
+                                       
                                        switch(keyCodeStr) {
                                                case "b":
                                                        doClick(document.getElementById("wmd-bold-button"));
@@ -1226,18 +1215,18 @@ Attacklab.wmdBase = function(){
                                                default:
                                                        return;
                                        }
-
+                                       
 
                                        if (key.preventDefault) {
                                                key.preventDefault();
                                        }
-
+                                       
                                        if (top.event) {
                                                top.event.returnValue = false;
                                        }
                                }
                        });
-
+                       
                        // Auto-continue lists, code blocks and block quotes when
                        // the enter key is pressed.
                        util.addEvent(inputBox, "keyup", function(key){
@@ -1251,7 +1240,7 @@ Attacklab.wmdBase = function(){
                                        }
                                }
                        });
-
+                       
                        // Disable ESC clearing the input textarea on IE
                        if (global.isIE) {
                                util.addEvent(inputBox, "keydown", function(key){
@@ -1262,7 +1251,7 @@ Attacklab.wmdBase = function(){
                                        }
                                });
                        }
-
+                       
                        if (inputBox.form) {
                                var submitCallback = inputBox.form.onsubmit;
                                inputBox.form.onsubmit = function(){
@@ -1273,19 +1262,19 @@ Attacklab.wmdBase = function(){
                                };
                        }
                };
-
+               
                // Convert the contents of the input textarea to HTML in the output/preview panels.
                var convertToHtml = function(){
-
+               
                        if (wmd.showdown) {
                                var markdownConverter = new wmd.showdown.converter();
                        }
                        var text = inputBox.value;
-
+                       
                        var callback = function(){
                                inputBox.value = text;
                        };
-
+                       
                        if (!/markdown/.test(wmd.wmd_env.output.toLowerCase())) {
                                if (markdownConverter) {
                                        inputBox.value = markdownConverter.makeHtml(text);
@@ -1294,26 +1283,26 @@ Attacklab.wmdBase = function(){
                        }
                        return true;
                };
-
-
+               
+               
                this.undo = function(){
                        if (undoMgr) {
                                undoMgr.undo();
                        }
                };
-
+               
                this.redo = function(){
                        if (undoMgr) {
                                undoMgr.redo();
                        }
                };
-
+               
                // This is pretty useless.  The setupEditor function contents
                // should just be copied here.
                var init = function(){
                        setupEditor();
                };
-
+               
                this.destroy = function(){
                        if (undoMgr) {
                                undoMgr.destroy();
@@ -1326,53 +1315,53 @@ Attacklab.wmdBase = function(){
                        }
                        top.clearInterval(creationHandle);
                };
-
+               
                init();
        };
-
+       
        // The input textarea state/contents.
        // This is used to implement undo/redo by the undo manager.
        wmd.TextareaState = function(){
-
+       
                // Aliases
                var stateObj = this;
                var inputArea = wmd.panels.input;
-
+               
                this.init = function() {
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                               
                        this.setInputAreaSelectionStartEnd();
                        this.scrollTop = inputArea.scrollTop;
                        if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) {
                                this.text = inputArea.value;
                        }
-
-               };
-
+                       
+               }
+               
                // Sets the selected text in the input box after we've performed an
                // operation.
                this.setInputAreaSelection = function(){
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                       
                        if (inputArea.selectionStart !== undefined && !global.isOpera) {
-
+                       
                                inputArea.focus();
                                inputArea.selectionStart = stateObj.start;
                                inputArea.selectionEnd = stateObj.end;
                                inputArea.scrollTop = stateObj.scrollTop;
                        }
                        else if (doc.selection) {
-
+                               
                                if (doc.activeElement && doc.activeElement !== inputArea) {
                                        return;
                                }
-
+                                       
                                inputArea.focus();
                                var range = inputArea.createTextRange();
                                range.moveStart("character", -inputArea.value.length);
@@ -1382,18 +1371,18 @@ Attacklab.wmdBase = function(){
                                range.select();
                        }
                };
-
+               
                this.setInputAreaSelectionStartEnd = function(){
-
+               
                        if (inputArea.selectionStart || inputArea.selectionStart === 0) {
-
+                       
                                stateObj.start = inputArea.selectionStart;
                                stateObj.end = inputArea.selectionEnd;
                        }
                        else if (doc.selection) {
-
+                               
                                stateObj.text = util.fixEolChars(inputArea.value);
-
+                               
                                // IE loses the selection in the textarea when buttons are
                                // clicked.  On IE we cache the selection and set a flag
                                // which we check for here.
@@ -1411,15 +1400,15 @@ Attacklab.wmdBase = function(){
                                var markedRange = marker + fixedRange + marker;
                                range.text = markedRange;
                                var inputText = util.fixEolChars(inputArea.value);
-
+                                       
                                range.moveStart("character", -markedRange.length);
                                range.text = fixedRange;
 
                                stateObj.start = inputText.indexOf(marker);
                                stateObj.end = inputText.lastIndexOf(marker) - marker.length;
-
+                                       
                                var len = stateObj.text.length - util.fixEolChars(inputArea.value).length;
-
+                                       
                                if (len) {
                                        range.moveStart("character", -fixedRange.length);
                                        while (len--) {
@@ -1428,48 +1417,48 @@ Attacklab.wmdBase = function(){
                                        }
                                        range.text = fixedRange;
                                }
-
+                                       
                                this.setInputAreaSelection();
                        }
                };
-
+               
                // Restore this state into the input area.
                this.restore = function(){
-
+               
                        if (stateObj.text != undefined && stateObj.text != inputArea.value) {
                                inputArea.value = stateObj.text;
                        }
                        this.setInputAreaSelection();
                        inputArea.scrollTop = stateObj.scrollTop;
                };
-
+               
                // Gets a collection of HTML chunks from the inptut textarea.
                this.getChunks = function(){
-
+               
                        var chunk = new wmd.Chunks();
-
+                       
                        chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start));
                        chunk.startTag = "";
                        chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end));
                        chunk.endTag = "";
                        chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end));
                        chunk.scrollTop = stateObj.scrollTop;
-
+                       
                        return chunk;
                };
-
+               
                // Sets the TextareaState properties given a chunk of markdown.
                this.setChunks = function(chunk){
-
+               
                        chunk.before = chunk.before + chunk.startTag;
                        chunk.after = chunk.endTag + chunk.after;
-
+                       
                        if (global.isOpera) {
                                chunk.before = chunk.before.replace(/\n/g, "\r\n");
                                chunk.selection = chunk.selection.replace(/\n/g, "\r\n");
                                chunk.after = chunk.after.replace(/\n/g, "\r\n");
                        }
-
+                       
                        this.start = chunk.before.length;
                        this.end = chunk.before.length + chunk.selection.length;
                        this.text = chunk.before + chunk.selection + chunk.after;
@@ -1478,42 +1467,42 @@ Attacklab.wmdBase = function(){
 
                this.init();
        };
-
+       
        // before: contains all the text in the input box BEFORE the selection.
        // after: contains all the text in the input box AFTER the selection.
        wmd.Chunks = function(){
        };
-
+       
        // startRegex: a regular expression to find the start tag
        // endRegex: a regular expresssion to find the end tag
        wmd.Chunks.prototype.findTags = function(startRegex, endRegex){
-
+       
                var chunkObj = this;
                var regex;
-
+               
                if (startRegex) {
-
+                       
                        regex = util.extendRegExp(startRegex, "", "$");
-
-                       this.before = this.before.replace(regex,
+                       
+                       this.before = this.before.replace(regex, 
                                function(match){
                                        chunkObj.startTag = chunkObj.startTag + match;
                                        return "";
                                });
-
+                       
                        regex = util.extendRegExp(startRegex, "^", "");
-
-                       this.selection = this.selection.replace(regex,
+                       
+                       this.selection = this.selection.replace(regex, 
                                function(match){
                                        chunkObj.startTag = chunkObj.startTag + match;
                                        return "";
                                });
                }
-
+               
                if (endRegex) {
-
+                       
                        regex = util.extendRegExp(endRegex, "", "$");
-
+                       
                        this.selection = this.selection.replace(regex,
                                function(match){
                                        chunkObj.endTag = match + chunkObj.endTag;
@@ -1521,7 +1510,7 @@ Attacklab.wmdBase = function(){
                                });
 
                        regex = util.extendRegExp(endRegex, "^", "");
-
+                       
                        this.after = this.after.replace(regex,
                                function(match){
                                        chunkObj.endTag = match + chunkObj.endTag;
@@ -1529,43 +1518,47 @@ Attacklab.wmdBase = function(){
                                });
                }
        };
-
+       
        // If remove is false, the whitespace is transferred
        // to the before/after regions.
        //
        // If remove is true, the whitespace disappears.
        wmd.Chunks.prototype.trimWhitespace = function(remove){
-
+       
                this.selection = this.selection.replace(/^(\s*)/, "");
-
+               
                if (!remove) {
                        this.before += re.$1;
                }
-
+               
                this.selection = this.selection.replace(/(\s*)$/, "");
-
+               
                if (!remove) {
                        this.after = re.$1 + this.after;
                }
        };
-
-
+       
+       
        wmd.Chunks.prototype.addBlankLines = function(nLinesBefore, nLinesAfter, findExtraNewlines){
-
+       
                if (nLinesBefore === undefined) {
                        nLinesBefore = 1;
                }
-
+               
                if (nLinesAfter === undefined) {
                        nLinesAfter = 1;
                }
-
+               
                nLinesBefore++;
                nLinesAfter++;
-
+               
                var regexText;
                var replacementText;
 
+        if (navigator.userAgent.match(/Chrome/)) {
+            "X".match(/()./)
+        }
+               
                this.selection = this.selection.replace(/(^\n*)/, "");
                this.startTag = this.startTag + re.$1;
                this.selection = this.selection.replace(/(\n*$)/, "");
@@ -1574,26 +1567,26 @@ Attacklab.wmdBase = function(){
                this.before = this.before + re.$1;
                this.endTag = this.endTag.replace(/(\n*$)/, "");
                this.after = this.after + re.$1;
-
+               
                if (this.before) {
-
+               
                        regexText = replacementText = "";
-
+                       
                        while (nLinesBefore--) {
                                regexText += "\\n?";
                                replacementText += "\n";
                        }
-
+                       
                        if (findExtraNewlines) {
                                regexText = "\\n*";
                        }
                        this.before = this.before.replace(new re(regexText + "$", ""), replacementText);
                }
-
+               
                if (this.after) {
-
+               
                        regexText = replacementText = "";
-
+                       
                        while (nLinesAfter--) {
                                regexText += "\\n?";
                                replacementText += "\n";
@@ -1601,60 +1594,60 @@ Attacklab.wmdBase = function(){
                        if (findExtraNewlines) {
                                regexText = "\\n*";
                        }
-
+                       
                        this.after = this.after.replace(new re(regexText, ""), replacementText);
                }
        };
-
+       
        // The markdown symbols - 4 spaces = code, > = blockquote, etc.
        command.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";
-
+       
        // Remove markdown symbols from the chunk selection.
        command.unwrap = function(chunk){
                var txt = new re("([^\\n])\\n(?!(\\n|" + command.prefixes + "))", "g");
                chunk.selection = chunk.selection.replace(txt, "$1 $2");
        };
-
+       
        command.wrap = function(chunk, len){
                command.unwrap(chunk);
                var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm");
-
+               
                chunk.selection = chunk.selection.replace(regex, function(line, marked){
                        if (new re("^" + command.prefixes, "").test(line)) {
                                return line;
                        }
                        return marked + "\n";
                });
-
+               
                chunk.selection = chunk.selection.replace(/\s+$/, "");
        };
-
+       
        command.doBold = function(chunk, postProcessing, useDefaultText){
                return command.doBorI(chunk, 2, "strong text");
        };
-
+       
        command.doItalic = function(chunk, postProcessing, useDefaultText){
                return command.doBorI(chunk, 1, "emphasized text");
        };
-
+       
        // chunk: The selected region that will be enclosed with */**
        // nStars: 1 for italics, 2 for bold
        // insertText: If you just click the button without highlighting text, this gets inserted
        command.doBorI = function(chunk, nStars, insertText){
-
-               // Get rid of whitespace and fixup newlines.
+       
+               // Get rid of whitespace and fix up newlines.
                chunk.trimWhitespace();
                chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n");
-
+               
                // Look for stars before and after.  Is the chunk already marked up?
                chunk.before.search(/(\**$)/);
                var starsBefore = re.$1;
-
+               
                chunk.after.search(/(^\**)/);
                var starsAfter = re.$1;
-
+               
                var prevStars = Math.min(starsBefore.length, starsAfter.length);
-
+               
                // Remove stars if we have to since the button acts as a toggle.
                if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) {
                        chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), "");
@@ -1669,26 +1662,26 @@ Attacklab.wmdBase = function(){
                        chunk.before = chunk.before + starsAfter + whitespace;
                }
                else {
-
+               
                        // In most cases, if you don't have any selected text and click the button
                        // you'll get a selected, marked up region with the default text inserted.
                        if (!chunk.selection && !starsAfter) {
                                chunk.selection = insertText;
                        }
-
+                       
                        // Add the true markup.
                        var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ?
                        chunk.before = chunk.before + markup;
                        chunk.after = markup + chunk.after;
                }
-
+               
                return;
        };
-
+       
        command.stripLinkDefs = function(text, defsToAdd){
-
-               text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,
-                       function(totalMatch, id, link, newlines, title){
+       
+               text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, 
+                       function(totalMatch, id, link, newlines, title){        
                                defsToAdd[id] = totalMatch.replace(/\s*$/, "");
                                if (newlines) {
                                        // Strip the title and return that separately.
@@ -1697,95 +1690,102 @@ Attacklab.wmdBase = function(){
                                }
                                return "";
                        });
-
+               
                return text;
        };
-
+       
        command.addLinkDef = function(chunk, linkDef){
-
+       
                var refNumber = 0; // The current reference number
                var defsToAdd = {}; //
                // Start with a clean slate by removing all previous link definitions.
                chunk.before = command.stripLinkDefs(chunk.before, defsToAdd);
                chunk.selection = command.stripLinkDefs(chunk.selection, defsToAdd);
                chunk.after = command.stripLinkDefs(chunk.after, defsToAdd);
-
+               
                var defs = "";
-               var regex = /(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;
-
+               var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;
+        
+        
+               
                var addDefNumber = function(def){
                        refNumber++;
                        def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, "  [" + refNumber + "]:");
                        defs += "\n" + def;
                };
-
-               var getLink = function(wholeMatch, link, id, end){
-
+               
+        // note that
+        // a) the recursive call to getLink cannot go infinite, because by definition
+        //    of regex, inner is always a proper substring of wholeMatch, and
+        // b) more than one level of nesting is neither supported by the regex
+        //    nor making a lot of sense (the only use case for nesting is a linked image)
+        var getLink = function (wholeMatch, before, inner, afterInner, id, end) {
+            inner = inner.replace(regex, getLink);
                        if (defsToAdd[id]) {
                                addDefNumber(defsToAdd[id]);
-                               return link + refNumber + end;
-
+                return before + inner + afterInner + refNumber + end;
+                               
                        }
                        return wholeMatch;
                };
-
+               
                chunk.before = chunk.before.replace(regex, getLink);
-
+               
                if (linkDef) {
                        addDefNumber(linkDef);
                }
                else {
                        chunk.selection = chunk.selection.replace(regex, getLink);
                }
-
+               
                var refOut = refNumber;
-
+               
                chunk.after = chunk.after.replace(regex, getLink);
-
+               
                if (chunk.after) {
                        chunk.after = chunk.after.replace(/\n*$/, "");
                }
                if (!chunk.after) {
                        chunk.selection = chunk.selection.replace(/\n*$/, "");
                }
-
+               
                chunk.after += "\n\n" + defs;
-
+               
                return refOut;
        };
-
+       
        command.doLinkOrImage = function(chunk, postProcessing, isImage){
-
+       
                chunk.trimWhitespace();
                chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
-
+               
                if (chunk.endTag.length > 1) {
-
+               
                        chunk.startTag = chunk.startTag.replace(/!?\[/, "");
                        chunk.endTag = "";
                        command.addLinkDef(chunk, null);
-
+                       
                }
                else {
-
+               
                        if (/\n\n/.test(chunk.selection)) {
                                command.addLinkDef(chunk, null);
                                return;
                        }
-
+                       
                        // The function to be executed when you enter a link and press OK or Cancel.
                        // Marks up the link and adds the ref.
                        var makeLinkMarkdown = function(link){
-
+                       
                                if (link !== null) {
-
+                               
                                        chunk.startTag = chunk.endTag = "";
                                        var linkDef = " [999]: " + link;
-
+                                       
                                        var num = command.addLinkDef(chunk, linkDef);
                                        chunk.startTag = isImage ? "![" : "[";
                                        chunk.endTag = "][" + num + "]";
-
+                                       
                                        if (!chunk.selection) {
                                                if (isImage) {
                                                        chunk.selection = "alt text";
@@ -1797,10 +1797,9 @@ Attacklab.wmdBase = function(){
                                }
                                postProcessing();
                        };
-
+                       
                        if (isImage) {
-                // add fourth param to identify image window
-                               util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown, 1);
+                               util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown);
                        }
                        else {
                                util.prompt(linkDialogText, linkDefaultText, makeLinkMarkdown);
@@ -1808,15 +1807,15 @@ Attacklab.wmdBase = function(){
                        return true;
                }
        };
-
+       
        util.makeAPI = function(){
                wmd.wmd = {};
                wmd.wmd.editor = wmd.editor;
                wmd.wmd.previewManager = wmd.previewManager;
        };
-
+       
        util.startEditor = function(){
-
+       
                if (wmd.wmd_env.autostart === false) {
                        util.makeAPI();
                        return;
@@ -1824,26 +1823,26 @@ Attacklab.wmdBase = function(){
 
                var edit;               // The editor (buttons + input + outputs) - the main object.
                var previewMgr; // The preview manager.
-
+               
                // Fired after the page has fully loaded.
                var loadListener = function(){
-
+               
                        wmd.panels = new wmd.PanelCollection();
-
+                       
                        previewMgr = new wmd.previewManager();
                        var previewRefreshCallback = previewMgr.refresh;
-
+                                               
                        edit = new wmd.editor(previewRefreshCallback);
-
+                       
                        previewMgr.refresh(true);
-
+                       
                };
-
+               
                util.addEvent(top, "load", loadListener);
        };
-
+       
        wmd.previewManager = function(){
-
+               
                var managerObj = this;
                var converter;
                var poller;
@@ -1853,47 +1852,47 @@ Attacklab.wmdBase = function(){
                var htmlOut;
                var maxDelay = 3000;
                var startType = "delayed"; // The other legal value is "manual"
-
+               
                // Adds event listeners to elements and creates the input poller.
                var setupEvents = function(inputElem, listener){
-
+               
                        util.addEvent(inputElem, "input", listener);
                        inputElem.onpaste = listener;
                        inputElem.ondrop = listener;
-
+                       
                        util.addEvent(inputElem, "keypress", listener);
                        util.addEvent(inputElem, "keydown", listener);
                        // previewPollInterval is set at the top of this file.
                        poller = new wmd.inputPoller(listener, previewPollInterval);
                };
-
+               
                var getDocScrollTop = function(){
-
+               
                        var result = 0;
-
+                       
                        if (top.innerHeight) {
                                result = top.pageYOffset;
                        }
-                       else
+                       else 
                                if (doc.documentElement && doc.documentElement.scrollTop) {
                                        result = doc.documentElement.scrollTop;
                                }
-                               else
+                               else 
                                        if (doc.body) {
                                                result = doc.body.scrollTop;
                                        }
-
+                       
                        return result;
                };
-
+               
                var makePreviewHtml = function(){
-
+               
                        // If there are no registered preview and output panels
                        // there is nothing to do.
                        if (!wmd.panels.preview && !wmd.panels.output) {
                                return;
                        }
-
+                       
                        var text = wmd.panels.input.value;
                        if (text && text == oldInputText) {
                                return; // Input text hasn't changed.
@@ -1901,71 +1900,71 @@ Attacklab.wmdBase = function(){
                        else {
                                oldInputText = text;
                        }
-
+                       
                        var prevTime = new Date().getTime();
-
+                       
                        if (!converter && wmd.showdown) {
                                converter = new wmd.showdown.converter();
                        }
-
+                       
                        if (converter) {
                                text = converter.makeHtml(text);
                        }
-
+                       
                        // Calculate the processing time of the HTML creation.
                        // It's used as the delay time in the event listener.
                        var currTime = new Date().getTime();
                        elapsedTime = currTime - prevTime;
-
+                       
                        pushPreviewHtml(text);
                        htmlOut = text;
                };
-
+               
                // setTimeout is already used.  Used as an event listener.
                var applyTimeout = function(){
-
+               
                        if (timeout) {
                                top.clearTimeout(timeout);
                                timeout = undefined;
                        }
-
+                       
                        if (startType !== "manual") {
-
+                       
                                var delay = 0;
-
+                               
                                if (startType === "delayed") {
                                        delay = elapsedTime;
                                }
-
+                               
                                if (delay > maxDelay) {
                                        delay = maxDelay;
                                }
                                timeout = top.setTimeout(makePreviewHtml, delay);
                        }
                };
-
+               
                var getScaleFactor = function(panel){
                        if (panel.scrollHeight <= panel.clientHeight) {
                                return 1;
                        }
                        return panel.scrollTop / (panel.scrollHeight - panel.clientHeight);
                };
-
+               
                var setPanelScrollTops = function(){
-
+               
                        if (wmd.panels.preview) {
                                wmd.panels.preview.scrollTop = (wmd.panels.preview.scrollHeight - wmd.panels.preview.clientHeight) * getScaleFactor(wmd.panels.preview);
                                ;
                        }
-
+                       
                        if (wmd.panels.output) {
                                wmd.panels.output.scrollTop = (wmd.panels.output.scrollHeight - wmd.panels.output.clientHeight) * getScaleFactor(wmd.panels.output);
                                ;
                        }
                };
-
+               
                this.refresh = function(requiresRefresh){
-
+               
                        if (requiresRefresh) {
                                oldInputText = "";
                                makePreviewHtml();
@@ -1974,28 +1973,28 @@ Attacklab.wmdBase = function(){
                                applyTimeout();
                        }
                };
-
+               
                this.processingTime = function(){
                        return elapsedTime;
                };
-
+               
                // The output HTML
                this.output = function(){
                        return htmlOut;
                };
-
+               
                // The mode can be "manual" or "delayed"
                this.setUpdateMode = function(mode){
                        startType = mode;
                        managerObj.refresh();
                };
-
+               
                var isFirstTimeFilled = true;
-
+               
                var pushPreviewHtml = function(text){
-
+               
                        var emptyTop = position.getTop(wmd.panels.input) - getDocScrollTop();
-
+                       
                        // Send the encoded HTML to the output textarea/div.
                        if (wmd.panels.output) {
                                // The value property is only defined if the output is a textarea.
@@ -2011,20 +2010,20 @@ Attacklab.wmdBase = function(){
                                        wmd.panels.output.innerHTML = "<pre><code>" + newText + "</code></pre>";
                                }
                        }
-
+                       
                        if (wmd.panels.preview) {
                                wmd.panels.preview.innerHTML = text;
                        }
-
+                       
                        setPanelScrollTops();
-
+                       
                        if (isFirstTimeFilled) {
                                isFirstTimeFilled = false;
                                return;
                        }
-
+                       
                        var fullTop = position.getTop(wmd.panels.input) - getDocScrollTop();
-
+                       
                        if (global.isIE) {
                                top.setTimeout(function(){
                                        top.scrollBy(0, fullTop - emptyTop);
@@ -2034,12 +2033,12 @@ Attacklab.wmdBase = function(){
                                top.scrollBy(0, fullTop - emptyTop);
                        }
                };
-
+               
                var init = function(){
-
+               
                        setupEvents(wmd.panels.input, applyTimeout);
                        makePreviewHtml();
-
+                       
                        if (wmd.panels.preview) {
                                wmd.panels.preview.scrollTop = 0;
                        }
@@ -2047,25 +2046,25 @@ Attacklab.wmdBase = function(){
                                wmd.panels.output.scrollTop = 0;
                        }
                };
-
+               
                this.destroy = function(){
                        if (poller) {
                                poller.destroy();
                        }
                };
-
+               
                init();
        };
 
        // Moves the cursor to the next line and continues lists, quotes and code.
        command.doAutoindent = function(chunk, postProcessing, useDefaultText){
-
+               
                chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n");
                chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n");
                chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n");
-
+               
                useDefaultText = false;
-
+               
                if(/(\n|^)[ ]{0,3}([*+-])[ \t]+.*\n$/.test(chunk.before)){
                        if(command.doList){
                                command.doList(chunk, postProcessing, false, true);
@@ -2087,49 +2086,104 @@ Attacklab.wmdBase = function(){
                        }
                }
        };
-
+       
        command.doBlockquote = function(chunk, postProcessing, useDefaultText){
-
+               
                chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,
                        function(totalMatch, newlinesBefore, text, newlinesAfter){
                                chunk.before += newlinesBefore;
                                chunk.after = newlinesAfter + chunk.after;
                                return text;
                        });
-
+                       
                chunk.before = chunk.before.replace(/(>[ \t]*)$/,
                        function(totalMatch, blankLine){
                                chunk.selection = blankLine + chunk.selection;
                                return "";
                        });
-
+               
                var defaultText = useDefaultText ? "Blockquote" : "";
                chunk.selection = chunk.selection.replace(/^(\s|>)+$/ ,"");
                chunk.selection = chunk.selection || defaultText;
-
+               
+        // The original code uses a regular expression to find out how much of the
+        // text *directly before* the selection already was a blockquote:
+        /*
                if(chunk.before){
                        chunk.before = chunk.before.replace(/\n?$/,"\n");
                }
+        chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,
+                       function (totalMatch) {
+                           chunk.startTag = totalMatch;
+                           return "";
+                       });
+        */
+        // This comes down to:
+        // Go backwards as many lines a possible, such that each line
+        //  a) starts with ">", or
+        //  b) is almost empty, except for whitespace, or
+        //  c) is preceeded by an unbroken chain of non-empty lines
+        //     leading up to a line that starts with ">" and at least one more character
+        // and in addition
+        //  d) at least one line fulfills a)
+        //
+        // Since this is essentially a backwards-moving regex, it's susceptible to
+        // catstrophic backtracking and can cause the browser to hang;
+        // see e.g. http://meta.stackoverflow.com/questions/9807.
+        //
+        // Hence we replaced this by a simple state machine that just goes through the
+        // lines and checks for a), b), and c).
+
+        var match = "";
+        var leftOver = "";
+        if (chunk.before) {
+            var lines = chunk.before.replace(/\n$/, "").split("\n");
+            var inChain = false;
+            for (var i in lines) {
+                var good = false;
+                line = lines[i];
+                inChain = inChain && line.length > 0; // c) any non-empty line continues the chain
+                if (/^>/.test(line)) {                // a)
+                    good = true;
+                    if (!inChain && line.length > 1)  // c) any line that starts with ">" and has at least one more character starts the chain
+                        inChain = true;
+                } else if (/^[ \t]*$/.test(line)) {   // b)
+                    good = true;
+                } else {
+                    good = inChain;                   // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain
+                }
+                if (good) {
+                    match += line + "\n";
+                } else {
+                    leftOver += match + line;
+                    match = "\n";
+                }
+            }
+            if (!/(^|\n)>/.test(match)) {             // d)
+                leftOver += match;
+                match = "";
+            }
+        }
+
+        chunk.startTag = match;
+        chunk.before = leftOver;
+
+        // end of change
+        
                if(chunk.after){
                        chunk.after = chunk.after.replace(/^\n?/,"\n");
                }
-
-               chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,
-                       function(totalMatch){
-                               chunk.startTag = totalMatch;
-                               return "";
-                       });
-
+               
                chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,
                        function(totalMatch){
                                chunk.endTag = totalMatch;
                                return "";
                        });
-
+               
                var replaceBlanksInTags = function(useBracket){
-
+                       
                        var replacement = useBracket ? "> " : "";
-
+                       
                        if(chunk.startTag){
                                chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/,
                                        function(totalMatch, markdown){
@@ -2143,7 +2197,7 @@ Attacklab.wmdBase = function(){
                                        });
                        }
                };
-
+               
                if(/^(?![ ]{0,3}>)/m.test(chunk.selection)){
                        command.wrap(chunk, wmd.wmd_env.lineLength - 2);
                        chunk.selection = chunk.selection.replace(/^/gm, "> ");
@@ -2154,16 +2208,16 @@ Attacklab.wmdBase = function(){
                        chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, "");
                        command.unwrap(chunk);
                        replaceBlanksInTags(false);
-
+                       
                        if(!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag){
                                chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n");
                        }
-
+                       
                        if(!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag){
                                chunk.endTag=chunk.endTag.replace(/^\n{0,2}/, "\n\n");
                        }
                }
-
+               
                if(!/\n/.test(chunk.selection)){
                        chunk.selection = chunk.selection.replace(/^(> *)/,
                        function(wholeMatch, blanks){
@@ -2174,33 +2228,33 @@ Attacklab.wmdBase = function(){
        };
 
        command.doCode = function(chunk, postProcessing, useDefaultText){
-
+               
                var hasTextBefore = /\S[ ]*$/.test(chunk.before);
                var hasTextAfter = /^[ ]*\S/.test(chunk.after);
-
+               
                // Use 'four space' markdown if the selection is on its own
                // line or is multiline.
                if((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)){
-
+                       
                        chunk.before = chunk.before.replace(/[ ]{4}$/,
                                function(totalMatch){
                                        chunk.selection = totalMatch + chunk.selection;
                                        return "";
                                });
-
+                               
                        var nLinesBefore = 1;
                        var nLinesAfter = 1;
-
-
+                       
+                       
                        if(/\n(\t|[ ]{4,}).*\n$/.test(chunk.before) || chunk.after === ""){
-                               nLinesBefore = 0;
+                               nLinesBefore = 0; 
                        }
                        if(/^\n(\t|[ ]{4,})/.test(chunk.after)){
                                nLinesAfter = 0; // This needs to happen on line 1
                        }
-
+                       
                        chunk.addBlankLines(nLinesBefore, nLinesAfter);
-
+                       
                        if(!chunk.selection){
                                chunk.startTag = "    ";
                                chunk.selection = useDefaultText ? "enter code here" : "";
@@ -2216,10 +2270,10 @@ Attacklab.wmdBase = function(){
                }
                else{
                        // Use backticks (`) to delimit the code block.
-
+                       
                        chunk.trimWhitespace();
                        chunk.findTags(/`/, /`/);
-
+                       
                        if(!chunk.startTag && !chunk.endTag){
                                chunk.startTag = chunk.endTag="`";
                                if(!chunk.selection){
@@ -2235,22 +2289,22 @@ Attacklab.wmdBase = function(){
                        }
                }
        };
-
+       
        command.doList = function(chunk, postProcessing, isNumberedList, useDefaultText){
-
+                               
                // These are identical except at the very beginning and end.
                // Should probably use the regex extension function to make this clearer.
                var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;
                var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;
-
+               
                // The default bullet is a dash but others are possible.
                // This has nothing to do with the particular HTML bullet,
                // it's just a markdown bullet.
                var bullet = "-";
-
+               
                // The number in a numbered list.
                var num = 1;
-
+               
                // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list.
                var getItemPrefix = function(){
                        var prefix;
@@ -2263,39 +2317,39 @@ Attacklab.wmdBase = function(){
                        }
                        return prefix;
                };
-
+               
                // Fixes the prefixes of the other list items.
                var getPrefixedItem = function(itemText){
-
+               
                        // The numbering flag is unset when called by autoindent.
                        if(isNumberedList === undefined){
                                isNumberedList = /^\s*\d/.test(itemText);
                        }
-
+                       
                        // Renumber/bullet the list element.
                        itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,
                                function( _ ){
                                        return getItemPrefix();
                                });
-
+                               
                        return itemText;
                };
-
+               
                chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null);
-
+               
                if(chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)){
                        chunk.before += chunk.startTag;
                        chunk.startTag = "";
                }
-
+               
                if(chunk.startTag){
-
+                       
                        var hasDigits = /\d+[.]/.test(chunk.startTag);
                        chunk.startTag = "";
                        chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n");
                        command.unwrap(chunk);
                        chunk.addBlankLines();
-
+                       
                        if(hasDigits){
                                // Have to renumber the bullet points if this is a numbered list.
                                chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem);
@@ -2304,9 +2358,9 @@ Attacklab.wmdBase = function(){
                                return;
                        }
                }
-
+               
                var nLinesBefore = 1;
-
+               
                chunk.before = chunk.before.replace(previousItemsRegex,
                        function(itemText){
                                if(/^\s*([*+-])/.test(itemText)){
@@ -2315,36 +2369,36 @@ Attacklab.wmdBase = function(){
                                nLinesBefore = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
                                return getPrefixedItem(itemText);
                        });
-
+                       
                if(!chunk.selection){
                        chunk.selection = useDefaultText ? "List item" : " ";
                }
-
+               
                var prefix = getItemPrefix();
-
+               
                var nLinesAfter = 1;
-
+               
                chunk.after = chunk.after.replace(nextItemsRegex,
                        function(itemText){
                                nLinesAfter = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
                                return getPrefixedItem(itemText);
                        });
-
+                       
                chunk.trimWhitespace(true);
                chunk.addBlankLines(nLinesBefore, nLinesAfter, true);
                chunk.startTag = prefix;
                var spaces = prefix.replace(/./g, " ");
                command.wrap(chunk, wmd.wmd_env.lineLength - spaces.length);
                chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces);
-
+               
        };
-
+       
        command.doHeading = function(chunk, postProcessing, useDefaultText){
-
+               
                // Remove leading/trailing whitespace and reduce internal spaces to single spaces.
                chunk.selection = chunk.selection.replace(/\s+/g, " ");
                chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, "");
-
+               
                // If we clicked the button with no selected text, we just
                // make a level 2 hash header around some default text.
                if(!chunk.selection){
@@ -2353,16 +2407,16 @@ Attacklab.wmdBase = function(){
                        chunk.endTag = " ##";
                        return;
                }
-
+               
                var headerLevel = 0;            // The existing header level of the selected text.
-
+               
                // Remove any existing hash heading markdown and save the header level.
                chunk.findTags(/#+[ ]*/, /[ ]*#+/);
                if(/#+/.test(chunk.startTag)){
                        headerLevel = re.lastMatch.length;
                }
                chunk.startTag = chunk.endTag = "";
-
+               
                // Try to get the current header level by looking for - and = in the line
                // below the selection.
                chunk.findTags(null, /\s?(-+|=+)/);
@@ -2372,7 +2426,7 @@ Attacklab.wmdBase = function(){
                if(/-+/.test(chunk.endTag)){
                        headerLevel = 2;
                }
-
+               
                // Skip to the next line so we can create the header markdown.
                chunk.startTag = chunk.endTag = "";
                chunk.addBlankLines(1, 1);
@@ -2381,9 +2435,9 @@ Attacklab.wmdBase = function(){
                // If there is a header level, we substract one from the header level.
                // If it's already a level 1 header, it's removed.
                var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1;
-
+               
                if(headerLevelToCreate > 0){
-
+                       
                        // The button only creates level 1 and 2 underline headers.
                        // Why not have it iterate over hash header levels?  Wouldn't that be easier and cleaner?
                        var headerChar = headerLevelToCreate >= 2 ? "-" : "=";
@@ -2396,8 +2450,8 @@ Attacklab.wmdBase = function(){
                                chunk.endTag += headerChar;
                        }
                }
-       };
-
+       };      
+       
        command.doHorizontalRule = function(chunk, postProcessing, useDefaultText){
                chunk.startTag = "----------\n";
                chunk.selection = "";
@@ -2408,7 +2462,7 @@ Attacklab.wmdBase = function(){
 
 Attacklab.wmd_env = {};
 Attacklab.account_options = {};
-Attacklab.wmd_defaults = {version:1, output:"Markdown", lineLength:40, delayLoad:false};
+Attacklab.wmd_defaults = {version:1, output:"HTML", lineLength:40, delayLoad:false};
 
 if(!Attacklab.wmd)
 {
@@ -2422,25 +2476,25 @@ if(!Attacklab.wmd)
                                {
                                        return;
                                }
-
+                       
                                for(var key in env)
                                {
                                        Attacklab.wmd_env[key] = env[key];
                                }
                        };
-
+                       
                        mergeEnv(Attacklab.wmd_defaults);
                        mergeEnv(Attacklab.account_options);
                        mergeEnv(top["wmd_options"]);
                        Attacklab.full = true;
-
+                       
                        var defaultButtons = "bold italic link blockquote code image ol ul heading hr";
                        Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons;
                };
                Attacklab.loadEnv();
 
        };
-
+       
        Attacklab.wmd();
        Attacklab.wmdBase();
        Attacklab.Util.startEditor();
index 8e9c5446845011fc7647366fa16887321e7f3050..60fc30fe4dee2a8bffb9514fda264576af0b6d0b 100644 (file)
@@ -2,6 +2,8 @@
 import os.path
 import time, datetime, random
 import logging
+
+from django.views.decorators.csrf import csrf_exempt
 from django.core.files.storage import FileSystemStorage
 from django.shortcuts import render_to_response, get_object_or_404
 from django.http import HttpResponseRedirect, HttpResponse, Http404
@@ -10,17 +12,19 @@ from django.utils.html import *
 from django.utils import simplejson
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import reverse
-from django.core.exceptions import PermissionDenied
 
+from django.core.exceptions import PermissionDenied
 from forum.actions import AskAction, AnswerAction, ReviseAction, RollbackAction, RetagAction, AnswerToQuestionAction, CommentToQuestionAction
 from forum.forms import *
 from forum.models import *
 from forum.forms import get_next_url
 from forum.utils import html
+
 from forum.http_responses import HttpResponseUnauthorized
 
 from vars import PENDING_SUBMISSION_SESSION_ATTR
 
+@csrf_exempt
 def upload(request):#ajax upload file to a question or answer
     class FileTypeNotAllow(Exception):
         pass