Lively Kernel canvas
//
DraftingMultipleUndo....40
//newTestRunner().openIn(WorldMorph.current(),pt(500,100))TestCase.subclass("TextReplacementCommandTest",{setUp:function(){this.text=newTextMorph(newRectangle(100,100,10,10));this.text.renderAfterReplacement=function(){};},testUndoAndRedo:function(){this.text.setTextString("Hello");varcmd=newReplaceTextCommand(this.text,0,"","H");cmd.undo();this.assertEqual(this.text.textString,"ello","undofirstbroken")cmd.redo();this.assertEqual(this.text.textString,"Hello","redofirstbroken")cmd=newReplaceTextCommand(this.text,1,"abcde","ell");cmd.undo();this.assertEqual(this.text.textString,"Habcdeo","undomiddlebroken")cmd.redo();this.assertEqual(this.text.textString,"Hello","redomiddlebroken")cmd=newReplaceTextCommand(this.text,5,"Last","");cmd.undo();this.assertEqual(this.text.textString,"HelloLast","undolastbroken")cmd.redo();this.assertEqual(this.text.textString,"Hello","redolastbroken")},testUndoRichText:function(){this.text.setTextString("Hello");varoldText=newlively.Text.Text("World",{color:Color.green});varcmd=newReplaceTextCommand(this.text,6,oldText,"");cmd.undo();this.assertEqual(this.text.textString,"HelloWorld","undotextStringbroken");this.assertEqual(this.text.textStyle.valueAt(7).color,Color.green,"undotextStringbroken")},testSlicdeRichText:function(){this.text.setTextString("HelloWorldhowareyou?");this.text.setSelectionRange(6,12);this.text.emphasizeSelection({color:Color.green});varstyleSlice=this.text.textStyle.slice(4,14);this.assertEqual(styleSlice.valueAt(0).color,undefined,"0");this.assertEqual(styleSlice.valueAt(3).color,Color.green,"1");this.assert(styleSlice.runs,"norunsinslice");this.assertEqual(styleSlice.runs.length,3,"wrongnumberofruns");varstringSlice=this.text.textString.slice(4,14);vartextObj=newlively.Text.Text(stringSlice,styleSlice);this.text.setSelectionRange(1,2);this.text.replaceSelectionWith(textObj);console.log("text:"+textObj);},});TestCase.subclass("UndoHistoryTest",{setUp:function(){this.sut=newUndoHistory();},testAddCommand:function(){varcmd=newUndoableCommand();this.sut.addCommand(cmd);this.assertEqual(this.sut.undoStack.length,1)},testUndo:function(){varundoWasRun=false;varcmd=newUndoableCommand();cmd.undo=function(){undoWasRun=true};this.sut.addCommand(cmd);this.sut.undo();this.assertEqual(this.sut.undoStack.length,0);this.assert(undoWasRun,"undowasnotperformed");this.assertEqual(this.sut.redoStack.length,1)},testForgetRedoHistoryAfterNewCommand:function(){this.sut.addCommand(newUndoableCommand());this.sut.undo();this.assertEqual(this.sut.redoStack.length,1);this.sut.addCommand(newUndoableCommand());this.assertEqual(this.sut.undoStack.length,1);this.assertEqual(this.sut.redoStack.length,0);},testRedo:function(){varredoWasRun=false;varcmd=newUndoableCommand();cmd.redo=function(){redoWasRun=true};this.sut.addCommand(cmd);this.sut.undo();this.sut.redo();this.assertEqual(this.sut.undoStack.length,1);this.assert(redoWasRun,"undowasnotperformed");this.assertEqual(this.sut.redoStack.length,0);},testHasUndoableCommand:function(){this.assertEqual(this.sut.hasUndoableCommand(),false);varcmd=newUndoableCommand();this.sut.addCommand(cmd);this.assertEqual(this.sut.hasUndoableCommand(),true);this.sut.undo();this.sut.undo();//emtpy},});TestCase.subclass("TextWithUndoStackTest",{setUp:function(){this.text=newTextMorph(newRectangle(100,100,10,10));this.text.setWithLayers([UndoLayer]);//textcompositionseemstodependonatextisisactuallydisplayedinaworld//sowedisablethenonworkingpart...this.text.renderAfterReplacement=function(){};//WorldMorph.current().addMorph(this.text);},testSetTextStringProducesCommand:function(){this.text.undoHistory=newUndoHistory();this.text.setTextString("ANewText");this.assert(this.text.undoHistory.hasUndoableCommand(),"noundoablecmd")},testUndoDoesNotProduceAnUndo:function(){this.text.setTextString("OldText");this.text.undoHistory=newUndoHistory();this.text.setTextString("NewText");varaddCommandExecuted=false;this.text.addCommand=function(){addCommandExecuted=true}this.text.undoHistory.undo();this.assert(!addCommandExecuted,"addCommandwasexecutedinundo");this.assertEqual(this.text.undoHistory.undoStack.length,0);this.assertEqual(this.text.textString,"OldText","undodidnotwork")},testMultipleUndoAndRedo:function(){this.text.setTextString("OldText");this.text.undoHistory=newUndoHistory();this.text.setTextString("NewText1");this.text.setTextString("NewText2");this.text.setTextString("NewText3");this.assertEqual(this.text.textString,"NewText3","undodidnotwork");this.text.undoHistory.undo();this.assertEqual(this.text.textString,"NewText2","undodidnotwork");this.text.undoHistory.undo();this.assertEqual(this.text.textString,"NewText1","undodidnotwork");this.text.undoHistory.undo();this.assertEqual(this.text.textString,"OldText","undodidnotwork");this.text.undoHistory.undo();this.assertEqual(this.text.textString,"OldText","undoafteremtydidnotwork");this.text.undoHistory.redo();this.assertEqual(this.text.textString,"NewText1","redodidnotwork");},testReplaceSelectionTriggersUndo:function(){this.text.setTextString("OldText");this.text.undoHistory=newUndoHistory();this.text.setSelectionRange(0,3);this.text.replaceSelectionWith("New");this.assertEqual(this.text.textString,"NewText","replacedidnotwork");this.text.undoHistory.undo();this.assertEqual(this.text.textString,"OldText","undodidnotwork");},testUndoPreservesStyle:function(){this.text.setTextString("OldText");this.text.setSelectionRange(0,3);this.text.emphasizeSelection({color:Color.green});this.assertEqual(this.text.textStyle.valueAt(1).color,Color.green,"stylingbroken");this.text.undoHistory=newUndoHistory();this.text.setSelectionRange(0,3);this.text.replaceSelectionWith("");this.text.undoHistory.undo();this.assertEqual(this.text.textStyle.valueAt(1).color,Color.green,"undoforgetsstyle");},tearDown:function(){//this.text.remove();}});6truetruefalsetruetruetruefalse
false1truefalsetruetruefalse
nullfalsetruenullfalse
falsenullfalse
false
false
falsenullfalsefalsenullnullnullnullfalse
-----falsetruefalse0
worldis1:WorldMorph([0,0,2000,1200])falsetruefalse0
startingWikiNavigatorfalsetruefalse0
status207onPROPFINDhttp://lively-kernel.org/repository/webwerkstatt/DraftUndo.xhtmlfalsetruefalse0
http://lively-kernel.org/repository/webwerkstatt/anonymous_module_2loadedin40msfalsetruefalse0
http://lively-kernel.org/repository/webwerkstatt/anonymous_module_1loadedin378msfalsetruefalse0
fontHelvetica40:spacewidth11from51xWidth20falsetruefalse0
fitWidthfailureonTextMorph.getCharBoundsfalsetruefalse0
fontHelvetica12:spacewidth3from15xWidth6falsetruefalse0
fitWidthfailureonTextMorph.getCharBoundsfalsetruefalse0
ResizingSVGcanvasfalsetruefalse0
Moduleloadcheckdone.28modulesloaded.falsetruefalse0
-------------------------------------------falsetruefalse0
invoke1:WorldMorph([0,0,2000,1200])falsetruefalse0
fitWidthfailureonTextMorph.getCharBoundsfalsetruefalse0
fitWidthfailureonTextMorph.getCharBoundsfalsetruefalse0
status200onGEThttp://lively-kernel.org/repository/webwerkstatt/DraftUndo.xhtmlfalsetruefalse0
extendForSerializationundefinedfalsetruefalse01087-1nulltrue0falsetruetruefalse
false1truefalse
nullfalse
nullfalse
nullfalse
nullfalsefalsetruetruefalse
truetrue100false
truefalsenull050
nullfalsetruenullfalse
Consolefalsenullfalse
false
false
falsenullfalsefalsenullnullnullnullfalse
Wasgehtabfunktioniertdashier
Object.subclass("UndoHistory",{initialize:function(){this.undoStack=[];this.redoStack=[];},addCommand:function(cmd){this.undoStack.push(cmd);this.redoStack=[];//redostackisinvalidnow},undo:function(){if(!this.hasUndoableCommand())return;varcmd=this.undoStack.pop();cmd.undo();this.redoStack.push(cmd);},redo:function(){if(!this.hasRedoableCommand())return;varcmd=this.redoStack.pop();cmd.redo();this.undoStack.push(cmd);},hasUndoableCommand:function(){returnthis.undoStack.length>0},hasRedoableCommand:function(){returnthis.redoStack.length>0},});Object.subclass("UndoableCommand",{undo:function(){},redo:function(){},});UndoableCommand.subclass("ReplaceTextCommand",{initialize:function(morph,index,oldText,newText){this.morph=morph;this.index=index;this.oldText=oldText;this.newText=newText;},undo:function(){withoutLayers([UndoLayer],function(){this.morph.setSelectionRange(this.index,this.index+this.newText.length);this.morph.replaceSelectionWith(this.oldText);}.bind(this))},redo:function(){withoutLayers([UndoLayer],function(){this.morph.setSelectionRange(this.index,this.index+this.oldText.length);this.morph.replaceSelectionWith(this.newText);varpos=this.index+this.newText.length;this.morph.setSelectionRange(pos,pos)}.bind(this))},});createLayer("UndoLayer")layerClass(UndoLayer,TextMorph,{getUndoHistory:function(){if(!this.undoHistory)this.undoHistory=newUndoHistory();returnthis.undoHistory},processCommandKeys:function(proceed,evt){varkey=evt.getKeyChar();if(key)key=key.toLowerCase();if(key=='z'&&evt.isShiftDown()){this.doRedo();returntrue;};returnproceed(evt)},doRedo:function(proceed){varundoHistory=this.getUndoHistory();if(undoHistory){returnundoHistory.redo()}},doUndo:function(proceed){varundoHistory=this.getUndoHistory();if(undoHistory){returnundoHistory.undo()}else{returnproceed()}},replaceSelectionWith:function(proceed,replacement){//console.log("replacewith"+replacement);varundoHistory=this.getUndoHistory();varselStart=this.selectionRange[0];varselStop=this.selectionRange[1];varoldText=this.getSelectionString();if(undoHistory){//console.log("replace"+oldText+"at"+selStart+"with"+replacement);if(this.textStyle){varstyle=this.textStyle.slice(selStart,selStop+1);//console.log("style"+style)}if(style){vartext=newlively.Text.Text(oldText,style);//console.log("text"+text);oldText=text;}varcmd=newReplaceTextCommand(this,selStart,oldText,replacement)undoHistory.addCommand(cmd);};varresult;withoutLayers([UndoLayer],function(){result=proceed(replacement);})returnresult},//hereisthebug....//tofix,interceptreplaceSelectionWith:function(replacement){pvtUpdateTextString:function(proceed,replacement,replacementHints){varundoHistory=this.getUndoHistory();//logStack();//console.log("DEBUGruns"+this.textStyle)if(replacementHints){varrh=replacementHints;varoldText=this.textString.slice(rh.selStart,rh.selStop+1);varnewText=replacement.slice(rh.selStart,rh.selStart+rh.repLength);//console.log("updatetext:'"+oldText+"'with'"+newText+"'hints:"+//rh.selStart+""+rh.selStop+""+rh.repLength)if(undoHistory){varcmd=newReplaceTextCommand(this,rh.selStart,oldText,newText)undoHistory.addCommand(cmd);}}else{if(undoHistory){//replaceall//varoldText=newlively.Text.Text(this.textString,this.textStyle);varcmd=newReplaceTextCommand(this,0,this.textString,replacement)undoHistory.addCommand(cmd);}//console.log("noreplacementhints..."+replacement)}returnproceed(replacement,replacementHints)},}); 0\r\t},\r\r\thasRedoableCommand: function() {\n\t\treturn this.redoStack.length > 0\n\t},\n\r});\r\rObject.subclass(\"UndoableCommand\", {\n\n\tundo: function() {},\n\tredo: function() {},\n\n});\n\rUndoableCommand.subclass(\"ReplaceTextCommand\", {\n\tinitialize: function(morph, index, oldText, newText) {\r\t\tthis.morph = morph;\r\t\tthis.index = index;\r\t\tthis.oldText = oldText;\r\t\tthis.newText = newText;\r\t},\r\r\tundo: function() {\r\t\twithoutLayers([UndoLayer], function() {\r\t\t\tthis.morph.setSelectionRange(this.index, this.index + this.newText.length);\r\t\t\tthis.morph.replaceSelectionWith(this.oldText);\r\t\t}.bind(this))\r\t},\t\r\n\tredo: function() {\n\t\twithoutLayers([UndoLayer], function() {\n\t\t\tthis.morph.setSelectionRange(this.index, this.index + this.oldText.length);\n\t\t\tthis.morph.replaceSelectionWith(this.newText);\r\t\t\tvar pos = this.index + this.newText.length;\r\t\t\tthis.morph.setSelectionRange(pos, pos)\r\t\t}.bind(this))\n\t},\t\r});\n\rcreateLayer(\"UndoLayer\")\r\r\nlayerClass(UndoLayer, TextMorph, {\n\r\tgetUndoHistory: function() {\r\t\tif (!this.undoHistory)\r\t\t\tthis.undoHistory = new UndoHistory();\r\r\t\treturn this.undoHistory\r\t},\r\r\tprocessCommandKeys: function(proceed, evt) {\r\t\tvar key = evt.getKeyChar();\r\t\tif (key) key = key.toLowerCase();\r\t\tif (key == 'z' && evt.isShiftDown()) {\n\t\t\tthis.doRedo(); return true;\n\t\t};\r\t\treturn proceed(evt)\r\t},\r\r\tdoRedo: function(proceed) {\n\t\tvar undoHistory = this.getUndoHistory();\n\t\tif (undoHistory) {\n\t\t\treturn undoHistory.redo()\n\t\t}\n\t},\n\r\tdoUndo: function(proceed) {\r\t\tvar undoHistory = this.getUndoHistory();\r\t\tif (undoHistory) {\r\t\t\treturn undoHistory.undo()\r\t\t} else {\r\t\t\treturn proceed()\r\t\t}\r\t},\r\r\treplaceSelectionWith: function(proceed, replacement) {\r\t\t// console.log(\"replace with \" + replacement);\r\t\tvar undoHistory = this.getUndoHistory();\n\r\t\tvar selStart = this.selectionRange[0];\n\t\tvar selStop = this.selectionRange[1];\r\t\tvar oldText = this.getSelectionString();\r\t\tif (undoHistory) {\r\t\t\t// console.log(\"replace \" + oldText + \" at \" + selStart + \" with \" + replacement);\r\t\t\tif (this.textStyle) {\r\t\t\t\tvar style = this.textStyle.slice(selStart, selStop + 1);\r\t\t\t\t// console.log(\"style \" + style)\r\t\t\t}\r\t\t\tif (style) {\r\t\t\t\tvar text = new lively.Text.Text(oldText, style);\r\t\t\t\t// console.log(\"text \" + text);\r\t\t\t\toldText = text;\r\t\t\t}\n\t\t\tvar cmd = new ReplaceTextCommand(this, selStart, oldText, replacement)\n\t\t\tundoHistory.addCommand(cmd);\n\t\t};\n\r\t\tvar result;\r\t\twithoutLayers([UndoLayer], function(){\r\t\t\tresult = proceed(replacement);\r\t\t})\r\t\treturn result\r\t},\r\r\r\t// here is the bug....\r\t// to fix, intercept replaceSelectionWith: function(replacement) { \n\tpvtUpdateTextString: function(proceed, replacement, replacementHints) {\n\t\tvar undoHistory = this.getUndoHistory();\r\t\t// logStack();\r\t\t// console.log(\"DEBUG runs \" + this.textStyle)\r\t\tif (replacementHints) {\n\t\t\tvar rh = replacementHints;\r\t\t\tvar oldText = this.textString.slice(rh.selStart, rh.selStop + 1);\r\t\t\tvar newText = replacement.slice(rh.selStart, rh.selStart + rh.repLength);\n\t\t\t// console.log(\"update text: '\" + oldText + \"' with '\" + newText + \"' hints: \" + \n\t\t\t//\trh.selStart + \" \" + rh.selStop + \" \" + rh.repLength)\n\r\t\t\tif (undoHistory) {\n\t\t\t\tvar cmd = new ReplaceTextCommand(this, rh.selStart, oldText, newText)\n\t\t\t\tundoHistory.addCommand(cmd);\n\t\t\t}\r\t\t} else {\n\t\t\tif (undoHistory) {\r\t\t\t\t// replace all\r\t\t\t\t// var oldText = new lively.Text.Text(this.textString, this.textStyle);\r\t\t\t\n\t\t\t\tvar cmd = new ReplaceTextCommand(this, 0, this.textString, replacement)\n\t\t\t\tundoHistory.addCommand(cmd);\n\t\t\t}\r\t\t\t// console.log(\"no replacement hints... \" + replacement) \n\t\t}\n\t\treturn proceed(replacement, replacementHints)\n\n\t},\n\n}); \n\n\r\r"]]>131truetruefalsetruetruetruefalse
false1truefalsenulltruetruefalse
nullfalsetruenullfalse
falsenullfalse
false
false
falsenullfalsefalsenullnullnullnullfalse
null70