|
|
|
|
@@ -1,6 +1,6 @@
|
|
|
|
|
// needs Markdown.Converter.js at the moment
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
(function() {
|
|
|
|
|
var util = {},
|
|
|
|
|
position = {},
|
|
|
|
|
ui = {},
|
|
|
|
|
@@ -62,7 +62,7 @@
|
|
|
|
|
// - getConverter() returns the markdown converter object that was passed to the constructor
|
|
|
|
|
// - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op.
|
|
|
|
|
// - refreshPreview() forces the preview to be updated. This method is only available after run() was called.
|
|
|
|
|
Markdown.Editor = function(markdownConverter, idPostfix, help, imageUploadHandler) {
|
|
|
|
|
Markdown.Editor = function(markdownConverter, idPostfix, help, imageUploadHandler) {
|
|
|
|
|
idPostfix = idPostfix || '';
|
|
|
|
|
|
|
|
|
|
var hooks = this.hooks = new Markdown.HookCollection();
|
|
|
|
|
@@ -107,7 +107,7 @@
|
|
|
|
|
var forceRefresh = that.refreshPreview = function() { previewManager.refresh(true); };
|
|
|
|
|
|
|
|
|
|
forceRefresh();
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// before: contains all the text in the input box BEFORE the selection.
|
|
|
|
|
@@ -116,11 +116,11 @@
|
|
|
|
|
|
|
|
|
|
// startRegex: a regular expression to find the start tag
|
|
|
|
|
// endRegex: a regular expresssion to find the end tag
|
|
|
|
|
Chunks.prototype.findTags = function(startRegex, endRegex) {
|
|
|
|
|
Chunks.prototype.findTags = function(startRegex, endRegex) {
|
|
|
|
|
var chunkObj = this;
|
|
|
|
|
var regex;
|
|
|
|
|
|
|
|
|
|
if (startRegex) {
|
|
|
|
|
if (startRegex) {
|
|
|
|
|
regex = util.extendRegExp(startRegex, '', '$');
|
|
|
|
|
|
|
|
|
|
this.before = this.before.replace(regex,
|
|
|
|
|
@@ -138,7 +138,7 @@
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (endRegex) {
|
|
|
|
|
if (endRegex) {
|
|
|
|
|
regex = util.extendRegExp(endRegex, '', '$');
|
|
|
|
|
|
|
|
|
|
this.selection = this.selection.replace(regex,
|
|
|
|
|
@@ -174,7 +174,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Chunks.prototype.skipLines = function(nLinesBefore, nLinesAfter, findExtraNewlines) {
|
|
|
|
|
Chunks.prototype.skipLines = function(nLinesBefore, nLinesAfter, findExtraNewlines) {
|
|
|
|
|
if (nLinesBefore === undefined) {
|
|
|
|
|
nLinesBefore = 1;
|
|
|
|
|
}
|
|
|
|
|
@@ -205,7 +205,7 @@
|
|
|
|
|
this.endTag = this.endTag.replace(/(\n*$)/, '');
|
|
|
|
|
this.after = this.after + re.$1;
|
|
|
|
|
|
|
|
|
|
if (this.before) {
|
|
|
|
|
if (this.before) {
|
|
|
|
|
regexText = replacementText = '';
|
|
|
|
|
|
|
|
|
|
while (nLinesBefore--) {
|
|
|
|
|
@@ -219,7 +219,7 @@
|
|
|
|
|
this.before = this.before.replace(new re(regexText + '$', ''), replacementText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.after) {
|
|
|
|
|
if (this.after) {
|
|
|
|
|
regexText = replacementText = '';
|
|
|
|
|
|
|
|
|
|
while (nLinesAfter--) {
|
|
|
|
|
@@ -264,7 +264,7 @@
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
util.isVisible = function(elem) {
|
|
|
|
|
if (window.getComputedStyle) {
|
|
|
|
|
// Most browsers
|
|
|
|
|
return window.getComputedStyle(elem, null).getPropertyValue('display') !== 'none';
|
|
|
|
|
@@ -318,7 +318,7 @@
|
|
|
|
|
// The flags are unchanged.
|
|
|
|
|
//
|
|
|
|
|
// regex is a RegExp, pre and post are strings.
|
|
|
|
|
util.extendRegExp = function(regex, pre, post) {
|
|
|
|
|
util.extendRegExp = function(regex, pre, post) {
|
|
|
|
|
if (pre === null || pre === undefined) {
|
|
|
|
|
pre = '';
|
|
|
|
|
}
|
|
|
|
|
@@ -363,7 +363,7 @@
|
|
|
|
|
return elem.offsetWidth || elem.scrollWidth;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
position.getPageSize = function() {
|
|
|
|
|
position.getPageSize = function() {
|
|
|
|
|
var scrollWidth, scrollHeight;
|
|
|
|
|
var innerWidth, innerHeight;
|
|
|
|
|
|
|
|
|
|
@@ -404,7 +404,7 @@
|
|
|
|
|
|
|
|
|
|
// Handles pushing and popping TextareaStates for undo/redo commands.
|
|
|
|
|
// I should rename the stack variables to list.
|
|
|
|
|
function UndoManager(callback, panels) {
|
|
|
|
|
function UndoManager(callback, panels) {
|
|
|
|
|
var undoObj = this;
|
|
|
|
|
var undoStack = []; // A stack of undo states
|
|
|
|
|
var stackPtr = 0; // The index of the current state
|
|
|
|
|
@@ -453,7 +453,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Removes the last state and restores it.
|
|
|
|
|
this.undo = function() {
|
|
|
|
|
this.undo = function() {
|
|
|
|
|
if (undoObj.canUndo()) {
|
|
|
|
|
if (lastState) {
|
|
|
|
|
// What about setting state -1 to null or checking for undefined?
|
|
|
|
|
@@ -476,8 +476,8 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Redo an action.
|
|
|
|
|
this.redo = function() {
|
|
|
|
|
if (undoObj.canRedo()) {
|
|
|
|
|
this.redo = function() {
|
|
|
|
|
if (undoObj.canRedo()) {
|
|
|
|
|
undoStack[++stackPtr].restore();
|
|
|
|
|
|
|
|
|
|
if (callback) {
|
|
|
|
|
@@ -516,10 +516,10 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var handleCtrlYZ = function(event) {
|
|
|
|
|
var handleCtrlYZ = function(event) {
|
|
|
|
|
var handled = false;
|
|
|
|
|
|
|
|
|
|
if (event.ctrlKey || event.metaKey) {
|
|
|
|
|
if (event.ctrlKey || event.metaKey) {
|
|
|
|
|
// IE and Opera do not support charCode.
|
|
|
|
|
var keyCode = event.charCode || event.keyCode;
|
|
|
|
|
var keyCodeChar = String.fromCharCode(keyCode);
|
|
|
|
|
@@ -555,8 +555,8 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Set the mode depending on what is going on in the input area.
|
|
|
|
|
var handleModeChange = function(event) {
|
|
|
|
|
if (!event.ctrlKey && !event.metaKey) {
|
|
|
|
|
var handleModeChange = function(event) {
|
|
|
|
|
if (!event.ctrlKey && !event.metaKey) {
|
|
|
|
|
var keyCode = event.keyCode;
|
|
|
|
|
|
|
|
|
|
if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) {
|
|
|
|
|
@@ -630,7 +630,7 @@
|
|
|
|
|
|
|
|
|
|
// The input textarea state/contents.
|
|
|
|
|
// This is used to implement undo/redo by the undo manager.
|
|
|
|
|
function TextareaState(panels, isInitialState) {
|
|
|
|
|
function TextareaState(panels, isInitialState) {
|
|
|
|
|
// Aliases
|
|
|
|
|
var stateObj = this;
|
|
|
|
|
var inputArea = panels.input;
|
|
|
|
|
@@ -646,23 +646,23 @@
|
|
|
|
|
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() {
|
|
|
|
|
this.setInputAreaSelection = function() {
|
|
|
|
|
if (!util.isVisible(inputArea)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) {
|
|
|
|
|
if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) {
|
|
|
|
|
inputArea.focus();
|
|
|
|
|
inputArea.selectionStart = stateObj.start;
|
|
|
|
|
inputArea.selectionEnd = stateObj.end;
|
|
|
|
|
inputArea.scrollTop = stateObj.scrollTop;
|
|
|
|
|
}
|
|
|
|
|
else if (doc.selection) {
|
|
|
|
|
else if (doc.selection) {
|
|
|
|
|
if (doc.activeElement && doc.activeElement !== inputArea) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@@ -677,12 +677,12 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.setInputAreaSelectionStartEnd = function() {
|
|
|
|
|
if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) {
|
|
|
|
|
this.setInputAreaSelectionStartEnd = function() {
|
|
|
|
|
if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) {
|
|
|
|
|
stateObj.start = inputArea.selectionStart;
|
|
|
|
|
stateObj.end = inputArea.selectionEnd;
|
|
|
|
|
}
|
|
|
|
|
else if (doc.selection) {
|
|
|
|
|
else if (doc.selection) {
|
|
|
|
|
stateObj.text = util.fixEolChars(inputArea.value);
|
|
|
|
|
|
|
|
|
|
// IE loses the selection in the textarea when buttons are
|
|
|
|
|
@@ -723,7 +723,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Restore this state into the input area.
|
|
|
|
|
this.restore = function() {
|
|
|
|
|
this.restore = function() {
|
|
|
|
|
if (stateObj.text != undefined && stateObj.text != inputArea.value) {
|
|
|
|
|
inputArea.value = stateObj.text;
|
|
|
|
|
}
|
|
|
|
|
@@ -732,7 +732,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Gets a collection of HTML chunks from the inptut textarea.
|
|
|
|
|
this.getChunks = function() {
|
|
|
|
|
this.getChunks = function() {
|
|
|
|
|
var chunk = new Chunks();
|
|
|
|
|
chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start));
|
|
|
|
|
chunk.startTag = '';
|
|
|
|
|
@@ -745,7 +745,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sets the TextareaState properties given a chunk of markdown.
|
|
|
|
|
this.setChunks = function(chunk) {
|
|
|
|
|
this.setChunks = function(chunk) {
|
|
|
|
|
chunk.before = chunk.before + chunk.startTag;
|
|
|
|
|
chunk.after = chunk.endTag + chunk.after;
|
|
|
|
|
|
|
|
|
|
@@ -757,7 +757,7 @@
|
|
|
|
|
this.init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PreviewManager(converter, panels, previewPushCallback) {
|
|
|
|
|
function PreviewManager(converter, panels, previewPushCallback) {
|
|
|
|
|
var managerObj = this;
|
|
|
|
|
var timeout;
|
|
|
|
|
var elapsedTime;
|
|
|
|
|
@@ -766,7 +766,7 @@
|
|
|
|
|
var startType = 'delayed'; // The other legal value is "manual"
|
|
|
|
|
|
|
|
|
|
// Adds event listeners to elements
|
|
|
|
|
var setupEvents = function(inputElem, listener) {
|
|
|
|
|
var setupEvents = function(inputElem, listener) {
|
|
|
|
|
util.addEvent(inputElem, 'input', listener);
|
|
|
|
|
inputElem.onpaste = listener;
|
|
|
|
|
inputElem.ondrop = listener;
|
|
|
|
|
@@ -775,7 +775,7 @@
|
|
|
|
|
util.addEvent(inputElem, 'keydown', listener);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var getDocScrollTop = function() {
|
|
|
|
|
var getDocScrollTop = function() {
|
|
|
|
|
var result = 0;
|
|
|
|
|
|
|
|
|
|
if (window.innerHeight) {
|
|
|
|
|
@@ -793,7 +793,7 @@
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var makePreviewHtml = function() {
|
|
|
|
|
var makePreviewHtml = function() {
|
|
|
|
|
// If there is no registered preview panel
|
|
|
|
|
// there is nothing to do.
|
|
|
|
|
if (!panels.preview)
|
|
|
|
|
@@ -821,13 +821,13 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// setTimeout is already used. Used as an event listener.
|
|
|
|
|
var applyTimeout = function() {
|
|
|
|
|
var applyTimeout = function() {
|
|
|
|
|
if (timeout) {
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
timeout = undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (startType !== 'manual') {
|
|
|
|
|
if (startType !== 'manual') {
|
|
|
|
|
var delay = 0;
|
|
|
|
|
|
|
|
|
|
if (startType === 'delayed') {
|
|
|
|
|
@@ -854,7 +854,7 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.refresh = function(requiresRefresh) {
|
|
|
|
|
this.refresh = function(requiresRefresh) {
|
|
|
|
|
if (requiresRefresh) {
|
|
|
|
|
oldInputText = '';
|
|
|
|
|
makePreviewHtml();
|
|
|
|
|
@@ -904,7 +904,7 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var pushPreviewHtml = function(text) {
|
|
|
|
|
var pushPreviewHtml = function(text) {
|
|
|
|
|
var emptyTop = position.getTop(panels.input) - getDocScrollTop();
|
|
|
|
|
|
|
|
|
|
if (panels.preview) {
|
|
|
|
|
@@ -930,7 +930,7 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var init = function() {
|
|
|
|
|
var init = function() {
|
|
|
|
|
setupEvents(panels.input, applyTimeout);
|
|
|
|
|
makePreviewHtml();
|
|
|
|
|
|
|
|
|
|
@@ -946,7 +946,7 @@
|
|
|
|
|
// And download dialog
|
|
|
|
|
// Most of this has been moved to CSS but the div creation and
|
|
|
|
|
// browser-specific hacks remain here.
|
|
|
|
|
ui.createBackground = function() {
|
|
|
|
|
ui.createBackground = function() {
|
|
|
|
|
var background = doc.createElement('div'),
|
|
|
|
|
style = background.style;
|
|
|
|
|
|
|
|
|
|
@@ -999,7 +999,7 @@
|
|
|
|
|
defaultInputText,
|
|
|
|
|
callback,
|
|
|
|
|
imageIsDecorativeLabel,
|
|
|
|
|
imageUploadHandler) {
|
|
|
|
|
imageUploadHandler) {
|
|
|
|
|
// These variables need to be declared at this level since they are used
|
|
|
|
|
// in multiple functions.
|
|
|
|
|
var dialog, // The dialog box.
|
|
|
|
|
@@ -1108,7 +1108,7 @@
|
|
|
|
|
imageIsDecorativeLabel: imageIsDecorativeLabel,
|
|
|
|
|
imageUploadHandler: imageUploadHandler
|
|
|
|
|
});
|
|
|
|
|
dialog.setAttribute('dir', doc.head.getAttribute('dir'));
|
|
|
|
|
dialog.setAttribute('dir', doc.head.getAttribute('dir'));
|
|
|
|
|
dialog.setAttribute('role', 'dialog');
|
|
|
|
|
dialog.setAttribute('tabindex', '-1');
|
|
|
|
|
dialog.setAttribute('aria-labelledby', 'editorDialogTitle');
|
|
|
|
|
@@ -1184,7 +1184,7 @@
|
|
|
|
|
|
|
|
|
|
// Why is this in a zero-length timeout?
|
|
|
|
|
// Is it working around a browser bug?
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
createDialog();
|
|
|
|
|
|
|
|
|
|
var defTextLen = defaultInputText.length;
|
|
|
|
|
@@ -1204,7 +1204,7 @@
|
|
|
|
|
}, 0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, imageUploadHandler) {
|
|
|
|
|
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, imageUploadHandler) {
|
|
|
|
|
var inputBox = panels.input,
|
|
|
|
|
buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements.
|
|
|
|
|
|
|
|
|
|
@@ -1215,9 +1215,9 @@
|
|
|
|
|
keyEvent = 'keypress';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
util.addEvent(inputBox, keyEvent, function(key) {
|
|
|
|
|
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) && !key.altKey && !key.shiftKey) {
|
|
|
|
|
if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) {
|
|
|
|
|
var keyCode = key.charCode || key.keyCode;
|
|
|
|
|
var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
|
|
|
|
|
|
|
|
|
|
@@ -1303,10 +1303,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Perform the button's action.
|
|
|
|
|
function doClick(button) {
|
|
|
|
|
function doClick(button) {
|
|
|
|
|
inputBox.focus();
|
|
|
|
|
|
|
|
|
|
if (button.textOp) {
|
|
|
|
|
if (button.textOp) {
|
|
|
|
|
if (undoManager) {
|
|
|
|
|
undoManager.setCommandMode();
|
|
|
|
|
}
|
|
|
|
|
@@ -1336,7 +1336,7 @@
|
|
|
|
|
// Yes this is awkward and I think it sucks, but there's
|
|
|
|
|
// no real workaround. Only the image and link code
|
|
|
|
|
// create dialogs and require the function pointers.
|
|
|
|
|
var fixupInputArea = function() {
|
|
|
|
|
var fixupInputArea = function() {
|
|
|
|
|
inputBox.focus();
|
|
|
|
|
|
|
|
|
|
if (chunks) {
|
|
|
|
|
@@ -1351,7 +1351,7 @@
|
|
|
|
|
|
|
|
|
|
if (!noCleanup) {
|
|
|
|
|
fixupInputArea();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (button.execute) {
|
|
|
|
|
@@ -1359,7 +1359,7 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setupButton(button, isEnabled) {
|
|
|
|
|
function setupButton(button, isEnabled) {
|
|
|
|
|
var normalYShift = '0px';
|
|
|
|
|
var disabledYShift = '-20px';
|
|
|
|
|
var highlightYShift = '-40px';
|
|
|
|
|
@@ -1429,7 +1429,7 @@
|
|
|
|
|
return function() { method.apply(commandManager, arguments); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function makeSpritedButtonRow() {
|
|
|
|
|
function makeSpritedButtonRow() {
|
|
|
|
|
var buttonBar = panels.buttonBar;
|
|
|
|
|
|
|
|
|
|
var normalYShift = '0px';
|
|
|
|
|
@@ -1442,9 +1442,9 @@
|
|
|
|
|
buttonRow.className = 'wmd-button-row';
|
|
|
|
|
buttonRow = buttonBar.appendChild(buttonRow);
|
|
|
|
|
var xPosition = 0;
|
|
|
|
|
var makeButton = function(id, title, XShift, textOp) {
|
|
|
|
|
var makeButton = function(id, title, XShift, textOp, tabIndex) {
|
|
|
|
|
var button = document.createElement('button');
|
|
|
|
|
button.tabIndex = 0;
|
|
|
|
|
button.tabIndex = tabIndex;
|
|
|
|
|
button.className = 'wmd-button';
|
|
|
|
|
button.style.left = xPosition + 'px';
|
|
|
|
|
xPosition += 25;
|
|
|
|
|
@@ -1468,35 +1468,35 @@
|
|
|
|
|
xPosition += 25;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
buttons.bold = makeButton('wmd-bold-button', gettext('Bold (Ctrl+B)'), '0px', bindCommand('doBold'));
|
|
|
|
|
buttons.italic = makeButton('wmd-italic-button', gettext('Italic (Ctrl+I)'), '-20px', bindCommand('doItalic'));
|
|
|
|
|
buttons.bold = makeButton('wmd-bold-button', gettext('Bold (Ctrl+B)'), '0px', bindCommand('doBold'), 0);
|
|
|
|
|
buttons.italic = makeButton('wmd-italic-button', gettext('Italic (Ctrl+I)'), '-20px', bindCommand('doItalic'), -1);
|
|
|
|
|
makeSpacer(1);
|
|
|
|
|
buttons.link = makeButton('wmd-link-button', gettext('Hyperlink (Ctrl+L)'), '-40px', bindCommand(function(chunk, postProcessing) {
|
|
|
|
|
return this.doLinkOrImage(chunk, postProcessing, false);
|
|
|
|
|
}));
|
|
|
|
|
buttons.quote = makeButton('wmd-quote-button', gettext('Blockquote (Ctrl+Q)'), '-60px', bindCommand('doBlockquote'));
|
|
|
|
|
buttons.code = makeButton('wmd-code-button', gettext('Code Sample (Ctrl+K)'), '-80px', bindCommand('doCode'));
|
|
|
|
|
}), -1);
|
|
|
|
|
buttons.quote = makeButton('wmd-quote-button', gettext('Blockquote (Ctrl+Q)'), '-60px', bindCommand('doBlockquote'), -1);
|
|
|
|
|
buttons.code = makeButton('wmd-code-button', gettext('Code Sample (Ctrl+K)'), '-80px', bindCommand('doCode'), -1);
|
|
|
|
|
buttons.image = makeButton('wmd-image-button', gettext('Image (Ctrl+G)'), '-100px', bindCommand(function(chunk, postProcessing) {
|
|
|
|
|
return this.doLinkOrImage(chunk, postProcessing, true, imageUploadHandler);
|
|
|
|
|
}));
|
|
|
|
|
}), -1);
|
|
|
|
|
makeSpacer(2);
|
|
|
|
|
buttons.olist = makeButton('wmd-olist-button', gettext('Numbered List (Ctrl+O)'), '-120px', bindCommand(function(chunk, postProcessing) {
|
|
|
|
|
this.doList(chunk, postProcessing, true);
|
|
|
|
|
}));
|
|
|
|
|
}), -1);
|
|
|
|
|
buttons.ulist = makeButton('wmd-ulist-button', gettext('Bulleted List (Ctrl+U)'), '-140px', bindCommand(function(chunk, postProcessing) {
|
|
|
|
|
this.doList(chunk, postProcessing, false);
|
|
|
|
|
}));
|
|
|
|
|
buttons.heading = makeButton('wmd-heading-button', gettext('Heading (Ctrl+H)'), '-160px', bindCommand('doHeading'));
|
|
|
|
|
buttons.hr = makeButton('wmd-hr-button', gettext('Horizontal Rule (Ctrl+R)'), '-180px', bindCommand('doHorizontalRule'));
|
|
|
|
|
}), -1);
|
|
|
|
|
buttons.heading = makeButton('wmd-heading-button', gettext('Heading (Ctrl+H)'), '-160px', bindCommand('doHeading'), -1);
|
|
|
|
|
buttons.hr = makeButton('wmd-hr-button', gettext('Horizontal Rule (Ctrl+R)'), '-180px', bindCommand('doHorizontalRule'), -1);
|
|
|
|
|
makeSpacer(3);
|
|
|
|
|
buttons.undo = makeButton('wmd-undo-button', gettext('Undo (Ctrl+Z)'), '-200px', null);
|
|
|
|
|
buttons.undo = makeButton('wmd-undo-button', gettext('Undo (Ctrl+Z)'), '-200px', null, -1);
|
|
|
|
|
buttons.undo.execute = function(manager) { if (manager) manager.undo(); };
|
|
|
|
|
|
|
|
|
|
var redoTitle = /win/.test(nav.platform.toLowerCase()) ?
|
|
|
|
|
gettext('Redo (Ctrl+Y)') :
|
|
|
|
|
gettext('Redo (Ctrl+Shift+Z)'); // mac and other non-Windows platforms
|
|
|
|
|
|
|
|
|
|
buttons.redo = makeButton('wmd-redo-button', redoTitle, '-220px', null);
|
|
|
|
|
buttons.redo = makeButton('wmd-redo-button', redoTitle, '-220px', null, -1);
|
|
|
|
|
buttons.redo.execute = function(manager) { if (manager) manager.redo(); };
|
|
|
|
|
|
|
|
|
|
if (helpOptions) {
|
|
|
|
|
@@ -1526,7 +1526,7 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.setUndoRedoButtonStates = setUndoRedoButtonStates;
|
|
|
|
|
this.setUndoRedoButtonStates = setUndoRedoButtonStates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function CommandManager(pluginHooks) {
|
|
|
|
|
@@ -1570,7 +1570,7 @@
|
|
|
|
|
// 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
|
|
|
|
|
commandProto.doBorI = function(chunk, postProcessing, nStars, insertText) {
|
|
|
|
|
commandProto.doBorI = function(chunk, postProcessing, nStars, insertText) {
|
|
|
|
|
// Get rid of whitespace and fixup newlines.
|
|
|
|
|
chunk.trimWhitespace();
|
|
|
|
|
chunk.selection = chunk.selection.replace(/\n{2,}/g, '\n');
|
|
|
|
|
@@ -1595,7 +1595,7 @@
|
|
|
|
|
var whitespace = re.$1;
|
|
|
|
|
chunk.before = chunk.before + starsAfter + whitespace;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
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) {
|
|
|
|
|
@@ -1611,7 +1611,7 @@
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandProto.stripLinkDefs = function(text, defsToAdd) {
|
|
|
|
|
commandProto.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) {
|
|
|
|
|
defsToAdd[id] = totalMatch.replace(/\s*$/, '');
|
|
|
|
|
@@ -1626,7 +1626,7 @@
|
|
|
|
|
return text;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandProto.addLinkDef = function(chunk, linkDef) {
|
|
|
|
|
commandProto.addLinkDef = function(chunk, linkDef) {
|
|
|
|
|
var refNumber = 0; // The current reference number
|
|
|
|
|
var defsToAdd = {}; //
|
|
|
|
|
// Start with a clean slate by removing all previous link definitions.
|
|
|
|
|
@@ -1707,12 +1707,12 @@
|
|
|
|
|
chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
|
|
|
|
|
var background;
|
|
|
|
|
|
|
|
|
|
if (chunk.endTag.length > 1 && chunk.startTag.length > 0) {
|
|
|
|
|
if (chunk.endTag.length > 1 && chunk.startTag.length > 0) {
|
|
|
|
|
chunk.startTag = chunk.startTag.replace(/!?\[/, '');
|
|
|
|
|
chunk.endTag = '';
|
|
|
|
|
this.addLinkDef(chunk, null);
|
|
|
|
|
this.addLinkDef(chunk, null);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else {
|
|
|
|
|
// We're moving start and end tag back into the selection, since (as we're in the else block) we're not
|
|
|
|
|
// *removing* a link, but *adding* one, so whatever findTags() found is now back to being part of the
|
|
|
|
|
// link text. linkEnteredCallback takes care of escaping any brackets.
|
|
|
|
|
@@ -1808,7 +1808,7 @@
|
|
|
|
|
|
|
|
|
|
// When making a list, hitting shift-enter will put your cursor on the next line
|
|
|
|
|
// at the current indent level.
|
|
|
|
|
commandProto.doAutoindent = function(chunk, postProcessing) {
|
|
|
|
|
commandProto.doAutoindent = function(chunk, postProcessing) {
|
|
|
|
|
var commandMgr = this,
|
|
|
|
|
fakeSelection = false;
|
|
|
|
|
|
|
|
|
|
@@ -1850,7 +1850,7 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandProto.doBlockquote = function(chunk, postProcessing) {
|
|
|
|
|
commandProto.doBlockquote = function(chunk, postProcessing) {
|
|
|
|
|
chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,
|
|
|
|
|
function(totalMatch, newlinesBefore, text, newlinesAfter) {
|
|
|
|
|
chunk.before += newlinesBefore;
|
|
|
|
|
@@ -1945,7 +1945,7 @@
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var replaceBlanksInTags = function(useBracket) {
|
|
|
|
|
var replaceBlanksInTags = function(useBracket) {
|
|
|
|
|
var replacement = useBracket ? '> ' : '';
|
|
|
|
|
|
|
|
|
|
if (chunk.startTag) {
|
|
|
|
|
@@ -1992,13 +1992,13 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandProto.doCode = function(chunk, postProcessing) {
|
|
|
|
|
commandProto.doCode = function(chunk, postProcessing) {
|
|
|
|
|
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)) {
|
|
|
|
|
if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) {
|
|
|
|
|
chunk.before = chunk.before.replace(/[ ]{4}$/,
|
|
|
|
|
function(totalMatch) {
|
|
|
|
|
chunk.selection = totalMatch + chunk.selection;
|
|
|
|
|
@@ -2055,7 +2055,7 @@
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandProto.doList = function(chunk, postProcessing, isNumberedList) {
|
|
|
|
|
commandProto.doList = function(chunk, postProcessing, isNumberedList) {
|
|
|
|
|
// 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*$/;
|
|
|
|
|
@@ -2083,7 +2083,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Fixes the prefixes of the other list items.
|
|
|
|
|
var getPrefixedItem = function(itemText) {
|
|
|
|
|
var getPrefixedItem = function(itemText) {
|
|
|
|
|
// The numbering flag is unset when called by autoindent.
|
|
|
|
|
if (isNumberedList === undefined) {
|
|
|
|
|
isNumberedList = /^\s*\d/.test(itemText);
|
|
|
|
|
@@ -2105,7 +2105,7 @@
|
|
|
|
|
chunk.startTag = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chunk.startTag) {
|
|
|
|
|
if (chunk.startTag) {
|
|
|
|
|
var hasDigits = /\d+[.]/.test(chunk.startTag);
|
|
|
|
|
chunk.startTag = '';
|
|
|
|
|
chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, '\n');
|
|
|
|
|
@@ -2151,10 +2151,10 @@
|
|
|
|
|
chunk.startTag = prefix;
|
|
|
|
|
var spaces = prefix.replace(/./g, ' ');
|
|
|
|
|
this.wrap(chunk, SETTINGS.lineLength - spaces.length);
|
|
|
|
|
chunk.selection = chunk.selection.replace(/\n/g, '\n' + spaces);
|
|
|
|
|
chunk.selection = chunk.selection.replace(/\n/g, '\n' + spaces);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandProto.doHeading = function(chunk, postProcessing) {
|
|
|
|
|
commandProto.doHeading = function(chunk, postProcessing) {
|
|
|
|
|
// 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, '');
|
|
|
|
|
@@ -2196,7 +2196,7 @@
|
|
|
|
|
// If it's already a level 1 header, it's removed.
|
|
|
|
|
var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1;
|
|
|
|
|
|
|
|
|
|
if (headerLevelToCreate > 0) {
|
|
|
|
|
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 ? '-' : '=';
|
|
|
|
|
@@ -2215,5 +2215,5 @@
|
|
|
|
|
chunk.startTag = '----------\n';
|
|
|
|
|
chunk.selection = '';
|
|
|
|
|
chunk.skipLines(2, 1, true);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
})();
|
|
|
|
|
|