X-Git-Url: https://git.mdrn.pl/fnpeditor.git/blobdiff_plain/b31ffcb46eae55b9c66457e2330b16e8db84f1d6..4b866f317d1e2e774e55127ba6bf598d61b363d8:/src/smartxml/smartxml.js?ds=sidebyside diff --git a/src/smartxml/smartxml.js b/src/smartxml/smartxml.js index d9e44be..102a506 100644 --- a/src/smartxml/smartxml.js +++ b/src/smartxml/smartxml.js @@ -33,7 +33,20 @@ $.extend(DocumentNode.prototype, { }, clone: function() { - var clone = this._$.clone(true, true); + var clone = this._$.clone(true, true), + node = this; + clone.find('*').addBack().each(function() { + var el = this, + clonedData = $(this).data(); + + _.pairs(clonedData).forEach(function(pair) { + var key = pair[0], + value = pair[1]; + if(_.isFunction(value.clone)) { + clonedData[key] = value.clone(node.document.createDocumentNode(el)); + } + }); + }); return this.document.createDocumentNode(clone[0]); }, @@ -53,7 +66,7 @@ $.extend(DocumentNode.prototype, { } }); - if(idx !== 'undefined') { + if(idx !== undefined) { nodePath = nodePath.slice(0, idx); } toret = nodePath.map(function(node) {return node.getIndex(); }); @@ -110,6 +123,9 @@ $.extend(DocumentNode.prototype, { 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)) { + if(type === 'nodeMoved') { + event.meta.parent = origParent; + } this.document.trigger('change', event); } if((type === 'nodeAdded' || type === 'nodeMoved') && !this.document.containsNode(this) && nodeWasContained) { @@ -190,6 +206,10 @@ $.extend(ElementNode.prototype, { return toret; }, + containsNode: function(node) { + return node && (node.nativeNode === this.nativeNode || node._$.parents().index(this._$) !== -1); + }, + toXML: function() { var wrapper = $('
'); wrapper.append(this._getXMLDOMToDump()); @@ -222,7 +242,12 @@ $.extend(TextNode.prototype, { var parseXML = function(xml) { - return $($.trim(xml))[0]; + var toret = $($.trim(xml)); + if(!toret.length) { + throw new Error('Unable to parse XML: ' + xml); + } + return toret[0]; + }; var registerTransformation = function(desc, name, target) { @@ -245,8 +270,7 @@ var registerMethod = function(methodName, method, target) { }; -var Document = function(xml) { - this.loadXML(xml); +var Document = function(xml, extensions) { this.undoStack = []; this.redoStack = []; this._transactionStack = []; @@ -260,6 +284,11 @@ var Document = function(xml) { this._elementNodeTransformations = {}; this.registerExtension(coreTransformations); + + (extensions || []).forEach(function(extension) { + this.registerExtension(extension); + }.bind(this)); + this.loadXML(xml); }; $.extend(Document.prototype, Backbone.Events, { @@ -276,6 +305,9 @@ $.extend(Document.prototype, Backbone.Events, { /* globals document */ from = document.createTextNode(from.text); } else { + if(!from.tagName) { + throw new Error('tagName missing'); + } var node = $('<' + from.tagName + '>'); _.keys(from.attrs || {}).forEach(function(key) { @@ -329,7 +361,7 @@ $.extend(Document.prototype, Backbone.Events, { }, containsNode: function(node) { - return this.root && (node.nativeNode === this.root.nativeNode || node._$.parents().index(this.root._$) !== -1); + return this.root && this.root.containsNode(node); }, getSiblingParents: function(params) { @@ -416,6 +448,27 @@ $.extend(Document.prototype, Backbone.Events, { }); }, + ifChanged: function(context, action, documentChangedHandler, documentUnchangedHandler) { + var hasChanged = false, + changeMonitor = function() { + hasChanged = true; + }; + + this.on('change', changeMonitor); + action.call(context); + this.off('change', changeMonitor); + + if(hasChanged) { + if(documentChangedHandler) { + documentChangedHandler.call(context); + } + } else { + if(documentUnchangedHandler) { + documentUnchangedHandler.call(context); + } + } + }, + transform: function(Transformation, args) { var toret, transformation; @@ -426,17 +479,26 @@ $.extend(Document.prototype, Backbone.Events, { } if(transformation) { this._transformationLevel++; - toret = transformation.run({beUndoable:this._transformationLevel === 1}); - if(this._transformationLevel === 1 && !this._undoInProgress) { - if(this._transactionInProgress) { - this._transactionStack.push(transformation); - } else { - this.undoStack.push(transformation); + + this.ifChanged( + this, + function() { + toret = transformation.run({beUndoable:this._transformationLevel === 1}); + }, + function() { + if(this._transformationLevel === 1 && !this._undoInProgress) { + if(this._transactionInProgress) { + this._transactionStack.push(transformation); + } else { + this.undoStack.push(transformation); + } + } + if(!this._undoInProgress && this._transformationLevel === 1) { + this.redoStack = []; + } } - } - if(!this._undoInProgress && this._transformationLevel === 1) { - this.redoStack = []; - } + ); + this._transformationLevel--; return toret; } else { @@ -509,8 +571,16 @@ $.extend(Document.prototype, Backbone.Events, { throw new Error('End of transaction requested, but there is no transaction in progress!'); } this._transactionInProgress = false; - this.undoStack.push(this._transactionStack); - this._transactionStack = []; + if(this._transactionStack.length) { + this.undoStack.push(this._transactionStack); + this._transactionStack = []; + } + }, + + transaction: function(callback, context) { + this.startTransaction(); + callback.call(context); + this.endTransaction(); }, getNodeByPath: function(path) {