X-Git-Url: https://git.mdrn.pl/fnpeditor.git/blobdiff_plain/3c6c8fe1de117ec69a146ea18aedac5d8dc30691..55826c1f7f8bfec8c90096b1c1c779e118e357fc:/src/smartxml/transformations.js diff --git a/src/smartxml/transformations.js b/src/smartxml/transformations.js index 772cf75..4a8e5a8 100644 --- a/src/smartxml/transformations.js +++ b/src/smartxml/transformations.js @@ -5,78 +5,113 @@ define(function(require) { var _ = require('libs/underscore'), toret = {}; -var getTransDesc = function(desc, name) { + +var getTransDesc = function(desc) { if(typeof desc === 'function') { desc = {impl: desc}; } if(!desc.impl) { - throw new Error('Got transformation description without implementation.') + throw new Error('Got transformation description without implementation.'); } - desc.name = desc.name || name; return desc; }; toret.createGenericTransformation = function(desc, name) { - desc = getTransDesc(desc, name); + desc = getTransDesc(desc); var GenericTransformation = function(document, args) { - this.args = args || {}; - + this.args = args || []; var transformation = this; - _.keys(this.args).forEach(function(key) { - if(transformation.args[key].nodeType) { //@@ change to instanceof check, fix circular dependency - var value = transformation.args[key], - path = value.getPath(); - Object.defineProperty(transformation.args, key, { - get: function() { - if(transformation.hasRun) { - //console.log('returning via path'); - return transformation.document.getNodeByPath(path); - } else { - //console.log('returning original arg'); - return value; - - } + + var patchObject = function(obj, depth) { + depth = _.isNumber(depth) ? depth : 1; + if(depth > 3) { + return; + } + _.keys(obj).forEach(function(key) { + var value = obj[key]; + if(value) { + if(value.nodeType) { + transformation.wrapNodeProperty(obj, key); + } else if(_.isObject(value)) { + patchObject(value, depth+1); } - }); + } + }); + }; + + this.args.forEach(function(arg, idx, args) { + if(arg) { + if(arg.nodeType) { // ~ + transformation.wrapNodeProperty(args, idx); + } else if(_.isObject(arg)) { + patchObject(arg); + } } }); + this.document = document; - this.hasRun = false; + this.runCount = 0; if(desc.init) { desc.init.call(this); } }; _.extend(GenericTransformation.prototype, { - name: desc.name, - run: function() { + name: name, + run: function(options) { var changeRoot; - if(!desc.undo) { - changeRoot = desc.getChangeRoot ? desc.getChangeRoot.call(this) : this.document.root; - this.snapshot = changeRoot.clone(); + if(!desc.undo && options.beUndoable) { + changeRoot = this.getChangeRoot(); + if(!changeRoot) { + throw new Error( + 'Transformation {name} returned invalid change root value' + .replace('{name}', name) + ); + } this.changeRootPath = changeRoot.getPath(); + this.snapshot = changeRoot.clone(); } - var toret = desc.impl.call(this.context, this.args); // a argumenty do metody? - this.hasRun = true; + var argsToPass = desc.undo ? [this].concat(this.args) : this.args; + var toret = desc.impl.apply(this.context, argsToPass); + this.runCount++; return toret; }, undo: function() { if(desc.undo) { - desc.undo.call(this.context); + desc.undo.call(this.context, this); + this.runCount++; } else { this.document.getNodeByPath(this.changeRootPath).replaceWith(this.snapshot); } }, + getChangeRoot: desc.getChangeRoot || function() { + return this.document.root; + }, + wrapNodeProperty: function(object, propName, value) { + var transformation = this, + lastRunNumber = 0, + path; + + value = value || object[propName]; + if(value && value.nodeType) { + path = value.getPath(); + Object.defineProperty(object, propName, { + get: function() { + if((lastRunNumber !== transformation.runCount) && path) { + value = transformation.document.getNodeByPath(path); + lastRunNumber = transformation.runCount; + } + return value; + } + }); + } + } }); return GenericTransformation; }; -// var T = createGenericTransformation({impl: function() {}}); -// var t = T(doc, {a:1,b:2,c3:3}); - toret.createContextTransformation = function(desc, name) { - // mozna sie pozbyc przez przeniesienie object/context na koniec argumentow konstruktora generic transformation var GenericTransformation = toret.createGenericTransformation(desc, name); var ContextTransformation = function(document, object, args) { @@ -84,71 +119,14 @@ toret.createContextTransformation = function(desc, name) { if(document === object) { this.context = document; - } else { - var contextPath = object.getPath(); - Object.defineProperty(this, 'context', { - get: function() { - // todo: to jakos inaczej, bo np. this.context w undo transformacji before to juz nie ten sam obiekt - // moze transformacja powinna zwracac zmodyfikowana sciezke do obiektu po dzialaniu run? - - // tu tez trick z hasRun - return document.getNodeByPath(contextPath); - } - }); + } else { + this.wrapNodeProperty(this, 'context', object); } - } + }; ContextTransformation.prototype = Object.create(GenericTransformation.prototype); return ContextTransformation; -} -// var T = createContextTransformation({impl: function() {}}); -// var t = T(doc, node, {a:1,b:2,c3:3}); -/// - - - -toret.TransformationStorage = function() { - this._transformations = {}; }; -_.extend(toret.TransformationStorage.prototype, { - - register: function(Transformation) { - var list = (this._transformations[Transformation.prototype.name] = this._transformations[Transformation.prototype.name] || []); - list.push(Transformation); - }, - - get: function(name) { - var transformations = this._transformations[name]; - if(!transformations) { - throw new Error('Transformation "' + name + '" not found!'); - } - // na razie zwraca pierwsza - return transformations[0]; - } -}); - - - -// var registerTransformationFromMethod = (object, methodName, desc) { -// if(!object[methodName]) { -// throw new Exeption('Cannot register transformation from unknown method ' + methodName + ' on ' + object); -// } -// desc.impl = object[name]; -// Transformation = createContextTransformation(desc); -// object.prototype.registerContextTransformation(name, createContextTransformation(method)); -// }; - - -// registerTransformationFromMethod(ElementNode, 'setAttr', { -// impl: function(args) { -// this.setAttr(args.name, args.value); -// }, -// getChangeRoot: function() { -// return this.context; -// } - -// }); - return toret; }); \ No newline at end of file