diff options
Diffstat (limited to 'js/sceditor/development/plugins/undo.js')
| -rw-r--r-- | js/sceditor/development/plugins/undo.js | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/js/sceditor/development/plugins/undo.js b/js/sceditor/development/plugins/undo.js new file mode 100644 index 00000000..7abedbf4 --- /dev/null +++ b/js/sceditor/development/plugins/undo.js @@ -0,0 +1,187 @@ +(function ($) { + 'use strict'; + + $.sceditor.plugins.undo = function () { + var base = this; + var editor; + var charChangedCount = 0; + var previousValue; + + var undoLimit = 50; + var redoStates = []; + var undoStates = []; + var ignoreNextValueChanged = false; + + /** + * Sets the editor to the specified state. + * + * @param {Object} state + * @private + */ + var applyState = function (state) { + ignoreNextValueChanged = true; + + previousValue = state.value; + + editor.sourceMode(state.sourceMode); + editor.val(state.value, false); + editor.focus(); + + if (state.sourceMode) { + editor.sourceEditorCaret(state.caret); + } else { + editor.getRangeHelper().restoreRange(); + } + + ignoreNextValueChanged = false; + }; + + + /** + * Caluclates the number of characters that have changed + * between two strings. + * + * @param {String} strA + * @param {String} strB + * @return {String} + * @private + */ + var simpleDiff = function (strA, strB) { + var start, end, aLenDiff, bLenDiff, + aLength = strA.length, + bLength = strB.length, + length = Math.max(aLength, bLength); + + // Calculate the start + for (start = 0; start < length; start++) { + if (strA.charAt(start) !== strB.charAt(start)) { + break; + } + } + + // Calculate the end + aLenDiff = aLength < bLength ? bLength - aLength : 0; + bLenDiff = bLength < aLength ? aLength - bLength : 0; + + for (end = length - 1; end >= 0; end--) { + if (strA.charAt(end - aLenDiff) !== + strB.charAt(end - bLenDiff)) { + break; + } + } + + return (end - start) + 1; + }; + + base.init = function () { + // The this variable will be set to the instance of the editor + // calling it, hence why the plugins "this" is saved to the base + // variable. + editor = this; + + undoLimit = editor.undoLimit || undoLimit; + + // addShortcut is the easiest way to add handlers to specific + // shortcuts + editor.addShortcut('ctrl+z', base.undo); + editor.addShortcut('ctrl+shift+z', base.redo); + editor.addShortcut('ctrl+y', base.redo); + }; + + base.undo = function () { + var state = undoStates.pop(); + var rawEditorValue = editor.val(null, false); + + if (state && !redoStates.length && rawEditorValue === state.value) { + state = undoStates.pop(); + } + + if (state) { + if (!redoStates.length) { + redoStates.push({ + 'caret': editor.sourceEditorCaret(), + 'sourceMode': editor.sourceMode(), + 'value': rawEditorValue + }); + } + + redoStates.push(state); + applyState(state); + } + + return false; + }; + + base.redo = function () { + var state = redoStates.pop(); + + if (!undoStates.length) { + undoStates.push(state); + state = redoStates.pop(); + } + + if (state) { + undoStates.push(state); + applyState(state); + } + + return false; + }; + + base.signalReady = function () { + var rawValue = editor.val(null, false); + + // Store the initial value as the last value + previousValue = rawValue; + + undoStates.push({ + 'caret': this.sourceEditorCaret(), + 'sourceMode': this.sourceMode(), + 'value': rawValue + }); + }; + + /** + * Handle the valueChanged signal. + * + * e.rawValue will either be the raw HTML from the WYSIWYG editor with + * the rangeHelper range markers inserted, or it will be the raw value + * of the source editor (BBCode or HTML depening on plugins). + * @return {void} + */ + base.signalValuechangedEvent = function (e) { + var rawValue = e.rawValue; + + if (undoLimit > 0 && undoStates.length > undoLimit) { + undoStates.shift(); + } + + // If the editor hasn't fully loaded yet, + // then the previous value won't be set. + if (ignoreNextValueChanged || !previousValue || + previousValue === rawValue) { + return; + } + + // Value has changed so remove all redo states + redoStates.length = 0; + charChangedCount += simpleDiff(previousValue, rawValue); + + if (charChangedCount < 20) { + return; + // ?? + } else if (charChangedCount < 50 && !/\s$/g.test(e.rawValue)) { + return; + } + + undoStates.push({ + 'caret': editor.sourceEditorCaret(), + 'sourceMode': editor.sourceMode(), + 'value': rawValue + }); + + charChangedCount = 0; + previousValue = rawValue; + }; + }; +}(jQuery)); |
