X-Git-Url: https://git.mdrn.pl/fnpeditor.git/blobdiff_plain/f1f9dfddb9e3958dcbdc47aea789c5e1448fb68c..0c978bca67983e93096a8e744b53e50861a9333d:/src/smartxml/smartxml.js diff --git a/src/smartxml/smartxml.js b/src/smartxml/smartxml.js index 9f060e8..7b59b18 100644 --- a/src/smartxml/smartxml.js +++ b/src/smartxml/smartxml.js @@ -13,14 +13,26 @@ var TEXT_NODE = Node.TEXT_NODE; var INSERTION = function(implementation) { var toret = function(node) { - var insertion = this.getNodeInsertion(node); + var insertion = this.getNodeInsertion(node), + nodeParent; + if(!(this.document.containsNode(this))) { + nodeParent = insertion.ofNode.parent(); + } implementation.call(this, insertion.ofNode.nativeNode); - this.triggerChangeEvent(insertion.insertsNew ? 'nodeAdded' : 'nodeMoved', {node: insertion.ofNode}); + this.triggerChangeEvent(insertion.insertsNew ? 'nodeAdded' : 'nodeMoved', {node: insertion.ofNode}, nodeParent); return insertion.ofNode; }; 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'); @@ -37,7 +49,15 @@ $.extend(DocumentNode.prototype, { }, clone: function() { - return this.document.createDocumentNode(this._$.clone(true, true)[0]); + var clone = this._$.clone(true, true); + // clone.find('*').addBack().each(function() { + // var n = $(this); + // if(n.data('canvasElement')) { + // n.data('canvasElement', $.extend(true, {}, n.data('canvasElement'))); + // n.data('canvasElement').$element = n.data('canvasElement').$element.clone(true, true); + // } + // }); + return this.document.createDocumentNode(clone[0]); }, getPath: function(ancestor) { @@ -155,11 +175,16 @@ $.extend(DocumentNode.prototype, { } }, - triggerChangeEvent: function(type, metaData) { - var event = new events.ChangeEvent(type, $.extend({node: this}, metaData || {})); + triggerChangeEvent: function(type, metaData, origParent) { + var node = (metaData && metaData.node) ? metaData.node : this, + event = new events.ChangeEvent(type, $.extend({node: node}, metaData || {})); if(type === 'nodeDetached' || this.document.containsNode(event.meta.node)) { this.document.trigger('change', event); } + if((type === 'nodeAdded' || type === 'nodeMoved') && !(this.document.containsNode(this))) { + event = new events.ChangeEvent('nodeDetached', {node: node, parent: origParent}); + this.document.trigger('change', event); + } }, getNodeInsertion: function(node) { @@ -272,6 +297,15 @@ $.extend(ElementNode.prototype, { this._$.prepend(nativeNode); }), + insertAtIndex: function(nativeNode, index) { + var contents = this.contents(); + if(index < contents.length) { + return contents[index].before(nativeNode); + } else if(index === contents.length) { + return this.append(nativeNode); + } + }, + unwrapContent: function() { var parent = this.parent(); if(!parent) { @@ -349,6 +383,7 @@ $.extend(TextNode.prototype, { }, setText: function(text) { + console.log('smartxml: ' + text); this.nativeNode.data = text; this.triggerTextChangeEvent(); }, @@ -428,6 +463,8 @@ var parseXML = function(xml) { var Document = function(xml) { this.loadXML(xml); + this.undoStack = []; + this.redoStack = []; }; $.extend(Document.prototype, Backbone.Events, { @@ -602,6 +639,43 @@ $.extend(Document.prototype, Backbone.Events, { defineDocumentProperties(this, insertion.ofNode._$); insertion.ofNode.triggerChangeEvent('nodeAdded'); return insertion.ofNode; + }, + + transform: function(transformationName, args) { + console.log('transform'); + var Transformation = transformations[transformationName], + transformation; + if(Transformation) { + transformation = new Transformation(args); + transformation.run(); + this.undoStack.push(transformation); + console.log('clearing redo stack'); + this.redoStack = []; + } 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; } }); @@ -614,6 +688,185 @@ 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.nodePath = args.node.getPath(); + this.document = args.node.document; +}; +$.extend(Detach2NodeTransformation.prototype, { + run: function() { + var node = this.document.getNodeByPath(this.nodePath), + root = node.parent() ? node.parent() : this.document.root; + + this.rootPath = root.getPath(); + this.oldRoot = (root).clone(); + node.detach(); + }, + undo: function() { + this.document.getNodeByPath(this.rootPath).replaceWith(this.oldRoot); + } +}); +//transformations['detach2'] = Detach2NodeTransformation; + +//2a. generyczna transformacja + +var createTransformation = function(desc) { + + var NodeTransformation = function(args) { + this.nodePath = args.node.getPath(); + this.document = args.node.document; + this.args = args; + }; + $.extend(NodeTransformation.prototype, { + run: function() { + var node = this.document.getNodeByPath(this.nodePath), + root; + + if(desc.getRoot) { + root = desc.getRoot(node); + } else { + root = this.document.root; + } + + this.rootPath = root.getPath(); + this.oldRoot = (root).clone(); + desc.impl.call(node, this.args); + }, + undo: function() { + this.document.getNodeByPath(this.rootPath).replaceWith(this.oldRoot); + } + }); + + return NodeTransformation; +} + +transformations['detach2'] = createTransformation({ + // impl: function() { + // //this.setAttr('class', 'cite'); // + // }, + impl: ElementNode.prototype.detach, + getRoot: function(node) { + return node.parent(); + } + +}); + +transformations['setText'] = createTransformation({ + impl: function(args) { + this.setText(args.text) + }, + getRoot: function(node) { + return node; + } + +}); + +//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.path = this.node.getPath(); + if(this.node.isSurroundedByTextElements()) { + this.prevText = this.node.prev().getText(); + this.nextText = this.node.next().getText(); + this.merge = true; + } else { + this.prevText = this.nextText = null; + this.merge = false; + } + + this.node.detach(); + }, + undo: function() { + var parent = this.document.getNodeByPath(this.path.slice(0,-1)), + idx = _.last(this.path); + var inserted = parent.insertAtIndex(this.node, idx); + if(this.merge) { + if(inserted.next()) { + inserted.before({text: this.prevText}); + inserted.next().setText(this.nextText); + } else { + inserted.prev().setText(this.prevText); + inserted.after({text: this.nextText}); + } + } + } +}); +transformations['detach3'] = Detach3NodeTransformation; + return { documentFromXML: function(xml) { return new Document(xml);