]> git.openstreetmap.org Git - osqa.git/commitdiff
bugfix, the images in the preview mode of the WMD editor were not displayed, because...
authorjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Tue, 19 Apr 2011 19:38:32 +0000 (19:38 +0000)
committerjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Tue, 19 Apr 2011 19:38:32 +0000 (19:38 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@994 0cfe37f9-358a-4d5e-be75-b63607b5c754

forum/skins/default/media/js/wmd/showdown.js
forum/skins/default/media/js/wmd/wmd.js

index 330550d34913d139e2318a76993c78f1c928efd2..d223f45dca49891b16280e4e44f1871d68c6c364 100644 (file)
@@ -192,7 +192,7 @@ var _StripLinkDefinitions = function(text) {
                        } else if (m4) {
                                g_titles[m1] = m4.replace(/"/g,"&quot;");
                        }
-                       
+
                        // Completely remove the definition from the text
                        return "";
                }
@@ -264,7 +264,7 @@ 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(/
@@ -273,7 +273,7 @@ var _HashHTMLBlocks = function(text) {
                        [ ]{0,3}
                        (<(hr)                          // start tag = $2
                        \b                                      // word break
-                       ([^<>])*?                       // 
+                       ([^<>])*?                       //
                        \/?>)                           // the matching end tag
                        [ \t]*
                        (?=\n{2,})                      // followed by a blank line
@@ -331,13 +331,13 @@ var hashElement = function(wholeMatch,m1) {
        // Undo double lines
        blockText = blockText.replace(/\n\n/g,"\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;
 };
 
@@ -403,7 +403,7 @@ 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 
+       // 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;
 
@@ -508,14 +508,14 @@ 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) {
@@ -530,19 +530,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;
 }
 
@@ -613,14 +613,14 @@ 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) {
@@ -630,13 +630,14 @@ var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
                else {
                        return whole_match;
                }
-       }       
-       
+       }
+
        alt_text = alt_text.replace(/"/g,"&quot;");
        url = escapeCharacters(url,"*_");
-       url = scriptUrl + url
+    if (url.toString().indexOf('http://') != 0 && url.toString().indexOf('https://') != 0) {
+        url = scriptUrl + url
+    }
        var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
-       result = result.replace("//", "/")
 
        // attacklab: Markdown.pl adds empty title attributes to images.
        // Replicate this bug.
@@ -646,9 +647,9 @@ var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
                title = escapeCharacters(title,"*_");
                result +=  " title=\"" + title + "\"";
        //}
-       
+
        result += " />";
-       
+
        return result;
 }
 
@@ -658,7 +659,7 @@ var _DoHeaders = function(text) {
        // Setext-style headers:
        //      Header 1
        //      ========
-       //  
+       //
        //      Header 2
        //      --------
        //
@@ -742,7 +743,7 @@ var _DoLists = function(text) {
                        // paragraph for the last item in a list, if necessary:
                        list = list.replace(/\n{2,}/g,"\n\n\n");;
                        var result = _ProcessListItems(list);
-       
+
                        // 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
@@ -762,7 +763,7 @@ var _DoLists = function(text) {
                        // 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";   
+                       result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
                        return result;
                });
        }
@@ -848,7 +849,7 @@ _ProcessListItems = function(list_str) {
 var _DoCodeBlocks = function(text) {
 //
 //  Process Markdown `<pre><code>` blocks.
-//  
+//
 
        /*
                text = text.replace(text,
@@ -865,12 +866,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
@@ -897,26 +898,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> ...
 //
 
@@ -1019,7 +1020,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(
@@ -1031,7 +1032,7 @@ var _DoBlockQuotes = function(text) {
                                        pre = pre.replace(/~0/g,"");
                                        return pre;
                                });
-                       
+
                        return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
                });
        return text;
@@ -1090,14 +1091,14 @@ var _FormParagraphs = function(text) {
 
 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;
 }
 
index f54158185726291aa831b72f887845baddab1fc3..a0770119679bf0ea0c26f40bb96d5a599f1261d3 100644 (file)
@@ -51,26 +51,26 @@ 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";
@@ -87,10 +87,10 @@ Attacklab.wmdBase = function(){
        // -------------------------------------------------------------------
        //  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>";
@@ -100,28 +100,28 @@ Attacklab.wmdBase = function(){
             "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 +130,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 +149,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 +163,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 +178,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 +208,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 +217,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 +230,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 +261,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 +276,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 +285,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 +301,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) {
                                        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 +339,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 +351,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 +365,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,7 +383,7 @@ Attacklab.wmdBase = function(){
                        style.textAlign = "center";
                        style.position = "relative";
                        dialog.appendChild(form);
-                       
+
                        // The input text box
                        input = doc.createElement("input");
             if(type == 1){
@@ -396,7 +396,7 @@ Attacklab.wmdBase = function(){
                        style.width = "80%";
                        style.marginLeft = style.marginRight = "auto";
                        form.appendChild(input);
-                       
+
             // The upload file input
             if(type == 1){
                 var upload = doc.createElement("div");
@@ -415,7 +415,7 @@ Attacklab.wmdBase = function(){
                        style.display = "inline";
                        style.width = "7em";
 
-                       
+
                        // The cancel button
                        var cancelButton = doc.createElement("input");
                        cancelButton.type = "button";
@@ -446,20 +446,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 +474,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 +492,7 @@ Attacklab.wmdBase = function(){
                }
                return result;
        };
-       
+
        position.getHeight = function (elem) {
                return elem.offsetHeight || elem.scrollHeight;
        };
@@ -502,10 +502,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 +519,7 @@ Attacklab.wmdBase = function(){
                        scrollWidth = doc.body.offsetWidth;
                        scrollHeight = doc.body.offsetHeight;
                }
-               
+
                if(self.innerHeight){
                        // Non-IE browser
                        innerWidth = self.innerWidth;
@@ -535,33 +535,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 +569,7 @@ Attacklab.wmdBase = function(){
                                if (start != lastStart || end != lastEnd) {
                                        lastStart = start;
                                        lastEnd = end;
-                                       
+
                                        if (markdown != inputArea.value) {
                                                markdown = inputArea.value;
                                                return true;
@@ -578,37 +578,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 +617,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 +635,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 +671,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 +726,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 +755,7 @@ Attacklab.wmdBase = function(){
                                                break;
                                }
                        }
-                       
+
                        if (handled) {
                                if (event.preventDefault) {
                                        event.preventDefault();
@@ -766,14 +766,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 +794,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 +802,9 @@ Attacklab.wmdBase = function(){
                                }
                        }
                };
-               
+
                var setEventHandlers = function(){
-               
+
                        util.addEvent(wmd.panels.input, "keypress", function(event){
                                // keyCode 89: y
                                // keyCode 90: z
@@ -812,7 +812,7 @@ Attacklab.wmdBase = function(){
                                        event.preventDefault();
                                }
                        });
-                       
+
                        var handlePaste = function(){
                                if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) {
                                        if (timer == undefined) {
@@ -822,77 +822,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 +901,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 +911,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,20 +985,20 @@ 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";
@@ -1007,7 +1007,7 @@ Attacklab.wmdBase = function(){
                        boldButton.textOp = command.doBold;
                        setupButton(boldButton, true);
                        buttonRow.appendChild(boldButton);
-                       
+
                        var italicButton = document.createElement("li");
                        italicButton.className = "wmd-button";
                        italicButton.id = "wmd-italic-button";
@@ -1020,7 +1020,7 @@ 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";
@@ -1041,7 +1041,7 @@ Attacklab.wmdBase = function(){
                        quoteButton.textOp = command.doBlockquote;
                        setupButton(quoteButton, true);
                        buttonRow.appendChild(quoteButton);
-                       
+
                        var codeButton = document.createElement("li");
                        codeButton.className = "wmd-button";
                        codeButton.id = "wmd-code-button";
@@ -1065,7 +1065,7 @@ 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";
@@ -1077,7 +1077,7 @@ Attacklab.wmdBase = function(){
                        };
                        setupButton(olistButton, true);
                        buttonRow.appendChild(olistButton);
-                       
+
                        var ulistButton = document.createElement("li");
                        ulistButton.className = "wmd-button";
                        ulistButton.id = "wmd-ulist-button";
@@ -1088,7 +1088,7 @@ Attacklab.wmdBase = function(){
                        };
                        setupButton(ulistButton, true);
                        buttonRow.appendChild(ulistButton);
-                       
+
                        var headingButton = document.createElement("li");
                        headingButton.className = "wmd-button";
                        headingButton.id = "wmd-heading-button";
@@ -1096,8 +1096,8 @@ Attacklab.wmdBase = function(){
                        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";
@@ -1105,13 +1105,13 @@ Attacklab.wmdBase = function(){
                        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";
@@ -1121,8 +1121,8 @@ Attacklab.wmdBase = function(){
                                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";
@@ -1139,40 +1139,40 @@ Attacklab.wmdBase = function(){
                                manager.redo();
                        };
                        setupButton(redoButton, true);
-                       buttonRow.appendChild(redoButton); 
-                       
+                       buttonRow.appendChild(redoButton);
+
                        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 = "";
@@ -1226,18 +1226,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 +1251,7 @@ Attacklab.wmdBase = function(){
                                        }
                                }
                        });
-                       
+
                        // Disable ESC clearing the input textarea on IE
                        if (global.isIE) {
                                util.addEvent(inputBox, "keydown", function(key){
@@ -1262,7 +1262,7 @@ Attacklab.wmdBase = function(){
                                        }
                                });
                        }
-                       
+
                        if (inputBox.form) {
                                var submitCallback = inputBox.form.onsubmit;
                                inputBox.form.onsubmit = function(){
@@ -1273,19 +1273,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 +1294,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 +1326,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 +1382,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 +1411,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 +1428,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 +1478,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 +1521,7 @@ Attacklab.wmdBase = function(){
                                });
 
                        regex = util.extendRegExp(endRegex, "^", "");
-                       
+
                        this.after = this.after.replace(regex,
                                function(match){
                                        chunkObj.endTag = match + chunkObj.endTag;
@@ -1529,43 +1529,43 @@ 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;
-               
+
                this.selection = this.selection.replace(/(^\n*)/, "");
                this.startTag = this.startTag + re.$1;
                this.selection = this.selection.replace(/(\n*$)/, "");
@@ -1574,26 +1574,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 +1601,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.
                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 +1669,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 +1697,95 @@ 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 addDefNumber = function(def){
                        refNumber++;
                        def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, "  [" + refNumber + "]:");
                        defs += "\n" + def;
                };
-               
+
                var getLink = function(wholeMatch, link, id, end){
-               
+
                        if (defsToAdd[id]) {
                                addDefNumber(defsToAdd[id]);
                                return link + 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,7 +1797,7 @@ Attacklab.wmdBase = function(){
                                }
                                postProcessing();
                        };
-                       
+
                        if (isImage) {
                 // add fourth param to identify image window
                                util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown, 1);
@@ -1808,15 +1808,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 +1824,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 +1853,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 +1901,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 +1974,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 +2011,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 +2034,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 +2047,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 +2087,49 @@ 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;
-               
+
                if(chunk.before){
                        chunk.before = chunk.before.replace(/\n?$/,"\n");
                }
                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 +2143,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 +2154,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 +2174,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 +2216,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 +2235,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 +2263,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 +2304,9 @@ Attacklab.wmdBase = function(){
                                return;
                        }
                }
-               
+
                var nLinesBefore = 1;
-               
+
                chunk.before = chunk.before.replace(previousItemsRegex,
                        function(itemText){
                                if(/^\s*([*+-])/.test(itemText)){
@@ -2315,36 +2315,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 +2353,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 +2372,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 +2381,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 +2396,8 @@ Attacklab.wmdBase = function(){
                                chunk.endTag += headerChar;
                        }
                }
-       };      
-       
+       };
+
        command.doHorizontalRule = function(chunk, postProcessing, useDefaultText){
                chunk.startTag = "----------\n";
                chunk.selection = "";
@@ -2422,25 +2422,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();