/** * undo redo * @file * @since 1.2.6.1 */ /** * 撤销上一次执行的命令 * @command undo * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * editor.execCommand( 'undo' ); * ``` */ /** * 重做上一次执行的命令 * @command redo * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * editor.execCommand( 'redo' ); * ``` */ UE.plugins['undo'] = function () { var saveSceneTimer; var me = this, maxUndoCount = me.options.maxUndoCount || 20, maxInputCount = me.options.maxInputCount || 20, fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的 var noNeedFillCharTags = { ol:1,ul:1,table:1,tbody:1,tr:1,body:1 }; var orgState = me.options.autoClearEmptyNode; function compareAddr(indexA, indexB) { if (indexA.length != indexB.length) return 0; for (var i = 0, l = indexA.length; i < l; i++) { if (indexA[i] != indexB[i]) return 0 } return 1; } function compareRangeAddress(rngAddrA, rngAddrB) { if (rngAddrA.collapsed != rngAddrB.collapsed) { return 0; } if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) { return 0; } return 1; } function UndoManager() { this.list = []; this.index = 0; this.hasUndo = false; this.hasRedo = false; this.undo = function () { if (this.hasUndo) { if (!this.list[this.index - 1] && this.list.length == 1) { this.reset(); return; } while (this.list[this.index].content == this.list[this.index - 1].content) { this.index--; if (this.index == 0) { return this.restore(0); } } this.restore(--this.index); } }; this.redo = function () { if (this.hasRedo) { while (this.list[this.index].content == this.list[this.index + 1].content) { this.index++; if (this.index == this.list.length - 1) { return this.restore(this.index); } } this.restore(++this.index); } }; this.restore = function () { var me = this.editor; var scene = this.list[this.index]; var root = UE.htmlparser(scene.content.replace(fillchar, '')); me.options.autoClearEmptyNode = false; me.filterInputRule(root); me.options.autoClearEmptyNode = orgState; //trace:873 //去掉展位符 me.document.body.innerHTML = root.toHtml(); me.fireEvent('afterscencerestore'); //处理undo后空格不展位的问题 if (browser.ie) { utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){ if(domUtils.isEmptyNode(node)){ domUtils.fillNode(me.document, node); } }) } try{ var rng = new dom.Range(me.document).moveToAddress(scene.address); rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]); }catch(e){} this.update(); this.clearKey(); //不能把自己reset了 me.fireEvent('reset', true); }; this.getScene = function () { var me = this.editor; var rng = me.selection.getRange(), rngAddress = rng.createAddress(false,true); me.fireEvent('beforegetscene'); var root = UE.htmlparser(me.body.innerHTML); me.options.autoClearEmptyNode = false; me.filterOutputRule(root); me.options.autoClearEmptyNode = orgState; var cont = root.toHtml(); //trace:3461 //这个会引起回退时导致空格丢失的情况 // browser.ie && (cont = cont.replace(/> <').replace(/\s*\s*/g, '>')); me.fireEvent('aftergetscene'); return { address:rngAddress, content:cont } }; this.save = function (notCompareRange,notSetCursor) { clearTimeout(saveSceneTimer); var currentScene = this.getScene(notSetCursor), lastScene = this.list[this.index]; if(lastScene && lastScene.content != currentScene.content){ me.trigger('contentchange') } //内容相同位置相同不存 if (lastScene && lastScene.content == currentScene.content && ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) ) ) { return; } this.list = this.list.slice(0, this.index + 1); this.list.push(currentScene); //如果大于最大数量了,就把最前的剔除 if (this.list.length > maxUndoCount) { this.list.shift(); } this.index = this.list.length - 1; this.clearKey(); //跟新undo/redo状态 this.update(); }; this.update = function () { this.hasRedo = !!this.list[this.index + 1]; this.hasUndo = !!this.list[this.index - 1]; }; this.reset = function () { this.list = []; this.index = 0; this.hasUndo = false; this.hasRedo = false; this.clearKey(); }; this.clearKey = function () { keycont = 0; lastKeyCode = null; }; } me.undoManger = new UndoManager(); me.undoManger.editor = me; function saveScene() { this.undoManger.save(); } me.addListener('saveScene', function () { var args = Array.prototype.splice.call(arguments,1); this.undoManger.save.apply(this.undoManger,args); }); // me.addListener('beforeexeccommand', saveScene); // me.addListener('afterexeccommand', saveScene); me.addListener('reset', function (type, exclude) { if (!exclude) { this.undoManger.reset(); } }); me.commands['redo'] = me.commands['undo'] = { execCommand:function (cmdName) { this.undoManger[cmdName](); }, queryCommandState:function (cmdName) { return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1; }, notNeedUndo:1 }; var keys = { // /*Backspace*/ 8:1, /*Delete*/ 46:1, /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1, 37:1, 38:1, 39:1, 40:1 }, keycont = 0, lastKeyCode; //输入法状态下不计算字符数 var inputType = false; me.addListener('ready', function () { domUtils.on(this.body, 'compositionstart', function () { inputType = true; }); domUtils.on(this.body, 'compositionend', function () { inputType = false; }) }); //快捷键 me.addshortcutkey({ "Undo":"ctrl+90", //undo "Redo":"ctrl+89" //redo }); var isCollapsed = true; me.addListener('keydown', function (type, evt) { var me = this; var keyCode = evt.keyCode || evt.which; if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { if (inputType) return; if(!me.selection.getRange().collapsed){ me.undoManger.save(false,true); isCollapsed = false; return; } if (me.undoManger.list.length == 0) { me.undoManger.save(true); } clearTimeout(saveSceneTimer); function save(cont){ cont.undoManger.save(false,true); cont.fireEvent('selectionchange'); } saveSceneTimer = setTimeout(function(){ if(inputType){ var interalTimer = setInterval(function(){ if(!inputType){ save(me); clearInterval(interalTimer) } },300) return; } save(me); },200); lastKeyCode = keyCode; keycont++; if (keycont >= maxInputCount ) { save(me) } } }); me.addListener('keyup', function (type, evt) { var keyCode = evt.keyCode || evt.which; if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { if (inputType) return; if(!isCollapsed){ this.undoManger.save(false,true); isCollapsed = true; } } }); //扩展实例,添加关闭和开启命令undo me.stopCmdUndo = function(){ me.__hasEnterExecCommand = true; }; me.startCmdUndo = function(){ me.__hasEnterExecCommand = false; } };