Lively Kernel canvas // -----loadingscripthttps://lively-kernel.org/repository/webwerkstatt2011/lively/LKWiki.jshttps://lively-kernel.org/repository/webwerkstatt2011/anonymous_module_4loadedin1821mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/lib.jsloadedin768mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/ometa-base.jsloadedin1047mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/parser.jsloadedin1032mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/LKFileParser.jsloadedin1942mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/bs-js-compiler.jsloadedin803mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/bs-ometa-compiler.jsloadedin807mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/bs-ometa-optimizer.jsloadedin812mshttps://lively-kernel.org/repository/webwerkstatt2011/ometa/lk-parser-extensions.jsloadedin1073mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/Ometa.jsloadedin1965mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/ide/FileParsing.jsloadedin2404mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/ide/SourceDatabase.jsloadedin2959mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/ide/SystemCodeBrowser.jsloadedin3092mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/ide.jsloadedin3097mshttps://lively-kernel.org/repository/webwerkstatt2011/lively/LKWiki.jsloadedin302msstartingWikiNavigatorhttps://lively-kernel.org/repository/webwerkstatt2011/anonymous_module_5loadedin455ms050ConsoleDraftingMultipleUndo....1505RuntestsUndoTestmodule("lively.Tests.UndoTest").requires().toRun(function(){//newTestRunner().openIn(WorldMorph.current(),pt(500,100))TestCase.subclass("lively.Tests.UndoTest.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("lively.Tests.UndoTest.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("lively.Tests.UndoTest.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();}});});TheProblemwiththisUndoimplementationis,thattherearestillbugsintheTextMorphwhenithasnotcontents...GroßesEhrenwort!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(){console.log("undofrom"+this.index+"to"+this.newText.size())withoutLayers([UndoLayer],function(){this.morph.setSelectionRange(this.index,this.index+this.newText.size());this.morph.replaceSelectionWith(this.oldText);}.bind(this))},redo:function(){withoutLayers([UndoLayer],function(){this.morph.setSelectionRange(this.index,this.index+this.oldText.size());this.morph.replaceSelectionWith(this.newText);varpos=this.index+this.newText.size();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()}},textSliceFromTo:function(proceed,from,to){varstring=this.textString.substring(from,to+1);if(this.textStyle){varstyle=this.textStyle.slice(from,to+1);}returnnewlively.Text.Text(string,style);},replaceSelectionWith:function(proceed,replacement){varundoHistory=this.getUndoHistory();if(undoHistory){varfrom=this.selectionRange[0];varto=this.selectionRange[1];varoldText=this.textSliceFromTo(from,to);varcmd=newReplaceTextCommand(this,from,oldText,replacement)undoHistory.addCommand(cmd);};withoutLayers([UndoLayer],function(){returnproceed(replacement);})},emphasizeFromTo:function(proceed,emph,from,to){varundoHistory=this.getUndoHistory();varoldText=this.textSliceFromTo(from,to);withoutLayers([UndoLayer],function(){proceed(emph,from,to);})varnewText=this.textSliceFromTo(from,to);if(undoHistory){varcmd=newReplaceTextCommand(this,from,oldText,newText)this.getUndoHistory().addCommand(cmd);}},setTextString:function(proceed,string){varundoHistory=this.getUndoHistory();if(undoHistory){varfrom=0;varto=this.textString.size()-1;varoldText=this.textSliceFromTo(from,to);varcmd=newReplaceTextCommand(this,from,oldText,string)undoHistory.addCommand(cmd);};withoutLayers([UndoLayer],function(){proceed(string);})},});$morph('undoTestText').undoHistory=nullWikicontrol