]> git.openstreetmap.org Git - osqa.git/blobdiff - forum/skins/default/media/js/wmd/wmd.js
OSQA-720, changing the WMD markdown editor, applying patches that resolve issues...
[osqa.git] / forum / skins / default / media / js / wmd / wmd.js
index a0770119679bf0ea0c26f40bb96d5a599f1261d3..678776d1e2a1c9241763eaa869202773ecfab570 100644 (file)
@@ -51,77 +51,64 @@ Attacklab.wmdBase = function(){
        var doc  = top.document;
        var re   = top.RegExp;
        var nav  = top.navigator;
-
+       
        // Some namespaces.
        wmd.Util = {};
        wmd.Position = {};
        wmd.Command = {};
        wmd.Global = {};
-
+       
        var util = wmd.Util;
        var position = wmd.Position;
        var command = wmd.Command;
        var global = wmd.Global;
-
-
+       
+       
        // Used to work around some browser bugs where we can't use feature testing.
        global.isIE             = /msie/.test(nav.userAgent.toLowerCase());
        global.isIE_5or6        = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
        global.isIE_7plus       = global.isIE && !global.isIE_5or6;
        global.isOpera          = /opera/.test(nav.userAgent.toLowerCase());
        global.isKonqueror      = /konqueror/.test(nav.userAgent.toLowerCase());
-
-       var toolbar_strong_label = $.i18n._('bold') + " <strong> Ctrl-B";
-    var toolbar_emphasis_label = $.i18n._('italic') + " <em> Ctrl-I";
-    var toolbar_hyperlink_label = $.i18n._('link') + " <a> Ctrl-L";
-    var toolbar_blockquote_label = $.i18n._('quote') + " <blockquote> Ctrl-.";
-    var toolbar_code_label = $.i18n._('preformatted text') + " <pre><code> Ctrl-K";
-    var toolbar_image_label = $.i18n._('image') + " <img> Ctrl-G";
-    var toolbar_numbered_label = $.i18n._('numbered list') + " <ol> Ctrl-O";
-    var toolbar_bulleted_label = $.i18n._('bulleted list') + " <ul> Ctrl-U";
-    var toolbar_heading_label = $.i18n._('heading') + " <h1>/<h2> Ctrl-H";
-    var toolbar_horizontal_label = $.i18n._('horizontal bar') + " <hr> Ctrl-R";
-    var toolbar_undo_label = $.i18n._('undo') + " Ctrl-Z";
-    var toolbar_redo_label = $.i18n._('redo') + " Ctrl-Y";
-
+       
+       
        // -------------------------------------------------------------------
        //  YOUR CHANGES GO HERE
        //
-       // I've tried to localize the things you are likely to change to
+       // I've tried to localize the things you are likely to change to 
        // this area.
        // -------------------------------------------------------------------
-
+       
        // The text that appears on the upper part of the dialog box when
        // entering links.
-       var imageDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter image url') + "</p>";
-       var linkDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter url') + "</p>";
+    var imageDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter image url') + "</p>";
+    var linkDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter url') + "</p>";
     var uploadImageHTML ="<div>" + $.i18n._('upload image') + "</div>" +
             "<input type=\"file\" name=\"file-upload\" id=\"file-upload\" size=\"26\" "+
             "onchange=\"return ajaxFileUpload($('#image-url'));\"/><br>" +
             "<img id=\"loading\" src=\"" + mediaUrl("media/images/indicator.gif") + "\" style=\"display:none;\"/>";
 
-
        // The default text that appears in the dialog input box when entering
        // links.
        var imageDefaultText = "http://";
        var linkDefaultText = "http://";
-
+       
        // The location of your button images relative to the base directory.
        var imageDirectory = "images/";
-
+       
        // Some intervals in ms.  These can be adjusted to reduce the control's load.
        var previewPollInterval = 500;
        var pastePollInterval = 100;
-
+       
        // The link and title for the help button
        var helpLink = "http://wmd-editor.com/";
        var helpHoverTitle = "WMD website";
        var helpTarget = "_blank";
-
+       
        // -------------------------------------------------------------------
        //  END OF YOUR CHANGES
        // -------------------------------------------------------------------
-
+       
        // A collection of the important regions on the page.
        // Cached so we don't have to keep traversing the DOM.
        wmd.PanelCollection = function(){
@@ -130,13 +117,13 @@ Attacklab.wmdBase = function(){
                this.output = doc.getElementById("wmd-output");
                this.input = doc.getElementById("editor");
        };
-
+       
        // This PanelCollection object can't be filled until after the page
        // has loaded.
        wmd.panels = undefined;
-
+       
        // Internet explorer has problems with CSS sprite buttons that use HTML
-       // lists.  When you click on the background image "button", IE will
+       // lists.  When you click on the background image "button", IE will 
        // select the non-existent link text and discard the selection in the
        // textarea.  The solution to this is to cache the textarea selection
        // on the button's mousedown event and set a flag.  In the part of the
@@ -149,11 +136,11 @@ Attacklab.wmdBase = function(){
        // normally since the focus never leaves the textarea.
        wmd.ieCachedRange = null;               // cached textarea selection
        wmd.ieRetardedClick = false;    // flag
-
+       
        // Returns true if the DOM element is visible, false if it's hidden.
        // Checks if display is anything other than none.
        util.isVisible = function (elem) {
-
+       
            if (window.getComputedStyle) {
                // Most browsers
                        return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none";
@@ -163,8 +150,8 @@ Attacklab.wmdBase = function(){
                        return elem.currentStyle["display"] !== "none";
                }
        };
-
-
+       
+       
        // Adds a listener callback to a DOM element which is fired on a specified
        // event.
        util.addEvent = function(elem, event, listener){
@@ -178,7 +165,7 @@ Attacklab.wmdBase = function(){
                }
        };
 
-
+       
        // Removes a listener callback from a DOM element which is fired on a specified
        // event.
        util.removeEvent = function(elem, event, listener){
@@ -208,7 +195,7 @@ Attacklab.wmdBase = function(){
        //
        // regex is a RegExp, pre and post are strings.
        util.extendRegExp = function(regex, pre, post){
-
+               
                if (pre === null || pre === undefined)
                {
                        pre = "";
@@ -217,10 +204,10 @@ Attacklab.wmdBase = function(){
                {
                        post = "";
                }
-
+               
                var pattern = regex.toString();
                var flags = "";
-
+               
                // Replace the flags with empty space and store them.
                // Technically, this can match incorrect flags like "gmm".
                var result = pattern.match(/\/([gim]*)$/);
@@ -230,29 +217,29 @@ Attacklab.wmdBase = function(){
                else {
                        flags = "";
                }
-
+               
                // Remove the flags and slash delimiters from the regular expression.
                pattern = pattern.replace(/(^\/|\/[gim]*$)/g, "");
                pattern = pre + pattern + post;
-
+               
                return new RegExp(pattern, flags);
        }
 
-
+       
        // Sets the image for a button passed to the WMD editor.
        // Returns a new element with the image attached.
        // Adds several style properties to the image.
        util.createImage = function(img){
-
+               
                var imgPath = imageDirectory + img;
-
+               
                var elem = doc.createElement("img");
                elem.className = "wmd-button";
                elem.src = imgPath;
 
                return elem;
        };
-
+       
 
        // This simulates a modal dialog box and asks for the URL when you
        // click the hyperlink or image buttons.
@@ -261,7 +248,7 @@ Attacklab.wmdBase = function(){
        // defaultInputText: The default value that appears in the input box.
        // makeLinkMarkdown: The function which is executed when the prompt is dismissed, either via OK or Cancel
        util.prompt = function(text, defaultInputText, makeLinkMarkdown){
-
+       
                // These variables need to be declared at this level since they are used
                // in multiple functions.
                var dialog;                     // The dialog box.
@@ -276,7 +263,7 @@ Attacklab.wmdBase = function(){
                if (defaultInputText === undefined) {
                        defaultInputText = "";
                }
-
+               
                // Used as a keydown event handler. Esc dismisses the prompt.
                // Key code 27 is ESC.
                var checkEscape = function(key){
@@ -285,7 +272,7 @@ Attacklab.wmdBase = function(){
                                close(true);
                        }
                };
-
+               
                // Dismisses the hyperlink input box.
                // isCancel is true if we don't care about the input text.
                // isCancel is false if we are going to keep the text.
@@ -301,31 +288,31 @@ Attacklab.wmdBase = function(){
                                text = text.replace('http://http://', 'http://');
                                text = text.replace('http://https://', 'https://');
                                text = text.replace('http://ftp://', 'ftp://');
-
-                               if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1 && text.indexOf('/') !== 0) {
+                               
+                               if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1) {
                                        text = 'http://' + text;
                                }
                        }
-
+                       
                        dialog.parentNode.removeChild(dialog);
                        background.parentNode.removeChild(background);
                        makeLinkMarkdown(text);
                        return false;
                };
-
+               
                // Creates the background behind the hyperlink text entry box.
                // Most of this has been moved to CSS but the div creation and
                // browser-specific hacks remain here.
                var createBackground = function(){
-
+               
                        background = doc.createElement("div");
                        background.className = "wmd-prompt-background";
                        style = background.style;
                        style.position = "absolute";
                        style.top = "0";
-
+                       
                        style.zIndex = "1000";
-
+                       
                        // Some versions of Konqueror don't support transparent colors
                        // so we make the whole window transparent.
                        //
@@ -339,10 +326,10 @@ Attacklab.wmdBase = function(){
                        else {
                                style.opacity = "0.5";
                        }
-
+                       
                        var pageSize = position.getPageSize();
                        style.height = pageSize[1] + "px";
-
+                       
                        if(global.isIE){
                                style.left = doc.documentElement.scrollLeft;
                                style.width = doc.documentElement.clientWidth;
@@ -351,13 +338,13 @@ Attacklab.wmdBase = function(){
                                style.left = "0";
                                style.width = "100%";
                        }
-
+                       
                        doc.body.appendChild(background);
                };
-
+               
                // Create the text input box form/window.
                var createDialog = function(){
-
+               
                        // The main dialog box.
                        dialog = doc.createElement("div");
                        dialog.className = "wmd-prompt-dialog";
@@ -365,13 +352,13 @@ Attacklab.wmdBase = function(){
                        dialog.style.position = "fixed";
                        dialog.style.width = "400px";
                        dialog.style.zIndex = "1001";
-
+                       
                        // The dialog text.
                        var question = doc.createElement("div");
                        question.innerHTML = text;
                        question.style.padding = "5px";
                        dialog.appendChild(question);
-
+                       
                        // The web form container for the text box and buttons.
                        var form = doc.createElement("form");
                        form.onsubmit = function(){ return close(false); };
@@ -383,12 +370,9 @@ Attacklab.wmdBase = function(){
                        style.textAlign = "center";
                        style.position = "relative";
                        dialog.appendChild(form);
-
+                       
                        // The input text box
                        input = doc.createElement("input");
-            if(type == 1){
-                input.id = "image-url";
-            }
                        input.type = "text";
                        input.value = defaultInputText;
                        style = input.style;
@@ -398,13 +382,11 @@ Attacklab.wmdBase = function(){
                        form.appendChild(input);
 
             // The upload file input
-            if(type == 1){
-                var upload = doc.createElement("div");
-                upload.innerHTML = uploadImageHTML;
-                upload.style.padding = "5px";
-                form.appendChild(upload);
-            }
-
+            var upload = doc.createElement("div");
+            upload.innerHTML = uploadImageHTML;
+            upload.style.padding = "5px";
+            form.appendChild(upload);
+                       
                        // The ok button
                        var okButton = doc.createElement("input");
                        okButton.type = "button";
@@ -415,7 +397,7 @@ Attacklab.wmdBase = function(){
                        style.display = "inline";
                        style.width = "7em";
 
-
+                       
                        // The cancel button
                        var cancelButton = doc.createElement("input");
                        cancelButton.type = "button";
@@ -446,20 +428,20 @@ Attacklab.wmdBase = function(){
                                dialog.style.left = "50%";
                        }
                        doc.body.appendChild(dialog);
-
+                       
                        // This has to be done AFTER adding the dialog to the form if you
                        // want it to be centered.
                        dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px";
                        dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px";
-
+                       
                };
-
+               
                createBackground();
-
+               
                // Why is this in a zero-length timeout?
                // Is it working around a browser bug?
                top.setTimeout(function(){
-
+               
                        createDialog();
 
                        var defTextLen = defaultInputText.length;
@@ -474,12 +456,12 @@ Attacklab.wmdBase = function(){
                                range.moveEnd("character", defTextLen);
                                range.select();
                        }
-
+                       
                        input.focus();
                }, 0);
        };
-
-
+       
+       
        // UNFINISHED
        // The assignment in the while loop makes jslint cranky.
        // I'll change it to a better loop later.
@@ -492,7 +474,7 @@ Attacklab.wmdBase = function(){
                }
                return result;
        };
-
+       
        position.getHeight = function (elem) {
                return elem.offsetHeight || elem.scrollHeight;
        };
@@ -502,10 +484,10 @@ Attacklab.wmdBase = function(){
        };
 
        position.getPageSize = function(){
-
+               
                var scrollWidth, scrollHeight;
                var innerWidth, innerHeight;
-
+               
                // It's not very clear which blocks work with which browsers.
                if(self.innerHeight && self.scrollMaxY){
                        scrollWidth = doc.body.scrollWidth;
@@ -519,7 +501,7 @@ Attacklab.wmdBase = function(){
                        scrollWidth = doc.body.offsetWidth;
                        scrollHeight = doc.body.offsetHeight;
                }
-
+               
                if(self.innerHeight){
                        // Non-IE browser
                        innerWidth = self.innerWidth;
@@ -535,33 +517,33 @@ Attacklab.wmdBase = function(){
                        innerWidth = doc.body.clientWidth;
                        innerHeight = doc.body.clientHeight;
                }
-
+               
         var maxWidth = Math.max(scrollWidth, innerWidth);
         var maxHeight = Math.max(scrollHeight, innerHeight);
         return [maxWidth, maxHeight, innerWidth, innerHeight];
        };
-
+       
        // Watches the input textarea, polling at an interval and runs
        // a callback function if anything has changed.
        wmd.inputPoller = function(callback, interval){
-
+       
                var pollerObj = this;
                var inputArea = wmd.panels.input;
-
+               
                // Stored start, end and text.  Used to see if there are changes to the input.
                var lastStart;
                var lastEnd;
                var markdown;
-
+               
                var killHandle; // Used to cancel monitoring on destruction.
                // Checks to see if anything has changed in the textarea.
                // If so, it runs the callback.
                this.tick = function(){
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                       
                        // Update the selection start and end, text.
                        if (inputArea.selectionStart || inputArea.selectionStart === 0) {
                                var start = inputArea.selectionStart;
@@ -569,7 +551,7 @@ Attacklab.wmdBase = function(){
                                if (start != lastStart || end != lastEnd) {
                                        lastStart = start;
                                        lastEnd = end;
-
+                                       
                                        if (markdown != inputArea.value) {
                                                markdown = inputArea.value;
                                                return true;
@@ -578,37 +560,37 @@ Attacklab.wmdBase = function(){
                        }
                        return false;
                };
-
-
+               
+               
                var doTickCallback = function(){
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                       
                        // If anything has changed, call the function.
                        if (pollerObj.tick()) {
                                callback();
                        }
                };
-
+               
                // Set how often we poll the textarea for changes.
                var assignInterval = function(){
                        // previewPollInterval is set at the top of the namespace.
                        killHandle = top.setInterval(doTickCallback, interval);
                };
-
+               
                this.destroy = function(){
                        top.clearInterval(killHandle);
                };
-
+               
                assignInterval();
        };
-
+       
        // Handles pushing and popping TextareaStates for undo/redo commands.
        // I should rename the stack variables to list.
        wmd.undoManager = function(callback){
-
+       
                var undoObj = this;
                var undoStack = []; // A stack of undo states
                var stackPtr = 0; // The index of the current state
@@ -617,17 +599,17 @@ Attacklab.wmdBase = function(){
                var poller;
                var timer; // The setTimeout handle for cancelling the timer
                var inputStateObj;
-
+               
                // Set the mode for later logic steps.
                var setMode = function(newMode, noSave){
-
+               
                        if (mode != newMode) {
                                mode = newMode;
                                if (!noSave) {
                                        saveState();
                                }
                        }
-
+                       
                        if (!global.isIE || mode != "moving") {
                                timer = top.setTimeout(refreshState, 1);
                        }
@@ -635,33 +617,33 @@ Attacklab.wmdBase = function(){
                                inputStateObj = null;
                        }
                };
-
+               
                var refreshState = function(){
                        inputStateObj = new wmd.TextareaState();
                        poller.tick();
                        timer = undefined;
                };
-
+               
                this.setCommandMode = function(){
                        mode = "command";
                        saveState();
                        timer = top.setTimeout(refreshState, 0);
                };
-
+               
                this.canUndo = function(){
                        return stackPtr > 1;
                };
-
+               
                this.canRedo = function(){
                        if (undoStack[stackPtr + 1]) {
                                return true;
                        }
                        return false;
                };
-
+               
                // Removes the last state and restores it.
                this.undo = function(){
-
+               
                        if (undoObj.canUndo()) {
                                if (lastState) {
                                        // What about setting state -1 to null or checking for undefined?
@@ -671,40 +653,40 @@ Attacklab.wmdBase = function(){
                                else {
                                        undoStack[stackPtr] = new wmd.TextareaState();
                                        undoStack[--stackPtr].restore();
-
+                                       
                                        if (callback) {
                                                callback();
                                        }
                                }
                        }
-
+                       
                        mode = "none";
                        wmd.panels.input.focus();
                        refreshState();
                };
-
+               
                // Redo an action.
                this.redo = function(){
-
+               
                        if (undoObj.canRedo()) {
-
+                       
                                undoStack[++stackPtr].restore();
-
+                               
                                if (callback) {
                                        callback();
                                }
                        }
-
+                       
                        mode = "none";
                        wmd.panels.input.focus();
                        refreshState();
                };
-
+               
                // Push the input area state to the stack.
                var saveState = function(){
-
+               
                        var currState = inputStateObj || new wmd.TextareaState();
-
+                       
                        if (!currState) {
                                return false;
                        }
@@ -726,24 +708,24 @@ Attacklab.wmdBase = function(){
                                callback();
                        }
                };
-
+               
                var handleCtrlYZ = function(event){
-
+               
                        var handled = false;
-
+                       
                        if (event.ctrlKey || event.metaKey) {
-
+                       
                                // IE and Opera do not support charCode.
                                var keyCode = event.charCode || event.keyCode;
                                var keyCodeChar = String.fromCharCode(keyCode);
-
+                               
                                switch (keyCodeChar) {
-
+                               
                                        case "y":
                                                undoObj.redo();
                                                handled = true;
                                                break;
-
+                                               
                                        case "z":
                                                if (!event.shiftKey) {
                                                        undoObj.undo();
@@ -755,7 +737,7 @@ Attacklab.wmdBase = function(){
                                                break;
                                }
                        }
-
+                       
                        if (handled) {
                                if (event.preventDefault) {
                                        event.preventDefault();
@@ -766,14 +748,14 @@ Attacklab.wmdBase = function(){
                                return;
                        }
                };
-
+               
                // Set the mode depending on what is going on in the input area.
                var handleModeChange = function(event){
-
+               
                        if (!event.ctrlKey && !event.metaKey) {
-
+                       
                                var keyCode = event.keyCode;
-
+                               
                                if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) {
                                        // 33 - 40: page up/dn and arrow keys
                                        // 63232 - 63235: page up/dn and arrow keys on safari
@@ -794,7 +776,7 @@ Attacklab.wmdBase = function(){
                                        setMode("escape");
                                }
                                else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) {
-                                       // 16-20 are shift, etc.
+                                       // 16-20 are shift, etc. 
                                        // 91: left window key
                                        // I think this might be a little messed up since there are
                                        // a lot of nonprinting keys above 20.
@@ -802,9 +784,9 @@ Attacklab.wmdBase = function(){
                                }
                        }
                };
-
+               
                var setEventHandlers = function(){
-
+               
                        util.addEvent(wmd.panels.input, "keypress", function(event){
                                // keyCode 89: y
                                // keyCode 90: z
@@ -812,7 +794,7 @@ Attacklab.wmdBase = function(){
                                        event.preventDefault();
                                }
                        });
-
+                       
                        var handlePaste = function(){
                                if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) {
                                        if (timer == undefined) {
@@ -822,77 +804,77 @@ Attacklab.wmdBase = function(){
                                        }
                                }
                        };
-
+                       
                        // pastePollInterval is specified at the beginning of this namespace.
                        poller = new wmd.inputPoller(handlePaste, pastePollInterval);
-
+                       
                        util.addEvent(wmd.panels.input, "keydown", handleCtrlYZ);
                        util.addEvent(wmd.panels.input, "keydown", handleModeChange);
-
+                       
                        util.addEvent(wmd.panels.input, "mousedown", function(){
                                setMode("moving");
                        });
                        wmd.panels.input.onpaste = handlePaste;
                        wmd.panels.input.ondrop = handlePaste;
                };
-
+               
                var init = function(){
                        setEventHandlers();
                        refreshState();
                        saveState();
                };
-
+               
                this.destroy = function(){
                        if (poller) {
                                poller.destroy();
                        }
                };
-
+               
                init();
        };
-
+       
        // I think my understanding of how the buttons and callbacks are stored in the array is incomplete.
        wmd.editor = function(previewRefreshCallback){
-
+       
                if (!previewRefreshCallback) {
                        previewRefreshCallback = function(){};
                }
-
+               
                var inputBox = wmd.panels.input;
-
+               
                var offsetHeight = 0;
-
+               
                var editObj = this;
-
+               
                var mainDiv;
                var mainSpan;
-
+               
                var div; // This name is pretty ambiguous.  I should rename this.
-
+               
                // Used to cancel recurring events from setInterval.
                var creationHandle;
-
+               
                var undoMgr; // The undo manager
-
+               
                // Perform the button's action.
                var doClick = function(button){
-
+               
                        inputBox.focus();
-
+                       
                        if (button.textOp) {
-
+                               
                                if (undoMgr) {
                                        undoMgr.setCommandMode();
                                }
-
+                               
                                var state = new wmd.TextareaState();
-
+                               
                                if (!state) {
                                        return;
                                }
-
+                               
                                var chunks = state.getChunks();
-
+                               
                                // Some commands launch a "modal" prompt dialog.  Javascript
                                // can't really make a modal dialog box and the WMD code
                                // will continue to execute while the dialog is displayed.
@@ -901,7 +883,7 @@ Attacklab.wmdBase = function(){
                                //
                                // var link = CreateLinkDialog();
                                // makeMarkdownLink(link);
-                               //
+                               // 
                                // Instead of this straightforward method of handling a
                                // dialog I have to pass any code which would execute
                                // after the dialog is dismissed (e.g. link creation)
@@ -911,64 +893,64 @@ Attacklab.wmdBase = function(){
                                // no real workaround.  Only the image and link code
                                // create dialogs and require the function pointers.
                                var fixupInputArea = function(){
-
+                               
                                        inputBox.focus();
-
+                                       
                                        if (chunks) {
                                                state.setChunks(chunks);
                                        }
-
+                                       
                                        state.restore();
                                        previewRefreshCallback();
                                };
-
+                               
                                var useDefaultText = true;
                                var noCleanup = button.textOp(chunks, fixupInputArea, useDefaultText);
-
+                               
                                if(!noCleanup) {
                                        fixupInputArea();
                                }
-
+                               
                        }
-
+                       
                        if (button.execute) {
                                button.execute(editObj);
                        }
                };
-
+                       
                var setUndoRedoButtonStates = function(){
                        if(undoMgr){
                                setupButton(document.getElementById("wmd-undo-button"), undoMgr.canUndo());
                                setupButton(document.getElementById("wmd-redo-button"), undoMgr.canRedo());
                        }
                };
-
+               
                var setupButton = function(button, isEnabled) {
-
+               
                        var normalYShift = "0px";
                        var disabledYShift = "-20px";
                        var highlightYShift = "-40px";
-
+                       
                        if(isEnabled) {
                                button.style.backgroundPosition = button.XShift + " " + normalYShift;
                                button.onmouseover = function(){
                                        this.style.backgroundPosition = this.XShift + " " + highlightYShift;
                                };
-
+                                                       
                                button.onmouseout = function(){
                                        this.style.backgroundPosition = this.XShift + " " + normalYShift;
                                };
-
+                               
                                // IE tries to select the background image "button" text (it's
                                // implemented in a list item) so we have to cache the selection
                                // on mousedown.
                                if(global.isIE) {
-                                       button.onmousedown =  function() {
+                                       button.onmousedown =  function() { 
                                                wmd.ieRetardedClick = true;
-                                               wmd.ieCachedRange = document.selection.createRange();
+                                               wmd.ieCachedRange = document.selection.createRange(); 
                                        };
                                }
-
+                               
                                if (!button.isHelp)
                                {
                                        button.onclick = function() {
@@ -985,33 +967,33 @@ Attacklab.wmdBase = function(){
                                button.onmouseover = button.onmouseout = button.onclick = function(){};
                        }
                }
-
+       
                var makeSpritedButtonRow = function(){
-
+                       
                        var buttonBar = document.getElementById("wmd-button-bar");
-
+       
                        var normalYShift = "0px";
                        var disabledYShift = "-20px";
                        var highlightYShift = "-40px";
-
+                       
                        var buttonRow = document.createElement("ul");
                        buttonRow.id = "wmd-button-row";
                        buttonRow = buttonBar.appendChild(buttonRow);
 
-
+                       
                        var boldButton = document.createElement("li");
                        boldButton.className = "wmd-button";
                        boldButton.id = "wmd-bold-button";
-                       boldButton.title = toolbar_strong_label;
+                       boldButton.title = "Strong <strong> Ctrl+B";
                        boldButton.XShift = "0px";
                        boldButton.textOp = command.doBold;
                        setupButton(boldButton, true);
                        buttonRow.appendChild(boldButton);
-
+                       
                        var italicButton = document.createElement("li");
                        italicButton.className = "wmd-button";
                        italicButton.id = "wmd-italic-button";
-                       italicButton.title = toolbar_emphasis_label;
+                       italicButton.title = "Emphasis <em> Ctrl+I";
                        italicButton.XShift = "-20px";
                        italicButton.textOp = command.doItalic;
                        setupButton(italicButton, true);
@@ -1020,12 +1002,12 @@ Attacklab.wmdBase = function(){
                        var spacer1 = document.createElement("li");
                        spacer1.className = "wmd-spacer";
                        spacer1.id = "wmd-spacer1";
-                       buttonRow.appendChild(spacer1);
+                       buttonRow.appendChild(spacer1); 
 
                        var linkButton = document.createElement("li");
                        linkButton.className = "wmd-button";
                        linkButton.id = "wmd-link-button";
-                       linkButton.title = toolbar_hyperlink_label;
+                       linkButton.title = "Hyperlink <a> Ctrl+L";
                        linkButton.XShift = "-40px";
                        linkButton.textOp = function(chunk, postProcessing, useDefaultText){
                                return command.doLinkOrImage(chunk, postProcessing, false);
@@ -1036,16 +1018,16 @@ Attacklab.wmdBase = function(){
                        var quoteButton = document.createElement("li");
                        quoteButton.className = "wmd-button";
                        quoteButton.id = "wmd-quote-button";
-                       quoteButton.title = toolbar_blockquote_label;
+                       quoteButton.title = "Blockquote <blockquote> Ctrl+Q";
                        quoteButton.XShift = "-60px";
                        quoteButton.textOp = command.doBlockquote;
                        setupButton(quoteButton, true);
                        buttonRow.appendChild(quoteButton);
-
+                       
                        var codeButton = document.createElement("li");
                        codeButton.className = "wmd-button";
                        codeButton.id = "wmd-code-button";
-                       codeButton.title = toolbar_code_label;
+                       codeButton.title = "Code Sample <pre><code> Ctrl+K";
                        codeButton.XShift = "-80px";
                        codeButton.textOp = command.doCode;
                        setupButton(codeButton, true);
@@ -1054,7 +1036,7 @@ Attacklab.wmdBase = function(){
                        var imageButton = document.createElement("li");
                        imageButton.className = "wmd-button";
                        imageButton.id = "wmd-image-button";
-                       imageButton.title = toolbar_image_label;
+                       imageButton.title = "Image <img> Ctrl+G";
                        imageButton.XShift = "-100px";
                        imageButton.textOp = function(chunk, postProcessing, useDefaultText){
                                return command.doLinkOrImage(chunk, postProcessing, true);
@@ -1065,122 +1047,129 @@ Attacklab.wmdBase = function(){
                        var spacer2 = document.createElement("li");
                        spacer2.className = "wmd-spacer";
                        spacer2.id = "wmd-spacer2";
-                       buttonRow.appendChild(spacer2);
+                       buttonRow.appendChild(spacer2); 
 
                        var olistButton = document.createElement("li");
                        olistButton.className = "wmd-button";
                        olistButton.id = "wmd-olist-button";
-                       olistButton.title = toolbar_numbered_label;
+                       olistButton.title = "Numbered List <ol> Ctrl+O";
                        olistButton.XShift = "-120px";
                        olistButton.textOp = function(chunk, postProcessing, useDefaultText){
                                command.doList(chunk, postProcessing, true, useDefaultText);
                        };
                        setupButton(olistButton, true);
                        buttonRow.appendChild(olistButton);
-
+                       
                        var ulistButton = document.createElement("li");
                        ulistButton.className = "wmd-button";
                        ulistButton.id = "wmd-ulist-button";
-                       ulistButton.title = toolbar_bulleted_label;
+                       ulistButton.title = "Bulleted List <ul> Ctrl+U";
                        ulistButton.XShift = "-140px";
                        ulistButton.textOp = function(chunk, postProcessing, useDefaultText){
                                command.doList(chunk, postProcessing, false, useDefaultText);
                        };
                        setupButton(ulistButton, true);
                        buttonRow.appendChild(ulistButton);
-
+                       
                        var headingButton = document.createElement("li");
                        headingButton.className = "wmd-button";
                        headingButton.id = "wmd-heading-button";
-                       headingButton.title = toolbar_heading_label;
+                       headingButton.title = "Heading <h1>/<h2> Ctrl+H";
                        headingButton.XShift = "-160px";
                        headingButton.textOp = command.doHeading;
                        setupButton(headingButton, true);
-                       buttonRow.appendChild(headingButton);
-
+                       buttonRow.appendChild(headingButton); 
+                       
                        var hrButton = document.createElement("li");
                        hrButton.className = "wmd-button";
                        hrButton.id = "wmd-hr-button";
-                       hrButton.title = toolbar_horizontal_label;
+                       hrButton.title = "Horizontal Rule <hr> Ctrl+R";
                        hrButton.XShift = "-180px";
                        hrButton.textOp = command.doHorizontalRule;
                        setupButton(hrButton, true);
-                       buttonRow.appendChild(hrButton);
-
+                       buttonRow.appendChild(hrButton); 
+                       
                        var spacer3 = document.createElement("li");
                        spacer3.className = "wmd-spacer";
                        spacer3.id = "wmd-spacer3";
-                       buttonRow.appendChild(spacer3);
-
+                       buttonRow.appendChild(spacer3); 
+                       
                        var undoButton = document.createElement("li");
                        undoButton.className = "wmd-button";
                        undoButton.id = "wmd-undo-button";
-                       undoButton.title = toolbar_undo_label;
+                       undoButton.title = "Undo - Ctrl+Z";
                        undoButton.XShift = "-200px";
                        undoButton.execute = function(manager){
                                manager.undo();
                        };
                        setupButton(undoButton, true);
-                       buttonRow.appendChild(undoButton);
-
+                       buttonRow.appendChild(undoButton); 
+                       
                        var redoButton = document.createElement("li");
                        redoButton.className = "wmd-button";
                        redoButton.id = "wmd-redo-button";
-                       redoButton.title = toolbar_redo_label;
+                       redoButton.title = "Redo - Ctrl+Y";
                        if (/win/.test(nav.platform.toLowerCase())) {
-                               redoButton.title = toolbar_redo_label;
+                               redoButton.title = "Redo - Ctrl+Y";
                        }
                        else {
                                // mac and other non-Windows platforms
-                               redoButton.title = $.i18n._('redo') + " - Ctrl+Shift+Z";
+                               redoButton.title = "Redo - Ctrl+Shift+Z";
                        }
                        redoButton.XShift = "-220px";
                        redoButton.execute = function(manager){
                                manager.redo();
                        };
                        setupButton(redoButton, true);
-                       buttonRow.appendChild(redoButton);
-
+                       buttonRow.appendChild(redoButton); 
+                       
+                       var helpButton = document.createElement("li");
+                       helpButton.className = "wmd-button";
+                       helpButton.id = "wmd-help-button";
+                       helpButton.XShift = "-240px";
+                       helpButton.isHelp = true;
+                       
+                       var helpAnchor = document.createElement("a");
+                       helpAnchor.href = helpLink;
+                       helpAnchor.target = helpTarget
+                       helpAnchor.title = helpHoverTitle;
+                       helpButton.appendChild(helpAnchor);
+                       
+                       setupButton(helpButton, true);
+                       buttonRow.appendChild(helpButton);
+                       
                        setUndoRedoButtonStates();
                }
-
+               
                var setupEditor = function(){
-
+               
                        if (/\?noundo/.test(doc.location.href)) {
                                wmd.nativeUndo = true;
                        }
-
+                       
                        if (!wmd.nativeUndo) {
                                undoMgr = new wmd.undoManager(function(){
                                        previewRefreshCallback();
                                        setUndoRedoButtonStates();
                                });
                        }
-
+                       
                        makeSpritedButtonRow();
-
-
+                       
+                       
                        var keyEvent = "keydown";
                        if (global.isOpera) {
                                keyEvent = "keypress";
                        }
-
+                       
                        util.addEvent(inputBox, keyEvent, function(key){
-
+                               
                                // Check to see if we have a button key and, if so execute the callback.
                                if (key.ctrlKey || key.metaKey) {
-
+                       
                                        var keyCode = key.charCode || key.keyCode;
                                        var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
-
-                                       // Bugfix for messed up DEL and .
-                                       if (keyCode === 46) {
-                                               keyCodeStr = "";
-                                       }
-                                       if (keyCode === 190) {
-                                               keyCodeStr = ".";
-                                       }
-
+                                       
                                        switch(keyCodeStr) {
                                                case "b":
                                                        doClick(document.getElementById("wmd-bold-button"));
@@ -1226,18 +1215,18 @@ Attacklab.wmdBase = function(){
                                                default:
                                                        return;
                                        }
-
+                                       
 
                                        if (key.preventDefault) {
                                                key.preventDefault();
                                        }
-
+                                       
                                        if (top.event) {
                                                top.event.returnValue = false;
                                        }
                                }
                        });
-
+                       
                        // Auto-continue lists, code blocks and block quotes when
                        // the enter key is pressed.
                        util.addEvent(inputBox, "keyup", function(key){
@@ -1251,7 +1240,7 @@ Attacklab.wmdBase = function(){
                                        }
                                }
                        });
-
+                       
                        // Disable ESC clearing the input textarea on IE
                        if (global.isIE) {
                                util.addEvent(inputBox, "keydown", function(key){
@@ -1262,7 +1251,7 @@ Attacklab.wmdBase = function(){
                                        }
                                });
                        }
-
+                       
                        if (inputBox.form) {
                                var submitCallback = inputBox.form.onsubmit;
                                inputBox.form.onsubmit = function(){
@@ -1273,19 +1262,19 @@ Attacklab.wmdBase = function(){
                                };
                        }
                };
-
+               
                // Convert the contents of the input textarea to HTML in the output/preview panels.
                var convertToHtml = function(){
-
+               
                        if (wmd.showdown) {
                                var markdownConverter = new wmd.showdown.converter();
                        }
                        var text = inputBox.value;
-
+                       
                        var callback = function(){
                                inputBox.value = text;
                        };
-
+                       
                        if (!/markdown/.test(wmd.wmd_env.output.toLowerCase())) {
                                if (markdownConverter) {
                                        inputBox.value = markdownConverter.makeHtml(text);
@@ -1294,26 +1283,26 @@ Attacklab.wmdBase = function(){
                        }
                        return true;
                };
-
-
+               
+               
                this.undo = function(){
                        if (undoMgr) {
                                undoMgr.undo();
                        }
                };
-
+               
                this.redo = function(){
                        if (undoMgr) {
                                undoMgr.redo();
                        }
                };
-
+               
                // This is pretty useless.  The setupEditor function contents
                // should just be copied here.
                var init = function(){
                        setupEditor();
                };
-
+               
                this.destroy = function(){
                        if (undoMgr) {
                                undoMgr.destroy();
@@ -1326,53 +1315,53 @@ Attacklab.wmdBase = function(){
                        }
                        top.clearInterval(creationHandle);
                };
-
+               
                init();
        };
-
+       
        // The input textarea state/contents.
        // This is used to implement undo/redo by the undo manager.
        wmd.TextareaState = function(){
-
+       
                // Aliases
                var stateObj = this;
                var inputArea = wmd.panels.input;
-
+               
                this.init = function() {
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                               
                        this.setInputAreaSelectionStartEnd();
                        this.scrollTop = inputArea.scrollTop;
                        if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) {
                                this.text = inputArea.value;
                        }
-
-               };
-
+                       
+               }
+               
                // Sets the selected text in the input box after we've performed an
                // operation.
                this.setInputAreaSelection = function(){
-
+               
                        if (!util.isVisible(inputArea)) {
                                return;
                        }
-
+                       
                        if (inputArea.selectionStart !== undefined && !global.isOpera) {
-
+                       
                                inputArea.focus();
                                inputArea.selectionStart = stateObj.start;
                                inputArea.selectionEnd = stateObj.end;
                                inputArea.scrollTop = stateObj.scrollTop;
                        }
                        else if (doc.selection) {
-
+                               
                                if (doc.activeElement && doc.activeElement !== inputArea) {
                                        return;
                                }
-
+                                       
                                inputArea.focus();
                                var range = inputArea.createTextRange();
                                range.moveStart("character", -inputArea.value.length);
@@ -1382,18 +1371,18 @@ Attacklab.wmdBase = function(){
                                range.select();
                        }
                };
-
+               
                this.setInputAreaSelectionStartEnd = function(){
-
+               
                        if (inputArea.selectionStart || inputArea.selectionStart === 0) {
-
+                       
                                stateObj.start = inputArea.selectionStart;
                                stateObj.end = inputArea.selectionEnd;
                        }
                        else if (doc.selection) {
-
+                               
                                stateObj.text = util.fixEolChars(inputArea.value);
-
+                               
                                // IE loses the selection in the textarea when buttons are
                                // clicked.  On IE we cache the selection and set a flag
                                // which we check for here.
@@ -1411,15 +1400,15 @@ Attacklab.wmdBase = function(){
                                var markedRange = marker + fixedRange + marker;
                                range.text = markedRange;
                                var inputText = util.fixEolChars(inputArea.value);
-
+                                       
                                range.moveStart("character", -markedRange.length);
                                range.text = fixedRange;
 
                                stateObj.start = inputText.indexOf(marker);
                                stateObj.end = inputText.lastIndexOf(marker) - marker.length;
-
+                                       
                                var len = stateObj.text.length - util.fixEolChars(inputArea.value).length;
-
+                                       
                                if (len) {
                                        range.moveStart("character", -fixedRange.length);
                                        while (len--) {
@@ -1428,48 +1417,48 @@ Attacklab.wmdBase = function(){
                                        }
                                        range.text = fixedRange;
                                }
-
+                                       
                                this.setInputAreaSelection();
                        }
                };
-
+               
                // Restore this state into the input area.
                this.restore = function(){
-
+               
                        if (stateObj.text != undefined && stateObj.text != inputArea.value) {
                                inputArea.value = stateObj.text;
                        }
                        this.setInputAreaSelection();
                        inputArea.scrollTop = stateObj.scrollTop;
                };
-
+               
                // Gets a collection of HTML chunks from the inptut textarea.
                this.getChunks = function(){
-
+               
                        var chunk = new wmd.Chunks();
-
+                       
                        chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start));
                        chunk.startTag = "";
                        chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end));
                        chunk.endTag = "";
                        chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end));
                        chunk.scrollTop = stateObj.scrollTop;
-
+                       
                        return chunk;
                };
-
+               
                // Sets the TextareaState properties given a chunk of markdown.
                this.setChunks = function(chunk){
-
+               
                        chunk.before = chunk.before + chunk.startTag;
                        chunk.after = chunk.endTag + chunk.after;
-
+                       
                        if (global.isOpera) {
                                chunk.before = chunk.before.replace(/\n/g, "\r\n");
                                chunk.selection = chunk.selection.replace(/\n/g, "\r\n");
                                chunk.after = chunk.after.replace(/\n/g, "\r\n");
                        }
-
+                       
                        this.start = chunk.before.length;
                        this.end = chunk.before.length + chunk.selection.length;
                        this.text = chunk.before + chunk.selection + chunk.after;
@@ -1478,42 +1467,42 @@ Attacklab.wmdBase = function(){
 
                this.init();
        };
-
+       
        // before: contains all the text in the input box BEFORE the selection.
        // after: contains all the text in the input box AFTER the selection.
        wmd.Chunks = function(){
        };
-
+       
        // startRegex: a regular expression to find the start tag
        // endRegex: a regular expresssion to find the end tag
        wmd.Chunks.prototype.findTags = function(startRegex, endRegex){
-
+       
                var chunkObj = this;
                var regex;
-
+               
                if (startRegex) {
-
+                       
                        regex = util.extendRegExp(startRegex, "", "$");
-
-                       this.before = this.before.replace(regex,
+                       
+                       this.before = this.before.replace(regex, 
                                function(match){
                                        chunkObj.startTag = chunkObj.startTag + match;
                                        return "";
                                });
-
+                       
                        regex = util.extendRegExp(startRegex, "^", "");
-
-                       this.selection = this.selection.replace(regex,
+                       
+                       this.selection = this.selection.replace(regex, 
                                function(match){
                                        chunkObj.startTag = chunkObj.startTag + match;
                                        return "";
                                });
                }
-
+               
                if (endRegex) {
-
+                       
                        regex = util.extendRegExp(endRegex, "", "$");
-
+                       
                        this.selection = this.selection.replace(regex,
                                function(match){
                                        chunkObj.endTag = match + chunkObj.endTag;
@@ -1521,7 +1510,7 @@ Attacklab.wmdBase = function(){
                                });
 
                        regex = util.extendRegExp(endRegex, "^", "");
-
+                       
                        this.after = this.after.replace(regex,
                                function(match){
                                        chunkObj.endTag = match + chunkObj.endTag;
@@ -1529,43 +1518,47 @@ Attacklab.wmdBase = function(){
                                });
                }
        };
-
+       
        // If remove is false, the whitespace is transferred
        // to the before/after regions.
        //
        // If remove is true, the whitespace disappears.
        wmd.Chunks.prototype.trimWhitespace = function(remove){
-
+       
                this.selection = this.selection.replace(/^(\s*)/, "");
-
+               
                if (!remove) {
                        this.before += re.$1;
                }
-
+               
                this.selection = this.selection.replace(/(\s*)$/, "");
-
+               
                if (!remove) {
                        this.after = re.$1 + this.after;
                }
        };
-
-
+       
+       
        wmd.Chunks.prototype.addBlankLines = function(nLinesBefore, nLinesAfter, findExtraNewlines){
-
+       
                if (nLinesBefore === undefined) {
                        nLinesBefore = 1;
                }
-
+               
                if (nLinesAfter === undefined) {
                        nLinesAfter = 1;
                }
-
+               
                nLinesBefore++;
                nLinesAfter++;
-
+               
                var regexText;
                var replacementText;
 
+        if (navigator.userAgent.match(/Chrome/)) {
+            "X".match(/()./)
+        }
+               
                this.selection = this.selection.replace(/(^\n*)/, "");
                this.startTag = this.startTag + re.$1;
                this.selection = this.selection.replace(/(\n*$)/, "");
@@ -1574,26 +1567,26 @@ Attacklab.wmdBase = function(){
                this.before = this.before + re.$1;
                this.endTag = this.endTag.replace(/(\n*$)/, "");
                this.after = this.after + re.$1;
-
+               
                if (this.before) {
-
+               
                        regexText = replacementText = "";
-
+                       
                        while (nLinesBefore--) {
                                regexText += "\\n?";
                                replacementText += "\n";
                        }
-
+                       
                        if (findExtraNewlines) {
                                regexText = "\\n*";
                        }
                        this.before = this.before.replace(new re(regexText + "$", ""), replacementText);
                }
-
+               
                if (this.after) {
-
+               
                        regexText = replacementText = "";
-
+                       
                        while (nLinesAfter--) {
                                regexText += "\\n?";
                                replacementText += "\n";
@@ -1601,60 +1594,60 @@ Attacklab.wmdBase = function(){
                        if (findExtraNewlines) {
                                regexText = "\\n*";
                        }
-
+                       
                        this.after = this.after.replace(new re(regexText, ""), replacementText);
                }
        };
-
+       
        // The markdown symbols - 4 spaces = code, > = blockquote, etc.
        command.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";
-
+       
        // Remove markdown symbols from the chunk selection.
        command.unwrap = function(chunk){
                var txt = new re("([^\\n])\\n(?!(\\n|" + command.prefixes + "))", "g");
                chunk.selection = chunk.selection.replace(txt, "$1 $2");
        };
-
+       
        command.wrap = function(chunk, len){
                command.unwrap(chunk);
                var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm");
-
+               
                chunk.selection = chunk.selection.replace(regex, function(line, marked){
                        if (new re("^" + command.prefixes, "").test(line)) {
                                return line;
                        }
                        return marked + "\n";
                });
-
+               
                chunk.selection = chunk.selection.replace(/\s+$/, "");
        };
-
+       
        command.doBold = function(chunk, postProcessing, useDefaultText){
                return command.doBorI(chunk, 2, "strong text");
        };
-
+       
        command.doItalic = function(chunk, postProcessing, useDefaultText){
                return command.doBorI(chunk, 1, "emphasized text");
        };
-
+       
        // chunk: The selected region that will be enclosed with */**
        // nStars: 1 for italics, 2 for bold
        // insertText: If you just click the button without highlighting text, this gets inserted
        command.doBorI = function(chunk, nStars, insertText){
-
-               // Get rid of whitespace and fixup newlines.
+       
+               // Get rid of whitespace and fix up newlines.
                chunk.trimWhitespace();
                chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n");
-
+               
                // Look for stars before and after.  Is the chunk already marked up?
                chunk.before.search(/(\**$)/);
                var starsBefore = re.$1;
-
+               
                chunk.after.search(/(^\**)/);
                var starsAfter = re.$1;
-
+               
                var prevStars = Math.min(starsBefore.length, starsAfter.length);
-
+               
                // Remove stars if we have to since the button acts as a toggle.
                if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) {
                        chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), "");
@@ -1669,26 +1662,26 @@ Attacklab.wmdBase = function(){
                        chunk.before = chunk.before + starsAfter + whitespace;
                }
                else {
-
+               
                        // In most cases, if you don't have any selected text and click the button
                        // you'll get a selected, marked up region with the default text inserted.
                        if (!chunk.selection && !starsAfter) {
                                chunk.selection = insertText;
                        }
-
+                       
                        // Add the true markup.
                        var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ?
                        chunk.before = chunk.before + markup;
                        chunk.after = markup + chunk.after;
                }
-
+               
                return;
        };
-
+       
        command.stripLinkDefs = function(text, defsToAdd){
-
-               text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,
-                       function(totalMatch, id, link, newlines, title){
+       
+               text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, 
+                       function(totalMatch, id, link, newlines, title){        
                                defsToAdd[id] = totalMatch.replace(/\s*$/, "");
                                if (newlines) {
                                        // Strip the title and return that separately.
@@ -1697,95 +1690,102 @@ Attacklab.wmdBase = function(){
                                }
                                return "";
                        });
-
+               
                return text;
        };
-
+       
        command.addLinkDef = function(chunk, linkDef){
-
+       
                var refNumber = 0; // The current reference number
                var defsToAdd = {}; //
                // Start with a clean slate by removing all previous link definitions.
                chunk.before = command.stripLinkDefs(chunk.before, defsToAdd);
                chunk.selection = command.stripLinkDefs(chunk.selection, defsToAdd);
                chunk.after = command.stripLinkDefs(chunk.after, defsToAdd);
-
+               
                var defs = "";
-               var regex = /(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;
-
+               var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;
+        
+        
+               
                var addDefNumber = function(def){
                        refNumber++;
                        def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, "  [" + refNumber + "]:");
                        defs += "\n" + def;
                };
-
-               var getLink = function(wholeMatch, link, id, end){
-
+               
+        // note that
+        // a) the recursive call to getLink cannot go infinite, because by definition
+        //    of regex, inner is always a proper substring of wholeMatch, and
+        // b) more than one level of nesting is neither supported by the regex
+        //    nor making a lot of sense (the only use case for nesting is a linked image)
+        var getLink = function (wholeMatch, before, inner, afterInner, id, end) {
+            inner = inner.replace(regex, getLink);
                        if (defsToAdd[id]) {
                                addDefNumber(defsToAdd[id]);
-                               return link + refNumber + end;
-
+                return before + inner + afterInner + refNumber + end;
+                               
                        }
                        return wholeMatch;
                };
-
+               
                chunk.before = chunk.before.replace(regex, getLink);
-
+               
                if (linkDef) {
                        addDefNumber(linkDef);
                }
                else {
                        chunk.selection = chunk.selection.replace(regex, getLink);
                }
-
+               
                var refOut = refNumber;
-
+               
                chunk.after = chunk.after.replace(regex, getLink);
-
+               
                if (chunk.after) {
                        chunk.after = chunk.after.replace(/\n*$/, "");
                }
                if (!chunk.after) {
                        chunk.selection = chunk.selection.replace(/\n*$/, "");
                }
-
+               
                chunk.after += "\n\n" + defs;
-
+               
                return refOut;
        };
-
+       
        command.doLinkOrImage = function(chunk, postProcessing, isImage){
-
+       
                chunk.trimWhitespace();
                chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
-
+               
                if (chunk.endTag.length > 1) {
-
+               
                        chunk.startTag = chunk.startTag.replace(/!?\[/, "");
                        chunk.endTag = "";
                        command.addLinkDef(chunk, null);
-
+                       
                }
                else {
-
+               
                        if (/\n\n/.test(chunk.selection)) {
                                command.addLinkDef(chunk, null);
                                return;
                        }
-
+                       
                        // The function to be executed when you enter a link and press OK or Cancel.
                        // Marks up the link and adds the ref.
                        var makeLinkMarkdown = function(link){
-
+                       
                                if (link !== null) {
-
+                               
                                        chunk.startTag = chunk.endTag = "";
                                        var linkDef = " [999]: " + link;
-
+                                       
                                        var num = command.addLinkDef(chunk, linkDef);
                                        chunk.startTag = isImage ? "![" : "[";
                                        chunk.endTag = "][" + num + "]";
-
+                                       
                                        if (!chunk.selection) {
                                                if (isImage) {
                                                        chunk.selection = "alt text";
@@ -1797,10 +1797,9 @@ Attacklab.wmdBase = function(){
                                }
                                postProcessing();
                        };
-
+                       
                        if (isImage) {
-                // add fourth param to identify image window
-                               util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown, 1);
+                               util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown);
                        }
                        else {
                                util.prompt(linkDialogText, linkDefaultText, makeLinkMarkdown);
@@ -1808,15 +1807,15 @@ Attacklab.wmdBase = function(){
                        return true;
                }
        };
-
+       
        util.makeAPI = function(){
                wmd.wmd = {};
                wmd.wmd.editor = wmd.editor;
                wmd.wmd.previewManager = wmd.previewManager;
        };
-
+       
        util.startEditor = function(){
-
+       
                if (wmd.wmd_env.autostart === false) {
                        util.makeAPI();
                        return;
@@ -1824,26 +1823,26 @@ Attacklab.wmdBase = function(){
 
                var edit;               // The editor (buttons + input + outputs) - the main object.
                var previewMgr; // The preview manager.
-
+               
                // Fired after the page has fully loaded.
                var loadListener = function(){
-
+               
                        wmd.panels = new wmd.PanelCollection();
-
+                       
                        previewMgr = new wmd.previewManager();
                        var previewRefreshCallback = previewMgr.refresh;
-
+                                               
                        edit = new wmd.editor(previewRefreshCallback);
-
+                       
                        previewMgr.refresh(true);
-
+                       
                };
-
+               
                util.addEvent(top, "load", loadListener);
        };
-
+       
        wmd.previewManager = function(){
-
+               
                var managerObj = this;
                var converter;
                var poller;
@@ -1853,47 +1852,47 @@ Attacklab.wmdBase = function(){
                var htmlOut;
                var maxDelay = 3000;
                var startType = "delayed"; // The other legal value is "manual"
-
+               
                // Adds event listeners to elements and creates the input poller.
                var setupEvents = function(inputElem, listener){
-
+               
                        util.addEvent(inputElem, "input", listener);
                        inputElem.onpaste = listener;
                        inputElem.ondrop = listener;
-
+                       
                        util.addEvent(inputElem, "keypress", listener);
                        util.addEvent(inputElem, "keydown", listener);
                        // previewPollInterval is set at the top of this file.
                        poller = new wmd.inputPoller(listener, previewPollInterval);
                };
-
+               
                var getDocScrollTop = function(){
-
+               
                        var result = 0;
-
+                       
                        if (top.innerHeight) {
                                result = top.pageYOffset;
                        }
-                       else
+                       else 
                                if (doc.documentElement && doc.documentElement.scrollTop) {
                                        result = doc.documentElement.scrollTop;
                                }
-                               else
+                               else 
                                        if (doc.body) {
                                                result = doc.body.scrollTop;
                                        }
-
+                       
                        return result;
                };
-
+               
                var makePreviewHtml = function(){
-
+               
                        // If there are no registered preview and output panels
                        // there is nothing to do.
                        if (!wmd.panels.preview && !wmd.panels.output) {
                                return;
                        }
-
+                       
                        var text = wmd.panels.input.value;
                        if (text && text == oldInputText) {
                                return; // Input text hasn't changed.
@@ -1901,71 +1900,71 @@ Attacklab.wmdBase = function(){
                        else {
                                oldInputText = text;
                        }
-
+                       
                        var prevTime = new Date().getTime();
-
+                       
                        if (!converter && wmd.showdown) {
                                converter = new wmd.showdown.converter();
                        }
-
+                       
                        if (converter) {
                                text = converter.makeHtml(text);
                        }
-
+                       
                        // Calculate the processing time of the HTML creation.
                        // It's used as the delay time in the event listener.
                        var currTime = new Date().getTime();
                        elapsedTime = currTime - prevTime;
-
+                       
                        pushPreviewHtml(text);
                        htmlOut = text;
                };
-
+               
                // setTimeout is already used.  Used as an event listener.
                var applyTimeout = function(){
-
+               
                        if (timeout) {
                                top.clearTimeout(timeout);
                                timeout = undefined;
                        }
-
+                       
                        if (startType !== "manual") {
-
+                       
                                var delay = 0;
-
+                               
                                if (startType === "delayed") {
                                        delay = elapsedTime;
                                }
-
+                               
                                if (delay > maxDelay) {
                                        delay = maxDelay;
                                }
                                timeout = top.setTimeout(makePreviewHtml, delay);
                        }
                };
-
+               
                var getScaleFactor = function(panel){
                        if (panel.scrollHeight <= panel.clientHeight) {
                                return 1;
                        }
                        return panel.scrollTop / (panel.scrollHeight - panel.clientHeight);
                };
-
+               
                var setPanelScrollTops = function(){
-
+               
                        if (wmd.panels.preview) {
                                wmd.panels.preview.scrollTop = (wmd.panels.preview.scrollHeight - wmd.panels.preview.clientHeight) * getScaleFactor(wmd.panels.preview);
                                ;
                        }
-
+                       
                        if (wmd.panels.output) {
                                wmd.panels.output.scrollTop = (wmd.panels.output.scrollHeight - wmd.panels.output.clientHeight) * getScaleFactor(wmd.panels.output);
                                ;
                        }
                };
-
+               
                this.refresh = function(requiresRefresh){
-
+               
                        if (requiresRefresh) {
                                oldInputText = "";
                                makePreviewHtml();
@@ -1974,28 +1973,28 @@ Attacklab.wmdBase = function(){
                                applyTimeout();
                        }
                };
-
+               
                this.processingTime = function(){
                        return elapsedTime;
                };
-
+               
                // The output HTML
                this.output = function(){
                        return htmlOut;
                };
-
+               
                // The mode can be "manual" or "delayed"
                this.setUpdateMode = function(mode){
                        startType = mode;
                        managerObj.refresh();
                };
-
+               
                var isFirstTimeFilled = true;
-
+               
                var pushPreviewHtml = function(text){
-
+               
                        var emptyTop = position.getTop(wmd.panels.input) - getDocScrollTop();
-
+                       
                        // Send the encoded HTML to the output textarea/div.
                        if (wmd.panels.output) {
                                // The value property is only defined if the output is a textarea.
@@ -2011,20 +2010,20 @@ Attacklab.wmdBase = function(){
                                        wmd.panels.output.innerHTML = "<pre><code>" + newText + "</code></pre>";
                                }
                        }
-
+                       
                        if (wmd.panels.preview) {
                                wmd.panels.preview.innerHTML = text;
                        }
-
+                       
                        setPanelScrollTops();
-
+                       
                        if (isFirstTimeFilled) {
                                isFirstTimeFilled = false;
                                return;
                        }
-
+                       
                        var fullTop = position.getTop(wmd.panels.input) - getDocScrollTop();
-
+                       
                        if (global.isIE) {
                                top.setTimeout(function(){
                                        top.scrollBy(0, fullTop - emptyTop);
@@ -2034,12 +2033,12 @@ Attacklab.wmdBase = function(){
                                top.scrollBy(0, fullTop - emptyTop);
                        }
                };
-
+               
                var init = function(){
-
+               
                        setupEvents(wmd.panels.input, applyTimeout);
                        makePreviewHtml();
-
+                       
                        if (wmd.panels.preview) {
                                wmd.panels.preview.scrollTop = 0;
                        }
@@ -2047,25 +2046,25 @@ Attacklab.wmdBase = function(){
                                wmd.panels.output.scrollTop = 0;
                        }
                };
-
+               
                this.destroy = function(){
                        if (poller) {
                                poller.destroy();
                        }
                };
-
+               
                init();
        };
 
        // Moves the cursor to the next line and continues lists, quotes and code.
        command.doAutoindent = function(chunk, postProcessing, useDefaultText){
-
+               
                chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n");
                chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n");
                chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n");
-
+               
                useDefaultText = false;
-
+               
                if(/(\n|^)[ ]{0,3}([*+-])[ \t]+.*\n$/.test(chunk.before)){
                        if(command.doList){
                                command.doList(chunk, postProcessing, false, true);
@@ -2087,49 +2086,104 @@ Attacklab.wmdBase = function(){
                        }
                }
        };
-
+       
        command.doBlockquote = function(chunk, postProcessing, useDefaultText){
-
+               
                chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,
                        function(totalMatch, newlinesBefore, text, newlinesAfter){
                                chunk.before += newlinesBefore;
                                chunk.after = newlinesAfter + chunk.after;
                                return text;
                        });
-
+                       
                chunk.before = chunk.before.replace(/(>[ \t]*)$/,
                        function(totalMatch, blankLine){
                                chunk.selection = blankLine + chunk.selection;
                                return "";
                        });
-
+               
                var defaultText = useDefaultText ? "Blockquote" : "";
                chunk.selection = chunk.selection.replace(/^(\s|>)+$/ ,"");
                chunk.selection = chunk.selection || defaultText;
-
+               
+        // The original code uses a regular expression to find out how much of the
+        // text *directly before* the selection already was a blockquote:
+        /*
                if(chunk.before){
                        chunk.before = chunk.before.replace(/\n?$/,"\n");
                }
+        chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,
+                       function (totalMatch) {
+                           chunk.startTag = totalMatch;
+                           return "";
+                       });
+        */
+        // This comes down to:
+        // Go backwards as many lines a possible, such that each line
+        //  a) starts with ">", or
+        //  b) is almost empty, except for whitespace, or
+        //  c) is preceeded by an unbroken chain of non-empty lines
+        //     leading up to a line that starts with ">" and at least one more character
+        // and in addition
+        //  d) at least one line fulfills a)
+        //
+        // Since this is essentially a backwards-moving regex, it's susceptible to
+        // catstrophic backtracking and can cause the browser to hang;
+        // see e.g. http://meta.stackoverflow.com/questions/9807.
+        //
+        // Hence we replaced this by a simple state machine that just goes through the
+        // lines and checks for a), b), and c).
+
+        var match = "";
+        var leftOver = "";
+        if (chunk.before) {
+            var lines = chunk.before.replace(/\n$/, "").split("\n");
+            var inChain = false;
+            for (var i in lines) {
+                var good = false;
+                line = lines[i];
+                inChain = inChain && line.length > 0; // c) any non-empty line continues the chain
+                if (/^>/.test(line)) {                // a)
+                    good = true;
+                    if (!inChain && line.length > 1)  // c) any line that starts with ">" and has at least one more character starts the chain
+                        inChain = true;
+                } else if (/^[ \t]*$/.test(line)) {   // b)
+                    good = true;
+                } else {
+                    good = inChain;                   // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain
+                }
+                if (good) {
+                    match += line + "\n";
+                } else {
+                    leftOver += match + line;
+                    match = "\n";
+                }
+            }
+            if (!/(^|\n)>/.test(match)) {             // d)
+                leftOver += match;
+                match = "";
+            }
+        }
+
+        chunk.startTag = match;
+        chunk.before = leftOver;
+
+        // end of change
+        
                if(chunk.after){
                        chunk.after = chunk.after.replace(/^\n?/,"\n");
                }
-
-               chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,
-                       function(totalMatch){
-                               chunk.startTag = totalMatch;
-                               return "";
-                       });
-
+               
                chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,
                        function(totalMatch){
                                chunk.endTag = totalMatch;
                                return "";
                        });
-
+               
                var replaceBlanksInTags = function(useBracket){
-
+                       
                        var replacement = useBracket ? "> " : "";
-
+                       
                        if(chunk.startTag){
                                chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/,
                                        function(totalMatch, markdown){
@@ -2143,7 +2197,7 @@ Attacklab.wmdBase = function(){
                                        });
                        }
                };
-
+               
                if(/^(?![ ]{0,3}>)/m.test(chunk.selection)){
                        command.wrap(chunk, wmd.wmd_env.lineLength - 2);
                        chunk.selection = chunk.selection.replace(/^/gm, "> ");
@@ -2154,16 +2208,16 @@ Attacklab.wmdBase = function(){
                        chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, "");
                        command.unwrap(chunk);
                        replaceBlanksInTags(false);
-
+                       
                        if(!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag){
                                chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n");
                        }
-
+                       
                        if(!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag){
                                chunk.endTag=chunk.endTag.replace(/^\n{0,2}/, "\n\n");
                        }
                }
-
+               
                if(!/\n/.test(chunk.selection)){
                        chunk.selection = chunk.selection.replace(/^(> *)/,
                        function(wholeMatch, blanks){
@@ -2174,33 +2228,33 @@ Attacklab.wmdBase = function(){
        };
 
        command.doCode = function(chunk, postProcessing, useDefaultText){
-
+               
                var hasTextBefore = /\S[ ]*$/.test(chunk.before);
                var hasTextAfter = /^[ ]*\S/.test(chunk.after);
-
+               
                // Use 'four space' markdown if the selection is on its own
                // line or is multiline.
                if((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)){
-
+                       
                        chunk.before = chunk.before.replace(/[ ]{4}$/,
                                function(totalMatch){
                                        chunk.selection = totalMatch + chunk.selection;
                                        return "";
                                });
-
+                               
                        var nLinesBefore = 1;
                        var nLinesAfter = 1;
-
-
+                       
+                       
                        if(/\n(\t|[ ]{4,}).*\n$/.test(chunk.before) || chunk.after === ""){
-                               nLinesBefore = 0;
+                               nLinesBefore = 0; 
                        }
                        if(/^\n(\t|[ ]{4,})/.test(chunk.after)){
                                nLinesAfter = 0; // This needs to happen on line 1
                        }
-
+                       
                        chunk.addBlankLines(nLinesBefore, nLinesAfter);
-
+                       
                        if(!chunk.selection){
                                chunk.startTag = "    ";
                                chunk.selection = useDefaultText ? "enter code here" : "";
@@ -2216,10 +2270,10 @@ Attacklab.wmdBase = function(){
                }
                else{
                        // Use backticks (`) to delimit the code block.
-
+                       
                        chunk.trimWhitespace();
                        chunk.findTags(/`/, /`/);
-
+                       
                        if(!chunk.startTag && !chunk.endTag){
                                chunk.startTag = chunk.endTag="`";
                                if(!chunk.selection){
@@ -2235,22 +2289,22 @@ Attacklab.wmdBase = function(){
                        }
                }
        };
-
+       
        command.doList = function(chunk, postProcessing, isNumberedList, useDefaultText){
-
+                               
                // These are identical except at the very beginning and end.
                // Should probably use the regex extension function to make this clearer.
                var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;
                var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;
-
+               
                // The default bullet is a dash but others are possible.
                // This has nothing to do with the particular HTML bullet,
                // it's just a markdown bullet.
                var bullet = "-";
-
+               
                // The number in a numbered list.
                var num = 1;
-
+               
                // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list.
                var getItemPrefix = function(){
                        var prefix;
@@ -2263,39 +2317,39 @@ Attacklab.wmdBase = function(){
                        }
                        return prefix;
                };
-
+               
                // Fixes the prefixes of the other list items.
                var getPrefixedItem = function(itemText){
-
+               
                        // The numbering flag is unset when called by autoindent.
                        if(isNumberedList === undefined){
                                isNumberedList = /^\s*\d/.test(itemText);
                        }
-
+                       
                        // Renumber/bullet the list element.
                        itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,
                                function( _ ){
                                        return getItemPrefix();
                                });
-
+                               
                        return itemText;
                };
-
+               
                chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null);
-
+               
                if(chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)){
                        chunk.before += chunk.startTag;
                        chunk.startTag = "";
                }
-
+               
                if(chunk.startTag){
-
+                       
                        var hasDigits = /\d+[.]/.test(chunk.startTag);
                        chunk.startTag = "";
                        chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n");
                        command.unwrap(chunk);
                        chunk.addBlankLines();
-
+                       
                        if(hasDigits){
                                // Have to renumber the bullet points if this is a numbered list.
                                chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem);
@@ -2304,9 +2358,9 @@ Attacklab.wmdBase = function(){
                                return;
                        }
                }
-
+               
                var nLinesBefore = 1;
-
+               
                chunk.before = chunk.before.replace(previousItemsRegex,
                        function(itemText){
                                if(/^\s*([*+-])/.test(itemText)){
@@ -2315,36 +2369,36 @@ Attacklab.wmdBase = function(){
                                nLinesBefore = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
                                return getPrefixedItem(itemText);
                        });
-
+                       
                if(!chunk.selection){
                        chunk.selection = useDefaultText ? "List item" : " ";
                }
-
+               
                var prefix = getItemPrefix();
-
+               
                var nLinesAfter = 1;
-
+               
                chunk.after = chunk.after.replace(nextItemsRegex,
                        function(itemText){
                                nLinesAfter = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
                                return getPrefixedItem(itemText);
                        });
-
+                       
                chunk.trimWhitespace(true);
                chunk.addBlankLines(nLinesBefore, nLinesAfter, true);
                chunk.startTag = prefix;
                var spaces = prefix.replace(/./g, " ");
                command.wrap(chunk, wmd.wmd_env.lineLength - spaces.length);
                chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces);
-
+               
        };
-
+       
        command.doHeading = function(chunk, postProcessing, useDefaultText){
-
+               
                // Remove leading/trailing whitespace and reduce internal spaces to single spaces.
                chunk.selection = chunk.selection.replace(/\s+/g, " ");
                chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, "");
-
+               
                // If we clicked the button with no selected text, we just
                // make a level 2 hash header around some default text.
                if(!chunk.selection){
@@ -2353,16 +2407,16 @@ Attacklab.wmdBase = function(){
                        chunk.endTag = " ##";
                        return;
                }
-
+               
                var headerLevel = 0;            // The existing header level of the selected text.
-
+               
                // Remove any existing hash heading markdown and save the header level.
                chunk.findTags(/#+[ ]*/, /[ ]*#+/);
                if(/#+/.test(chunk.startTag)){
                        headerLevel = re.lastMatch.length;
                }
                chunk.startTag = chunk.endTag = "";
-
+               
                // Try to get the current header level by looking for - and = in the line
                // below the selection.
                chunk.findTags(null, /\s?(-+|=+)/);
@@ -2372,7 +2426,7 @@ Attacklab.wmdBase = function(){
                if(/-+/.test(chunk.endTag)){
                        headerLevel = 2;
                }
-
+               
                // Skip to the next line so we can create the header markdown.
                chunk.startTag = chunk.endTag = "";
                chunk.addBlankLines(1, 1);
@@ -2381,9 +2435,9 @@ Attacklab.wmdBase = function(){
                // If there is a header level, we substract one from the header level.
                // If it's already a level 1 header, it's removed.
                var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1;
-
+               
                if(headerLevelToCreate > 0){
-
+                       
                        // The button only creates level 1 and 2 underline headers.
                        // Why not have it iterate over hash header levels?  Wouldn't that be easier and cleaner?
                        var headerChar = headerLevelToCreate >= 2 ? "-" : "=";
@@ -2396,8 +2450,8 @@ Attacklab.wmdBase = function(){
                                chunk.endTag += headerChar;
                        }
                }
-       };
-
+       };      
+       
        command.doHorizontalRule = function(chunk, postProcessing, useDefaultText){
                chunk.startTag = "----------\n";
                chunk.selection = "";
@@ -2408,7 +2462,7 @@ Attacklab.wmdBase = function(){
 
 Attacklab.wmd_env = {};
 Attacklab.account_options = {};
-Attacklab.wmd_defaults = {version:1, output:"Markdown", lineLength:40, delayLoad:false};
+Attacklab.wmd_defaults = {version:1, output:"HTML", lineLength:40, delayLoad:false};
 
 if(!Attacklab.wmd)
 {
@@ -2422,25 +2476,25 @@ if(!Attacklab.wmd)
                                {
                                        return;
                                }
-
+                       
                                for(var key in env)
                                {
                                        Attacklab.wmd_env[key] = env[key];
                                }
                        };
-
+                       
                        mergeEnv(Attacklab.wmd_defaults);
                        mergeEnv(Attacklab.account_options);
                        mergeEnv(top["wmd_options"]);
                        Attacklab.full = true;
-
+                       
                        var defaultButtons = "bold italic link blockquote code image ol ul heading hr";
                        Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons;
                };
                Attacklab.loadEnv();
 
        };
-
+       
        Attacklab.wmd();
        Attacklab.wmdBase();
        Attacklab.Util.startEditor();