From: Aleksander Łukasz Date: Mon, 25 Nov 2013 16:07:13 +0000 (+0100) Subject: undoredo wip X-Git-Url: https://git.mdrn.pl/fnpeditor.git/commitdiff_plain/0bd5926e9f6c5057d80e829ca5af83b19a58ecfc undoredo wip --- diff --git a/src/smartxml/smartxml.js b/src/smartxml/smartxml.js index 021a116..fa5f551 100644 --- a/src/smartxml/smartxml.js +++ b/src/smartxml/smartxml.js @@ -25,6 +25,14 @@ var INSERTION = function(implementation) { return toret; }; +// var TRANSFORMATION = function(name, implementation) { +// //implementation._isTransformation = true; + +// createDumbTransformation(name, implementation, ) + +// return implementation; +// }; + var DocumentNode = function(nativeNode, document) { if(!document) { throw new Error('undefined document for a node'); @@ -446,6 +454,8 @@ var parseXML = function(xml) { var Document = function(xml) { this.loadXML(xml); + this.undoStack = []; + this.redoStack = []; }; $.extend(Document.prototype, Backbone.Events, { @@ -620,6 +630,40 @@ $.extend(Document.prototype, Backbone.Events, { defineDocumentProperties(this, insertion.ofNode._$); insertion.ofNode.triggerChangeEvent('nodeAdded'); return insertion.ofNode; + }, + + transform: function(transformationName, args) { + var Transformation = transformations[transformationName], + transformation; + if(Transformation) { + transformation = new Transformation(args); + transformation.run(); + this.undoStack.push(transformation); + } else { + throw new Error('Transformation ' + transformationName + ' doesn\'t exist!'); + } + }, + undo: function() { + var transformation = this.undoStack.pop(); + if(transformation) { + transformation.undo(); + this.redoStack.push(transformation); + } + }, + redo: function() { + var transformation = this.redoStack.pop(); + if(transformation) { + transformation.run(); + this.undoStack.push(transformation); + } + }, + + getNodeByPath: function(path) { + var toret = this.root; + path.forEach(function(idx) { + toret = toret.contents()[idx]; + }); + return toret; } }); @@ -632,6 +676,113 @@ var defineDocumentProperties = function(doc, $document) { }, configurable: true}); }; + +// var registerTransformationsFromObject = function(object) { +// _.values(object).filter(function(val) { +// return typeof val === 'function' && val._isTransformation; +// }) +// .forEach(function(val) { +// registerTransformation(val._transformationName, val, object); +// }); +// }; +// registerTransformationsFromObject(ElementNode.prototype); +// registerTransformationsFromObject(TextNode.prototype); +// registerTransformationsFromObject(Document.prototype); + +// var Transformation = function() { +// }; +// $.extend(Transformation.prototype, { + +// }); + + +// var createDumbTransformation = function(impl, contextObject) { +// var DumbTransformation = function(args) { +// this.args = this.args; +// }; +// DumbTransformation.prototype = Object.create(Transformation.prototype); +// $.extend(DumbTransformation.prototype, { +// run: function() { +// impl.apply(contextObject, this.args); +// } +// }); + +// return DumbTransformation; + + +// }; + +var transformations = {}; +// var registerTransformation = function(name, impl, contextObject) { +// if(typeof impl === 'function') { +// transformations[name] = createDumbTransformation(impl, contextObject); +// } +// }; + +// registerTransformation('detachx', DocumentNode.prototype.detach, ) + + +// 1. detach via totalny fallback +var DetachNodeTransformation = function(args) { + this.node = args.node; + this.document = this.node.document; +}; +$.extend(DetachNodeTransformation.prototype, { + run: function() { + this.oldRoot = this.node.document.root.clone(); + this.path = this.node.getPath(); + this.node.detach(); // @TS + + }, + undo: function() { + this.document.root.replaceWith(this.oldRoot); // this.getDocument? + this.node = this.document.getNodeByPath(this.path); + } +}); +transformations['detach'] = DetachNodeTransformation; + +//2. detach via wskazanie changeroot + +var Detach2NodeTransformation = function(args) { + this.node = args.node; + this.document = this.node.document; +}; +$.extend(Detach2NodeTransformation.prototype, { + run: function() { + this.root = this.node.parent() ? this.node.parent() : this.node.document.root; + this.oldRoot = (this.root).clone(); + this.path = this.node.getPath(); + this.node.detach(); + + }, + undo: function() { + this.root.replaceWith(this.oldRoot); // this.getDocument? + this.node = this.document.getNodeByPath(this.path); + } +}); +transformations['detach2'] = Detach2NodeTransformation; + +//3. detach z pełnym własnym redo + +var Detach3NodeTransformation = function(args) { + this.node = args.node; + this.document = this.node.document; +}; +$.extend(Detach3NodeTransformation.prototype, { + run: function() { + this.index = this.node.getIndex(); + this.parent = this.node.parent(); + this.node.detach(); + }, + undo: function() { + var contents = this.parent.contents(); + if(contents.length === 0) { + this.parent.append(this.node) + } + } +}); +transformations['detach3'] = Detach3NodeTransformation; + return { documentFromXML: function(xml) { return new Document(xml); diff --git a/src/smartxml/smartxml.test.js b/src/smartxml/smartxml.test.js index 0b85383..64e633f 100644 --- a/src/smartxml/smartxml.test.js +++ b/src/smartxml/smartxml.test.js @@ -814,6 +814,31 @@ describe('smartxml', function() { }); }); + describe('Undo/redo', function() { + it('does work', function() { + var doc = getDocumentFromXML('
Alice
'), + span = doc.root.contents()[0]; + + doc.transform('detach3', {node: span}); + + + doc.undo(); + + expect(doc.root.contents()).to.have.length(1); + expect(doc.root.contents()[0].getTagName()).to.equal('span'); + expect(doc.root.contents()[0].contents()[0].getText()).to.equal('Alice'); + + doc.redo(); + expect(doc.root.contents()).to.have.length(0); + + doc.undo(); + expect(doc.root.contents()).to.have.length(1); + expect(doc.root.contents()[0].getTagName()).to.equal('span'); + expect(doc.root.contents()[0].contents()[0].getText()).to.equal('Alice'); + + }); + }); + }); }); \ No newline at end of file