X-Git-Url: https://git.mdrn.pl/fnpeditor.git/blobdiff_plain/0bd5926e9f6c5057d80e829ca5af83b19a58ecfc..33bebd4a55d538b7229e3d6cd0673e39ee28a81e:/src/smartxml/smartxml.js diff --git a/src/smartxml/smartxml.js b/src/smartxml/smartxml.js index fa5f551..d912904 100644 --- a/src/smartxml/smartxml.js +++ b/src/smartxml/smartxml.js @@ -2,8 +2,9 @@ define([ 'libs/jquery', 'libs/underscore', 'libs/backbone', - 'smartxml/events' -], function($, _, Backbone, events) { + 'smartxml/events', + 'smartxml/transformations' +], function($, _, Backbone, events, transformations) { 'use strict'; /* globals Node */ @@ -14,25 +15,18 @@ var TEXT_NODE = Node.TEXT_NODE; var INSERTION = function(implementation) { var toret = function(node) { var insertion = this.getNodeInsertion(node), + nodeWasContained = this.document.containsNode(insertion.ofNode), 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}, nodeParent); + this.triggerChangeEvent(insertion.insertsNew ? 'nodeAdded' : 'nodeMoved', {node: insertion.ofNode}, nodeParent, nodeWasContained); 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'); @@ -43,13 +37,31 @@ var DocumentNode = function(nativeNode, document) { }; $.extend(DocumentNode.prototype, { + + transform: function(name, args) { + var Transformation = this.transformations.get(name), + transformation; + if(Transformation) { + transformation = new Transformation(this.document, this, args); + } + return this.document.transform(transformation); + }, + _setNativeNode: function(nativeNode) { this.nativeNode = nativeNode; this._$ = $(nativeNode); }, 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) { @@ -167,13 +179,13 @@ $.extend(DocumentNode.prototype, { } }, - triggerChangeEvent: function(type, metaData, origParent) { + triggerChangeEvent: function(type, metaData, origParent, nodeWasContained) { 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))) { + if((type === 'nodeAdded' || type === 'nodeMoved') && !this.document.containsNode(this) && nodeWasContained) { event = new events.ChangeEvent('nodeDetached', {node: node, parent: origParent}); this.document.trigger('change', event); } @@ -229,12 +241,18 @@ $.extend(ElementNode.prototype, { return this.nativeNode.tagName.toLowerCase(); }, - contents: function() { + contents: function(selector) { var toret = [], document = this.document; - this._$.contents().each(function() { - toret.push(document.createDocumentNode(this)); - }); + if(selector) { + this._$.children(selector).each(function() { + toret.push(document.createDocumentNode(this)); + }); + } else { + this._$.contents().each(function() { + toret.push(document.createDocumentNode(this)); + }); + } return toret; }, @@ -375,6 +393,7 @@ $.extend(TextNode.prototype, { }, setText: function(text) { + //console.log('smartxml: ' + text); this.nativeNode.data = text; this.triggerTextChangeEvent(); }, @@ -452,10 +471,31 @@ var parseXML = function(xml) { return $($.trim(xml))[0]; }; +var registerTransformation = function(desc, name, target) { + var Transformation = transformations.createContextTransformation(desc, name); + target.register(Transformation); +}; + +var registerMethod = function(methodName, method, target) { + if(target[methodName]) { + throw new Error('Cannot extend {target} with method name {methodName}. Name already exists.' + .replace('{target}', target) + .replace('{methodName}', methodName) + ); + } + target[methodName] = method; +}; + + var Document = function(xml) { this.loadXML(xml); this.undoStack = []; this.redoStack = []; + this._transformationLevel = 0; + this.transformations = new transformations.TransformationStorage(); + + this._nodeMethods = {}; + this._nodeTransformations = new transformations.TransformationStorage(); }; $.extend(Document.prototype, Backbone.Events, { @@ -483,7 +523,10 @@ $.extend(Document.prototype, Backbone.Events, { } else if(from.nodeType === Node.ELEMENT_NODE) { Factory = this.ElementNodeFactory; } - return new Factory(from, this); + var toret = new Factory(from, this); + _.extend(toret, this._nodeMethods); + toret.transformations = this._nodeTransformations; + return toret; }, loadXML: function(xml, options) { @@ -632,15 +675,75 @@ $.extend(Document.prototype, Backbone.Events, { return insertion.ofNode; }, - transform: function(transformationName, args) { - var Transformation = transformations[transformationName], - transformation; - if(Transformation) { - transformation = new Transformation(args); - transformation.run(); - this.undoStack.push(transformation); + registerMethod: function(methodName, method) { + registerMethod(methodName, method, this); + }, + + registerNodeMethod: function(methodName, method) { + registerMethod(methodName, method, this._nodeMethods); + }, + + registerDocumentTransformation: function(desc, name) { + registerTransformation(desc, name, this.transformations); + }, + + registerNodeTransformation: function(desc, name) { + registerTransformation(desc, name, this._nodeTransformations); + }, + + registerExtension: function(extension) { + //debugger; + var doc = this, + existingPropertyNames = _.values(this); + + ['document', 'documentNode'].forEach(function(dstName) { + var dstExtension = extension[dstName]; + if(dstExtension) { + if(dstExtension.methods) { + _.pairs(dstExtension.methods).forEach(function(pair) { + var methodName = pair[0], + method = pair[1], + operation; + operation = {document: 'registerMethod', documentNode: 'registerNodeMethod'}[dstName]; + doc[operation](methodName, method); + + }); + } + + if(dstExtension.transformations) { + _.pairs(dstExtension.transformations).forEach(function(pair) { + var name = pair[0], + desc = pair[1], + operation; + operation = {document: 'registerDocumentTransformation', documentNode: 'registerNodeTransformation'}[dstName]; + doc[operation](desc, name); + }); + } + } + }); + }, + + transform: function(transformation, args) { + //console.log('transform'); + var Transformation, toret; + if(typeof transformation === 'string') { + Transformation = this.transformations.get(transformation); + if(Transformation) { + transformation = new Transformation(this, this, args); + } + } + if(transformation) { + this._transformationLevel++; + toret = transformation.run(); + if(this._transformationLevel === 1) { + this.undoStack.push(transformation); + } + this._transformationLevel--; + //console.log('clearing redo stack'); + this.redoStack = []; + return toret; } else { - throw new Error('Transformation ' + transformationName + ' doesn\'t exist!'); + throw new Error('Transformation ' + transformation + ' doesn\'t exist!'); } }, undo: function() { @@ -677,112 +780,6 @@ var defineDocumentProperties = function(doc, $document) { }; -// 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); @@ -794,7 +791,8 @@ return { Document: Document, DocumentNode: DocumentNode, - ElementNode: ElementNode + ElementNode: ElementNode, + TextNode: TextNode }; }); \ No newline at end of file