9aea150ebdca7da820b195bd401f08da7845e792
[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, name) {
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
47         this.args.forEach(function(arg, idx, args) {
48             var path;
49             if(arg) {
50                 if(arg.nodeType) { // ~
51                     path = arg.getPath();
52                     Object.defineProperty(args, idx, {
53                         get: function() {
54                             if(transformation.hasRun && path) {
55                                 return transformation.document.getNodeByPath(path);
56                             } else {
57                                 return arg;
58                             }
59                         }
60                     });
61                 } else if(_.isObject(arg)) {
62                     _.keys(arg).forEach(function(key) {
63                         var value = arg[key],
64                             path;
65                         if(value && value.nodeType) {
66                             path = value.getPath();
67                             Object.defineProperty(arg, key, {
68                                 get: function() {
69                                     if(transformation.hasRun && path) {
70                                         return transformation.document.getNodeByPath(path);
71                                     } else {
72                                         return value;
73                                     }
74                                 }
75                             });   
76                         }
77                     });
78                 }
79             }
80         });
81
82         this.document = document;
83         this.hasRun = false;
84         if(desc.init) {
85             desc.init.call(this);
86         }
87     };
88     _.extend(GenericTransformation.prototype, {
89         name: name,
90         run: function(options) {
91             var changeRoot;
92             if(!desc.undo && options.beUndoable) {
93                 changeRoot = desc.getChangeRoot ? desc.getChangeRoot.call(this) : this.document.root;
94                 this.snapshot = changeRoot.clone();
95                 this.changeRootPath = changeRoot.getPath();
96             }
97             //var toret = desc.impl.call(this.context, this.args); // a argumenty do metody?
98             var argsToPass = desc.undo ? [this].concat(this.args) : this.args;
99             var toret = desc.impl.apply(this.context, argsToPass);
100             this.hasRun = true;
101             return toret;
102         },
103         undo: function() {
104             if(desc.undo) {
105                 desc.undo.call(this.context, this);
106             } else {
107                 this.document.getNodeByPath(this.changeRootPath).replaceWith(this.snapshot);
108             }
109         },
110     });
111
112     return GenericTransformation;
113 };
114 // var T = createGenericTransformation({impl: function() {}});
115 // var t = T(doc, {a:1,b:2,c3:3});
116
117
118 toret.createContextTransformation = function(desc, name) {
119     // mozna sie pozbyc przez przeniesienie object/context na koniec argumentow konstruktora generic transformation
120     var GenericTransformation = toret.createGenericTransformation(desc, name);
121
122     var ContextTransformation = function(document, object, args) {
123         GenericTransformation.call(this, document, args);
124
125         if(document === object) {
126             this.context = document;
127         } else {      
128             var contextPath = object.getPath(),
129                 transformation = this;
130             Object.defineProperty(this, 'context', {
131                 get: function() {
132                     // todo: to jakos inaczej, bo np. this.context w undo transformacji before to juz nie ten sam obiekt
133                     // moze transformacja powinna zwracac zmodyfikowana sciezke do obiektu po dzialaniu run?
134                     
135                     if(transformation.hasRun) {
136                         //console.log('returning via path');
137                         return transformation.document.getNodeByPath(contextPath);
138                     } else {
139                         //console.log('returning original arg');
140                         return object;
141
142                     }
143                 }
144             });
145         }
146     }
147     ContextTransformation.prototype = Object.create(GenericTransformation.prototype);
148     return ContextTransformation;
149 }
150 // var T = createContextTransformation({impl: function() {}});
151 // var t = T(doc, node, {a:1,b:2,c3:3});
152 ///
153
154
155
156 toret.TransformationStorage = function() {
157     this._transformations = {};
158 };
159
160 _.extend(toret.TransformationStorage.prototype, {
161     
162     register: function(Transformation) {
163         var list = (this._transformations[Transformation.prototype.name] = this._transformations[Transformation.prototype.name] || []);
164         list.push(Transformation);
165     },
166
167     get: function(name) {
168         var transformations = this._transformations[name];
169         if(!transformations) {
170             throw new Error('Transformation "' + name + '" not found!');
171         }
172         // na razie zwraca pierwsza
173         return transformations[0];
174     }
175 });
176
177
178
179 // var registerTransformationFromMethod = (object, methodName, desc) {
180 //         if(!object[methodName]) {
181 //             throw new Exeption('Cannot register transformation from unknown method ' + methodName + ' on ' + object);
182 //         }
183 //         desc.impl = object[name];
184 //         Transformation = createContextTransformation(desc);
185 //         object.prototype.registerContextTransformation(name, createContextTransformation(method));
186 // };
187
188
189 // registerTransformationFromMethod(ElementNode, 'setAttr', {
190 //     impl: function(args) {
191 //         this.setAttr(args.name, args.value);
192 //     },
193 //     getChangeRoot: function() {
194 //         return this.context;
195 //     }
196
197 // });
198
199 return toret;
200
201 });