X-Git-Url: https://git.openstreetmap.org./osqa.git/blobdiff_plain/1a3311389c99c601c42d2b0caffe458efac5686c..d51fb54019701a24740c85e85024de1fce7b0689:/forum/skins/default/media/js/wmd/wmd.js?ds=inline diff --git a/forum/skins/default/media/js/wmd/wmd.js b/forum/skins/default/media/js/wmd/wmd.js index 957603e..ac03ea2 100644 --- a/forum/skins/default/media/js/wmd/wmd.js +++ b/forum/skins/default/media/js/wmd/wmd.js @@ -1,6 +1,4 @@ -/* -Ajax upload -*/jQuery.extend({createUploadIframe:function(d,b){var a="jUploadFrame"+d;if(window.ActiveXObject){var c=document.createElement('');if(typeof b=="boolean"){c.src="javascript:false"}else{if(typeof b=="string"){c.src=b}}}else{var c=document.createElement("iframe");c.id=a;c.name=a}c.style.position="absolute";c.style.top="-1000px";c.style.left="-1000px";document.body.appendChild(c);return c},createUploadForm:function(g,b){var e="jUploadForm"+g;var a="jUploadFile"+g;var d=$('
');var c=$("#"+b);var f=$(c).clone();$(c).attr("id",a);$(c).before(f);$(c).appendTo(d);$(d).css("position","absolute");$(d).css("top","-1200px");$(d).css("left","-1200px");$(d).appendTo("body");return d},ajaxFileUpload:function(k){k=jQuery.extend({},jQuery.ajaxSettings,k);var a=new Date().getTime();var b=jQuery.createUploadForm(a,k.fileElementId);var i=jQuery.createUploadIframe(a,k.secureuri);var h="jUploadFrame"+a;var j="jUploadForm"+a;if(k.global&&!jQuery.active++){jQuery.event.trigger("ajaxStart")}var c=false;var f={};if(k.global){jQuery.event.trigger("ajaxSend",[f,k])}var d=function(l){var p=document.getElementById(h);try{if(p.contentWindow){f.responseText=p.contentWindow.document.body?p.contentWindow.document.body.innerText:null;f.responseXML=p.contentWindow.document.XMLDocument?p.contentWindow.document.XMLDocument:p.contentWindow.document}else{if(p.contentDocument){f.responseText=p.contentDocument.document.body?p.contentDocument.document.body.textContent||document.body.innerText:null;f.responseXML=p.contentDocument.document.XMLDocument?p.contentDocument.document.XMLDocument:p.contentDocument.document}}}catch(o){jQuery.handleError(k,f,null,o)}if(f||l=="timeout"){c=true;var m;try{m=l!="timeout"?"success":"error";if(m!="error"){var n=jQuery.uploadHttpData(f,k.dataType);if(k.success){k.success(n,m)}if(k.global){jQuery.event.trigger("ajaxSuccess",[f,k])}}else{jQuery.handleError(k,f,m)}}catch(o){m="error";jQuery.handleError(k,f,m,o)}if(k.global){jQuery.event.trigger("ajaxComplete",[f,k])}if(k.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}if(k.complete){k.complete(f,m)}jQuery(p).unbind();setTimeout(function(){try{$(p).remove();$(b).remove()}catch(q){jQuery.handleError(k,f,null,q)}},100);f=null}};if(k.timeout>0){setTimeout(function(){if(!c){d("timeout")}},k.timeout)}try{var b=$("#"+j);$(b).attr("action",k.url);$(b).attr("method","POST");$(b).attr("target",h);if(b.encoding){b.encoding="multipart/form-data"}else{b.enctype="multipart/form-data"}$(b).submit()}catch(g){jQuery.handleError(k,f,null,g)}if(window.attachEvent){document.getElementById(h).attachEvent("onload",d)}else{document.getElementById(h).addEventListener("load",d,false)}return{abort:function(){}}},uploadHttpData:function(r,type){var data=!type;data=type=="xml"||data?r.responseXML:r.responseText;if(type=="script"){jQuery.globalEval(data)}if(type=="json"){eval("data = "+data)}if(type=="html"){jQuery("Ctrl-."; - var toolbar_code_label = $.i18n._('preformatted text') + "Ctrl-K"; - var toolbar_image_label = $.i18n._('image') + "
Ctrl-G"; - var toolbar_numbered_label = $.i18n._('numbered list') + "
Ctrl-O"; - var toolbar_bulleted_label = $.i18n._('bulleted list') + "
Ctrl-U"; - var toolbar_heading_label = $.i18n._('heading') + "
/
Ctrl-H"; - var toolbar_horizontal_label = $.i18n._('horizontal bar') + "
Ctrl-R"; - var toolbar_undo_label = $.i18n._('undo') + " Ctrl-Z"; - var toolbar_redo_label = $.i18n._('redo') + " Ctrl-Y"; - + 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()); + + // ------------------------------------------------------------------- // YOUR CHANGES GO HERE // @@ -95,13 +81,13 @@ Attacklab.wmdBase = function(){ // The text that appears on the upper part of the dialog box when // entering links. - var imageDialogText = "" + $.i18n._('enter image url') + '
'; - var linkDialogText = "" + $.i18n._('enter url') + '
'; - var uploadImageHTML ="" + $.i18n._('upload image') + "" + - "
" + - " "; - + var imageDialogText = "" + $.i18n._('enter image url') + "
"; + var linkDialogText = "" + $.i18n._('enter url') + "
"; + var uploadImageHTML ="" + $.i18n._('upload image') + "" + + "
" + + " "; + // The default text that appears in the dialog input box when entering // links. var imageDefaultText = "http://"; @@ -161,7 +147,7 @@ Attacklab.wmdBase = function(){ } else if (elem.currentStyle) { // IE - return elem.currentStyle.display !== "none"; + return elem.currentStyle["display"] !== "none"; } }; @@ -220,18 +206,24 @@ Attacklab.wmdBase = function(){ } var pattern = regex.toString(); - var flags; + var flags = ""; // Replace the flags with empty space and store them. - pattern = pattern.replace(/\/([gim]*)$/, ""); - flags = re.$1; + // Technically, this can match incorrect flags like "gmm". + var result = pattern.match(/\/([gim]*)$/); + if (result === null) { + flags = result[0]; + } + else { + flags = ""; + } - // Remove the slash delimiters on the regular expression. - pattern = pattern.replace(/(^\/|\/$)/g, ""); + // Remove the flags and slash delimiters from the regular expression. + pattern = pattern.replace(/(^\/|\/[gim]*$)/g, ""); pattern = pre + pattern + post; - return new re(pattern, flags); - }; + return new RegExp(pattern, flags); + } // Sets the image for a button passed to the WMD editor. @@ -297,7 +289,7 @@ Attacklab.wmdBase = function(){ text = text.replace('http://https://', 'https://'); text = text.replace('http://ftp://', 'ftp://'); - if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('/') != 0) { + if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1) { text = 'http://' + text; } } @@ -381,9 +373,7 @@ Attacklab.wmdBase = function(){ // The input text box input = doc.createElement("input"); - if(type == 1){ - input.id = "image-url"; - } + input.id = "image-url"; input.type = "text"; input.value = defaultInputText; style = input.style; @@ -391,15 +381,13 @@ Attacklab.wmdBase = function(){ style.width = "80%"; style.marginLeft = style.marginRight = "auto"; form.appendChild(input); - + // The upload file input - if(type == 1){ - var upload = doc.createElement("div"); - 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"; @@ -481,10 +469,9 @@ Attacklab.wmdBase = function(){ position.getTop = function(elem, isInner){ var result = elem.offsetTop; if (!isInner) { - while (elem.offsetParent) { - elem = elem.offsetParent; - result += elem.offsetTop; - } + while (elem = elem.offsetParent) { + result += elem.offsetTop; + } } return result; }; @@ -811,7 +798,7 @@ Attacklab.wmdBase = function(){ var handlePaste = function(){ if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) { - if (timer === undefined) { + if (timer == undefined) { mode = "paste"; saveState(); refreshState(); @@ -918,7 +905,8 @@ Attacklab.wmdBase = function(){ previewRefreshCallback(); }; - var noCleanup = button.textOp(chunks, fixupInputArea); + var useDefaultText = true; + var noCleanup = button.textOp(chunks, fixupInputArea, useDefaultText); if(!noCleanup) { fixupInputArea(); @@ -972,17 +960,19 @@ Attacklab.wmdBase = function(){ } doClick(this); return false; - }; + } } } else { button.style.backgroundPosition = button.XShift + " " + disabledYShift; 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"; @@ -995,7 +985,7 @@ Attacklab.wmdBase = function(){ var boldButton = document.createElement("li"); boldButton.className = "wmd-button"; boldButton.id = "wmd-bold-button"; - boldButton.title = toolbar_strong_label; + boldButton.title = "Strong Ctrl+B"; boldButton.XShift = "0px"; boldButton.textOp = command.doBold; setupButton(boldButton, true); @@ -1004,7 +994,7 @@ Attacklab.wmdBase = function(){ var italicButton = document.createElement("li"); italicButton.className = "wmd-button"; italicButton.id = "wmd-italic-button"; - italicButton.title = toolbar_emphasis_label; + italicButton.title = "Emphasis Ctrl+I"; italicButton.XShift = "-20px"; italicButton.textOp = command.doItalic; setupButton(italicButton, true); @@ -1018,9 +1008,9 @@ Attacklab.wmdBase = function(){ var linkButton = document.createElement("li"); linkButton.className = "wmd-button"; linkButton.id = "wmd-link-button"; - linkButton.title = toolbar_hyperlink_label; + linkButton.title = "Hyperlink Ctrl+L"; linkButton.XShift = "-40px"; - linkButton.textOp = function(chunk, postProcessing){ + linkButton.textOp = function(chunk, postProcessing, useDefaultText){ return command.doLinkOrImage(chunk, postProcessing, false); }; setupButton(linkButton, true); @@ -1029,7 +1019,7 @@ Attacklab.wmdBase = function(){ var quoteButton = document.createElement("li"); quoteButton.className = "wmd-button"; quoteButton.id = "wmd-quote-button"; - quoteButton.title = toolbar_blockquote_label; + quoteButton.title = "BlockquoteCtrl+Q"; quoteButton.XShift = "-60px"; quoteButton.textOp = command.doBlockquote; setupButton(quoteButton, true); @@ -1038,7 +1028,7 @@ Attacklab.wmdBase = function(){ var codeButton = document.createElement("li"); codeButton.className = "wmd-button"; codeButton.id = "wmd-code-button"; - codeButton.title = toolbar_code_label; + codeButton.title = "Code SampleCtrl+K"; codeButton.XShift = "-80px"; codeButton.textOp = command.doCode; setupButton(codeButton, true); @@ -1047,9 +1037,9 @@ 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
Ctrl+G"; imageButton.XShift = "-100px"; - imageButton.textOp = function(chunk, postProcessing){ + imageButton.textOp = function(chunk, postProcessing, useDefaultText){ return command.doLinkOrImage(chunk, postProcessing, true); }; setupButton(imageButton, true); @@ -1063,10 +1053,10 @@ Attacklab.wmdBase = function(){ var olistButton = document.createElement("li"); olistButton.className = "wmd-button"; olistButton.id = "wmd-olist-button"; - olistButton.title = toolbar_numbered_label; + olistButton.title = "Numbered List
Ctrl+O"; olistButton.XShift = "-120px"; - olistButton.textOp = function(chunk, postProcessing){ - command.doList(chunk, postProcessing, true); + olistButton.textOp = function(chunk, postProcessing, useDefaultText){ + command.doList(chunk, postProcessing, true, useDefaultText); }; setupButton(olistButton, true); buttonRow.appendChild(olistButton); @@ -1074,10 +1064,10 @@ Attacklab.wmdBase = function(){ var ulistButton = document.createElement("li"); ulistButton.className = "wmd-button"; ulistButton.id = "wmd-ulist-button"; - ulistButton.title = toolbar_bulleted_label; + ulistButton.title = "Bulleted List
Ctrl+U"; ulistButton.XShift = "-140px"; - ulistButton.textOp = function(chunk, postProcessing){ - command.doList(chunk, postProcessing, false); + ulistButton.textOp = function(chunk, postProcessing, useDefaultText){ + command.doList(chunk, postProcessing, false, useDefaultText); }; setupButton(ulistButton, true); buttonRow.appendChild(ulistButton); @@ -1085,7 +1075,7 @@ Attacklab.wmdBase = function(){ var headingButton = document.createElement("li"); headingButton.className = "wmd-button"; headingButton.id = "wmd-heading-button"; - headingButton.title = toolbar_heading_label; + headingButton.title = "Heading
/
Ctrl+H"; headingButton.XShift = "-160px"; headingButton.textOp = command.doHeading; setupButton(headingButton, true); @@ -1094,7 +1084,7 @@ Attacklab.wmdBase = function(){ var hrButton = document.createElement("li"); hrButton.className = "wmd-button"; hrButton.id = "wmd-hr-button"; - hrButton.title = toolbar_horizontal_label; + hrButton.title = "Horizontal Rule
Ctrl+R"; hrButton.XShift = "-180px"; hrButton.textOp = command.doHorizontalRule; setupButton(hrButton, true); @@ -1108,7 +1098,7 @@ Attacklab.wmdBase = function(){ 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(); @@ -1119,13 +1109,13 @@ Attacklab.wmdBase = function(){ 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){ @@ -1133,7 +1123,7 @@ Attacklab.wmdBase = function(){ }; setupButton(redoButton, true); buttonRow.appendChild(redoButton); - /* + var helpButton = document.createElement("li"); helpButton.className = "wmd-button"; helpButton.id = "wmd-help-button"; @@ -1144,13 +1134,13 @@ Attacklab.wmdBase = function(){ helpAnchor.href = helpLink; helpAnchor.target = helpTarget helpAnchor.title = helpHoverTitle; - helpButton.appendChild(helpAnchor); + //helpButton.appendChild(helpAnchor); + + //setupButton(helpButton, true); + //buttonRow.appendChild(helpButton); - setupButton(helpButton, true); - buttonRow.appendChild(helpButton); - */ setUndoRedoButtonStates(); - }; + } var setupEditor = function(){ @@ -1177,18 +1167,10 @@ Attacklab.wmdBase = function(){ // 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")); @@ -1199,7 +1181,7 @@ Attacklab.wmdBase = function(){ case "l": doClick(document.getElementById("wmd-link-button")); break; - case ".": + case "q": doClick(document.getElementById("wmd-quote-button")); break; case "k": @@ -1246,11 +1228,12 @@ Attacklab.wmdBase = function(){ } }); - // Auto-indent on shift-enter + // Auto-continue lists, code blocks and block quotes when + // the enter key is pressed. util.addEvent(inputBox, "keyup", function(key){ - if (key.shiftKey && !key.ctrlKey && !key.metaKey) { + if (!key.shiftKey && !key.ctrlKey && !key.metaKey) { var keyCode = key.charCode || key.keyCode; - // Character 13 is Enter + // Key code 13 is Enter if (keyCode === 13) { fakeButton = {}; fakeButton.textOp = command.doAutoindent; @@ -1259,6 +1242,17 @@ Attacklab.wmdBase = function(){ } }); + // Disable ESC clearing the input textarea on IE + if (global.isIE) { + util.addEvent(inputBox, "keydown", function(key){ + var code = key.keyCode; + // Key code 27 is ESC + if (code === 27) { + return false; + } + }); + } + if (inputBox.form) { var submitCallback = inputBox.form.onsubmit; inputBox.form.onsubmit = function(){ @@ -1346,7 +1340,7 @@ Attacklab.wmdBase = function(){ this.text = inputArea.value; } - }; + } // Sets the selected text in the input box after we've performed an // operation. @@ -1432,7 +1426,7 @@ Attacklab.wmdBase = function(){ // Restore this state into the input area. this.restore = function(){ - if (stateObj.text !== undefined && stateObj.text != inputArea.value) { + if (stateObj.text != undefined && stateObj.text != inputArea.value) { inputArea.value = stateObj.text; } this.setInputAreaSelection(); @@ -1546,7 +1540,7 @@ Attacklab.wmdBase = function(){ }; - wmd.Chunks.prototype.skipLines = function(nLinesBefore, nLinesAfter, findExtraNewlines){ + wmd.Chunks.prototype.addBlankLines = function(nLinesBefore, nLinesAfter, findExtraNewlines){ if (nLinesBefore === undefined) { nLinesBefore = 1; @@ -1561,6 +1555,10 @@ Attacklab.wmdBase = function(){ var regexText; var replacementText; + + if (navigator.userAgent.match(/Chrome/)) { + "X".match(/()./) + } this.selection = this.selection.replace(/(^\n*)/, ""); this.startTag = this.startTag + re.$1; @@ -1625,20 +1623,20 @@ Attacklab.wmdBase = function(){ chunk.selection = chunk.selection.replace(/\s+$/, ""); }; - command.doBold = function(chunk, postProcessing){ - return command.doBorI(chunk, postProcessing, 2, "strong text"); + command.doBold = function(chunk, postProcessing, useDefaultText){ + return command.doBorI(chunk, 2, "strong text"); }; - command.doItalic = function(chunk, postProcessing){ - return command.doBorI(chunk, postProcessing, 1, "emphasized 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, postProcessing, nStars, insertText){ + 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"); @@ -1707,7 +1705,9 @@ Attacklab.wmdBase = function(){ chunk.after = command.stripLinkDefs(chunk.after, defsToAdd); var defs = ""; - var regex = /(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; + var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; + + var addDefNumber = function(def){ refNumber++; @@ -1715,11 +1715,16 @@ Attacklab.wmdBase = function(){ 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; @@ -1795,8 +1800,7 @@ Attacklab.wmdBase = function(){ }; if (isImage) { - // add forth param to identify image window - util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown, 1); + util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown); } else { util.prompt(linkDialogText, linkDefaultText, makeLinkMarkdown); @@ -1951,10 +1955,12 @@ Attacklab.wmdBase = 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); + ; } }; @@ -2051,32 +2057,38 @@ Attacklab.wmdBase = function(){ init(); }; - // When making a list, hitting shift-enter will put your cursor on the next line - // at the current indent level. - command.doAutoindent = function(chunk, postProcessing){ + // 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"); - if(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(chunk.before)){ + useDefaultText = false; + + if(/(\n|^)[ ]{0,3}([*+-])[ \t]+.*\n$/.test(chunk.before)){ if(command.doList){ - command.doList(chunk); + command.doList(chunk, postProcessing, false, true); + } + } + if(/(\n|^)[ ]{0,3}(\d+[.])[ \t]+.*\n$/.test(chunk.before)){ + if(command.doList){ + command.doList(chunk, postProcessing, true, true); } } if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)){ if(command.doBlockquote){ - command.doBlockquote(chunk); + command.doBlockquote(chunk, postProcessing, useDefaultText); } } if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)){ if(command.doCode){ - command.doCode(chunk); + command.doCode(chunk, postProcessing, useDefaultText); } } }; - command.doBlockquote = function(chunk, postProcessing){ + command.doBlockquote = function(chunk, postProcessing, useDefaultText){ chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/, function(totalMatch, newlinesBefore, text, newlinesAfter){ @@ -2090,23 +2102,79 @@ Attacklab.wmdBase = function(){ chunk.selection = blankLine + chunk.selection; return ""; }); - + + var defaultText = useDefaultText ? "Blockquote" : ""; chunk.selection = chunk.selection.replace(/^(\s|>)+$/ ,""); - chunk.selection = chunk.selection || "Blockquote"; + 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; @@ -2135,7 +2203,7 @@ Attacklab.wmdBase = function(){ command.wrap(chunk, wmd.wmd_env.lineLength - 2); chunk.selection = chunk.selection.replace(/^/gm, "> "); replaceBlanksInTags(true); - chunk.skipLines(); + chunk.addBlankLines(); } else{ chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, ""); @@ -2160,7 +2228,7 @@ Attacklab.wmdBase = function(){ } }; - command.doCode = function(chunk, postProcessing){ + command.doCode = function(chunk, postProcessing, useDefaultText){ var hasTextBefore = /\S[ ]*$/.test(chunk.before); var hasTextAfter = /^[ ]*\S/.test(chunk.after); @@ -2175,21 +2243,22 @@ Attacklab.wmdBase = function(){ return ""; }); - var nLinesBack = 1; - var nLinesForward = 1; + var nLinesBefore = 1; + var nLinesAfter = 1; + - if(/\n(\t|[ ]{4,}).*\n$/.test(chunk.before)){ - nLinesBack = 0; + if(/\n(\t|[ ]{4,}).*\n$/.test(chunk.before) || chunk.after === ""){ + nLinesBefore = 0; } if(/^\n(\t|[ ]{4,})/.test(chunk.after)){ - nLinesForward = 0; + nLinesAfter = 0; // This needs to happen on line 1 } - chunk.skipLines(nLinesBack, nLinesForward); + chunk.addBlankLines(nLinesBefore, nLinesAfter); if(!chunk.selection){ chunk.startTag = " "; - chunk.selection = "enter code here"; + chunk.selection = useDefaultText ? "enter code here" : ""; } else { if(/^[ ]{0,3}\S/m.test(chunk.selection)){ @@ -2209,7 +2278,7 @@ Attacklab.wmdBase = function(){ if(!chunk.startTag && !chunk.endTag){ chunk.startTag = chunk.endTag="`"; if(!chunk.selection){ - chunk.selection = "enter code here"; + chunk.selection = useDefaultText ? "enter code here" : ""; } } else if(chunk.endTag && !chunk.startTag){ @@ -2222,7 +2291,7 @@ Attacklab.wmdBase = function(){ } }; - command.doList = function(chunk, postProcessing, isNumberedList){ + 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. @@ -2280,7 +2349,7 @@ Attacklab.wmdBase = function(){ chunk.startTag = ""; chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n"); command.unwrap(chunk); - chunk.skipLines(); + chunk.addBlankLines(); if(hasDigits){ // Have to renumber the bullet points if this is a numbered list. @@ -2291,33 +2360,33 @@ Attacklab.wmdBase = function(){ } } - var nLinesUp = 1; + var nLinesBefore = 1; chunk.before = chunk.before.replace(previousItemsRegex, function(itemText){ if(/^\s*([*+-])/.test(itemText)){ bullet = re.$1; } - nLinesUp = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + nLinesBefore = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; return getPrefixedItem(itemText); }); if(!chunk.selection){ - chunk.selection = "List item"; + chunk.selection = useDefaultText ? "List item" : " "; } var prefix = getItemPrefix(); - var nLinesDown = 1; + var nLinesAfter = 1; chunk.after = chunk.after.replace(nextItemsRegex, function(itemText){ - nLinesDown = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + nLinesAfter = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; return getPrefixedItem(itemText); }); chunk.trimWhitespace(true); - chunk.skipLines(nLinesUp, nLinesDown, true); + chunk.addBlankLines(nLinesBefore, nLinesAfter, true); chunk.startTag = prefix; var spaces = prefix.replace(/./g, " "); command.wrap(chunk, wmd.wmd_env.lineLength - spaces.length); @@ -2325,7 +2394,7 @@ Attacklab.wmdBase = function(){ }; - command.doHeading = function(chunk, postProcessing){ + command.doHeading = function(chunk, postProcessing, useDefaultText){ // Remove leading/trailing whitespace and reduce internal spaces to single spaces. chunk.selection = chunk.selection.replace(/\s+/g, " "); @@ -2361,7 +2430,7 @@ Attacklab.wmdBase = function(){ // Skip to the next line so we can create the header markdown. chunk.startTag = chunk.endTag = ""; - chunk.skipLines(1, 1); + chunk.addBlankLines(1, 1); // We make a level 2 header if there is no current header. // If there is a header level, we substract one from the header level. @@ -2384,17 +2453,17 @@ Attacklab.wmdBase = function(){ } }; - command.doHorizontalRule = function(chunk, postProcessing){ + command.doHorizontalRule = function(chunk, postProcessing, useDefaultText){ chunk.startTag = "----------\n"; chunk.selection = ""; - chunk.skipLines(2, 1, true); + chunk.addBlankLines(2, 1, true); } }; 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) {