smartxml: transformations fix - wrapping nodes arguments with path accessor one level...
[fnpeditor.git] / src / smartxml / transformations.js
1 define(function(require) {
2     
3 'use strict';
4
5 var _ = require('libs/underscore'),
6     toret = {};
7
8 var getTransDesc = function(desc) {
9     if(typeof desc === 'function') {
10         desc = {impl: desc};
11     }
12     if(!desc.impl) {
13         throw new Error('Got transformation description without implementation.');
14     }
15     return desc;
16 };
17
18 toret.createGenericTransformation = function(desc, name) {
19     desc = getTransDesc(desc);
20     
21     var GenericTransformation = function(document, args) {
22         this.args = args || [];
23
24         var transformation = this;
25         // _.keys(this.args).forEach(function(key) {
26         //     if(transformation.args[key].nodeType) { //@@ change to instanceof check, fix circular dependency
27         //         var value = transformation.args[key],
28         //             path = value.getPath();
29         //         Object.defineProperty(transformation.args, key, {
30         //             get: function() {
31         //                 if(transformation.hasRun) {
32         //                     //console.log('returning via path');
33         //                     return transformation.document.getNodeByPath(path);
34         //                 } else {
35         //                     //console.log('returning original arg');
36         //                     return value;
37
38         //                 }
39         //             }
40         //         });
41         //     }
42         // });
43
44         // potem spr na dotychczasowych undo/redo tests;
45         
46         var patchObject = function(obj, depth) {
47             depth = _.isNumber(depth) ? depth : 1;
48             if(depth > 3) {
49                 return;
50             }
51             _.keys(obj).forEach(function(key) {
52                 var value = obj[key],
53                     path;
54                 if(value) {
55                     if(value.nodeType) {
56                         path = value.getPath();
57                         Object.defineProperty(obj, key, {
58                             get: function() {
59                                 if(transformation.hasRun && path) {
60                                     return transformation.document.getNodeByPath(path);
61                                 } else {
62                                     return value;
63                                 }
64                             }
65                         });
66                     } else if(_.isObject(value)) {
67                         patchObject(value, depth+1);
68                     }
69                 }
70             });
71         };
72
73         this.args.forEach(function(arg, idx, args) {
74             var path;
75             if(arg) {
76                 if(arg.nodeType) { // ~
77                     path = arg.getPath();
78                     Object.defineProperty(args, idx, {
79                         get: function() {
80                             if(transformation.hasRun && path) {
81                                 return transformation.document.getNodeByPath(path);
82                             } else {
83                                 return arg;
84                             }
85                         }
86                     });
87                 } else if(_.isObject(arg)) {
88                     patchObject(arg);
89                 }
90             }
91         });
92
93         this.document = document;
94         this.hasRun = false;
95         if(desc.init) {
96             desc.init.call(this);
97         }
98     };
99     _.extend(GenericTransformation.prototype, {
100         name: name,
101         run: function(options) {
102             var changeRoot;
103             if(!desc.undo && options.beUndoable) {
104                 changeRoot = this.getChangeRoot();
105                 if(!changeRoot) {
106                      throw new Error(
107                          'Transformation {name} returned invalid change root value'
108                          .replace('{name}', name)
109                      );
110                 }
111                 this.changeRootPath = changeRoot.getPath();
112                 this.snapshot = changeRoot.clone();
113             }
114             var argsToPass = desc.undo ? [this].concat(this.args) : this.args;
115             var toret = desc.impl.apply(this.context, argsToPass);
116             this.hasRun = true;
117             return toret;
118         },
119         undo: function() {
120             if(desc.undo) {
121                 desc.undo.call(this.context, this);
122             } else {
123                 this.document.getNodeByPath(this.changeRootPath).replaceWith(this.snapshot);
124             }
125         },
126         getChangeRoot: desc.getChangeRoot || function() {
127             return this.document.root;
128         }
129     });
130
131     return GenericTransformation;
132 };
133
134 toret.createContextTransformation = function(desc, name) {
135     var GenericTransformation = toret.createGenericTransformation(desc, name);
136
137     var ContextTransformation = function(document, object, args) {
138         GenericTransformation.call(this, document, args);
139
140         if(document === object) {
141             this.context = document;
142         } else {
143             var contextPath = object.getPath(),
144                 transformation = this;
145             Object.defineProperty(this, 'context', {
146                 get: function() {
147                     if(transformation.hasRun) {
148                         return transformation.document.getNodeByPath(contextPath);
149                     } else {
150                         return object;
151                     }
152                 }
153             });
154         }
155     };
156     ContextTransformation.prototype = Object.create(GenericTransformation.prototype);
157     return ContextTransformation;
158 };
159
160 return toret;
161
162 });