Pman.Builder.Tree.js
[Pman.Builder] / Pman.Builder.Tree.js
1 /**
2  * The code that manages the tree...
3  *
4  * used to be inside the Interface, but has proved to be to difficult to manage.
5  *
6  * In principle, simple event handling code is put in the interface, and any hard
7  * lifting is done in nice files...
8  *
9  * It might be better to just extend 'tree', and use the extended class..
10  * 
11  */
12
13 Pman.Builder.Tree = {
14     
15     currentNode: false,
16     dragProp: '',
17     
18     appendNode : function(parent, inConfig, point) {
19                                 
20                                     
21         var tree = Pman.Tab.BuilderTree.tree;
22         
23         if (typeof(inConfig) !== 'undefined') {
24             return;
25         }
26         var items = [];
27         if (typeof(inConfig) !== 'undefined' && inConfig.items) { // loading!
28             items = inConfig.items;
29             delete inConfig.items;
30         }
31         
32         var config = this.cloneConfig(inConfig);
33         
34         if (!parent) {
35             parent = tree.root;
36         }
37               
38         var newNode = new Roo.tree.TreeNode({
39                 text: this.configToText(config)
40         });
41                 
42         
43         newNode.elConfig = config;
44         //if (markUndo === true) {
45         //Pman.Tab.Builder.markUndo("Add " + newNode.text);
46         //
47             // appends to our tree...
48         console.log("APPEND NODE: " + point);    
49         switch(point) {
50             case 'above':
51                 parent.parentNode.insertBefore(newNode, parent);
52                 parent.parentNode.expand(true);
53                 break;
54             case 'below':
55                 // if it's the last node.. then we append..
56                 var ix = parent.parentNode.indexOf(parent) + 1;
57                 if (parent.parentNode.childNodes.length == ix) {
58                      parent.parentNode.appendChild(newNode);
59                      break;
60                 }
61                 var bef = parent.parentNode.childNodes[ix];
62                 parent.parentNode.insertBefore(newNode, bef);
63                 parent.parentNode.expand(true);
64                 break;
65             
66             case 'append':
67             default:    
68                 parent.appendChild(newNode);
69                 parent.expand(true);
70                 break;
71         }
72             
73         if (items.length) {
74             Roo.each(items, function(i) {
75                 this.appendNode(newNode, i);
76             },this);
77             
78         }
79             
80             
81             /*
82             -- panels with panes...
83                 if (items && items.length) {
84                         for (var i = 0; i < items.length; i++) {
85                                         this.appendConfig(items[i], newNode, false);
86                         }
87                 }
88                 if (opts.doUpdate !== false) {
89                         this.updateForm(false, newNode);
90                 }
91             */
92         return newNode;
93         
94          
95     },
96     clearAll : function() {
97         var tree = Pman.Tab.BuilderTree.tree;
98         
99         var rt = tree.root;
100          if (rt.childNodes.length) {
101             rt.removeChild(rt.childNodes[0]);
102         }
103         
104         tree.root.elConfig  = Roo.apply({ }, this.defaultElConfig());  
105         //var btop = Pman.Tab.BuilderTop;
106         //if (btop.modsel && btop.modsel.lastData) {
107         //    this.tree.root.elConfig.app = btop.modsel.lastData.app;
108         //}
109         
110         this.setCurrentNode(tree.root,true);
111         
112     },
113     cloneConfig : function(config) {
114         if (!config) { return null; }
115         var newConfig = {};
116         
117         for (var i in config) {
118             if (typeof config[i] == 'object') {
119                  newConfig[i] = this.cloneConfig(config[i]);
120             } else if (typeof config[i] != 'function') { // should this happen?
121                  newConfig[i] = config[i];
122             }
123         }
124         return newConfig;
125     },
126     configToText : function(c) {
127          
128         var txt = [];
129         c = c || {};
130               var sr = (typeof(c['+buildershow']) != 'undefined') &&  !c['+buildershow'] ? true : false;
131             if (sr) txt.push('<s>');
132             if (typeof(c['*prop']) != 'undefined')   { txt.push(c['*prop']+ ':'); }
133         if (c.xtype)      { txt.push(c.xtype); }
134         if (c.fieldLabel) { txt.push('[' + c.fieldLabel + ']'); }
135         if (c.boxLabel)   { txt.push('[' + c.boxLabel + ']'); }
136         
137         
138         if (c.layout)     { txt.push('<i>' + c.layout + '</i>'); }
139         if (c.title)      { txt.push('<b>' + c.title + '</b>'); }
140             if (c.header)    { txt.push('<b>' + c.header + '</b>'); }
141             if (c.legend)      { txt.push('<b>' + c.legend + '</b>'); }
142         if (c.text)       { txt.push('<b>' + c.text + '</b>'); }
143             if (c.name)       { txt.push('<b>' + c.name+ '</b>'); }
144         if (c.region)     { txt.push('<i>(' + c.region + ')</i>'); }
145             if (c.dataIndex) { txt.push('[' + c.dataIndex+ ']'); }
146             if (sr) txt.push('</s>');
147         return (txt.length == 0 ? "Element" : txt.join(" "));
148         
149         
150     },
151     currentNodeType : function() {
152         return this.nodeXtype(this.currentNode);
153         
154     },
155     defaultElConfig : function() {
156         return {
157            xtype : '*top',
158             
159             module : 'TestApp',
160             part:   'Partname',
161             modkey : 0,
162             region : 'center',
163             parent : 'Pman',
164             name : 'Module Name',
165             items: [] 
166         };
167     },
168     
169     replaceCurrentNode : function(cfg)
170     {
171         var tree = Pman.Tab.BuilderTree.tree;
172
173         if (this.currentNode == tree.root) {
174             return false;
175         }
176         var pn = this.currentNode.parentNode;
177         
178         var ix = pn.indexOf(this.currentNode);
179         pn.removeChild(this.currentNode);
180           
181         if (!pn.childNodes.length) {
182             this.appendNode(pn, cfg, 'append')
183             return true;
184         }
185         if (!ix) {
186             // first..
187             this.appendNode(pn.childNodes[0], cfg, 'above');
188             return true;
189         
190         }
191         
192         this.appendNode(pn.childNodes[ix-1], cfg, 'below');
193         
194         
195         return true;
196     },
197     
198     deleteCurrent : function()
199     {
200         
201         var tree = Pman.Tab.BuilderTree.tree;
202         
203         if (this.currentNode == tree.root) {
204             return false;
205         }
206         var cfg = this.currentNode.elConfig;
207         // things that can not be deleted...
208         
209         
210         var pn = this.currentNode.parentNode;
211         
212         // work out if we make the next or parent the 'current node'
213         var ix = pn.indexOf(this.currentNode);
214         //  console.log(ix);
215         pn.removeChild(this.currentNode);
216         if (pn.childNodes.length) {
217             ix = Math.min(pn.childNodes.length-1, ix);
218         }
219         this.setCurrentNode(pn.childNodes.length ? pn.childNodes[ix] : pn  ,true);
220         return true;
221     },
222     dupeNode : function(node)
223     {
224             var cfg = this.cloneConfig(node.elConfig);
225             
226             var newNode = new Roo.tree.TreeNode(
227             {
228                     id: Roo.id(),
229                     text: this.configToText(cfg)
230             });
231             
232             newNode.elConfig = cfg;
233             node.eachChild(function(n) {
234                 newNode.appendChild(this.dupeNode(n));
235             },this);
236             
237             return newNode;
238                 
239     },
240     loadBJS : function(module, part)
241     {
242         var _t = this;
243         new Pman.Request({
244             url : baseURL + '/Roo/Builder_part.php',
245             method : 'GET',
246             params : {
247                _id : part
248             },
249             success : function(res)         
250             {
251                 // data is in.. 
252                 Roo.log(res);
253                 
254                 if (!res.data.json.length) {
255                     var cfg = _t.defaultElConfig();
256                     cfg.name = Pman.Tab.BuilderTop.filesel.lastData.name;
257                     cfg.part = Pman.Tab.BuilderTop.filesel.lastData.name;
258                     cfg.module = '';
259                     _t.loadTree(cfg);
260                     return;
261                 
262                 }
263                 
264                 _t.loadTree(JSON.parse(res.data.json));
265                 
266              
267             }
268         
269          })  
270             
271         
272         
273     },
274     loadTree : function(o)
275     {
276         var tree = Pman.Tab.BuilderTree.tree;
277         this.clearAll();
278         tree.root.elConfig = o;
279         if (typeof(o.xtype) == 'undefined') {
280             o.xtype = '*top';
281         }
282         tree.root.setText(this.configToText(tree.root.elConfig));
283         this.appendNode(tree.root, o.items[0]);
284         tree.root.expand(true);
285         Pman.Tab.BuilderView.panel.redraw();
286         this.setCurrentNode(tree.root,true);
287     },
288     nodeXtype : function(n)
289     {
290         var tree = Pman.Tab.BuilderTree.tree;
291         if (!n || !n.elConfig) {return ''; }
292         var xt = n.elConfig.xtype ||  '';
293         var xns= n.elConfig['|xns'] ||   '';
294         xns += xns.length ? '.' : '';
295         return xns + xt;
296     },
297     setCurrentNode : function(node,select)
298     {
299         var tree = Pman.Tab.BuilderTree.tree;
300         
301         this.currentNode = node || tree.root;
302             
303         //Pman.Tab.BuilderView.highlightElement(this.currentNode);
304     
305         var p = Pman.Tab.BuilderProps.grid;
306         if (p) { //may not be ready yet..
307             p.setCurrrentNode(this.currentNode);
308         }
309         
310       
311         this.currentNode.setText(this.configToText(this.currentNode.elConfig));
312         
313         if (select) { //&& node !== this.tree.root) {
314             if (this.currentNode !== tree.root)  {
315                      this.currentNode.ensureVisible();
316               }   
317              this.currentNode.expand(false,false);
318             this.currentNode.select();
319         }
320         // update palete..
321         Pman.Tab.BuilderPalette.grid.getSelectionModel().clearSelections();
322         Pman.Tab.BuilderPalette.grid.view.refresh();
323         
324     },
325     toJS : function(n)
326     {
327         if (!n) {
328             var tree = Pman.Tab.BuilderTree.tree;
329             return this.toJS(tree.root);
330         }
331         var _this = this;
332         var ret = this.cloneConfig(n.elConfig);
333         if (n.childNodes.length) {
334             ret.items = [];
335             n.eachChild(function(cn) {
336                 ret.items.push(_this.toJS(cn));
337             });
338                 
339         }
340         return ret;
341           
342          
343     },
344     
345     /**
346      * handle dropNode
347      */
348     
349     handleDropNode : function (e)
350     {
351         // nodedragover handles the allow/disallow..
352         
353         /*
354         tree - The TreePanel
355         target - The node being targeted for the drop
356         data - The drag data from the drag source
357         point - The point of the drop - append, above or below
358         source - The drag source
359         rawEvent - Raw mouse event
360         dropNode - Drop node(s) provided by the source OR you can supply node(s) to be inserted by setting them on this object.
361         cancel - Set this to true to cancel the drop.
362         */
363         
364         var _t = this;
365         var np = e.point == 'append' ? e.target : e.target.parentNode ; // new parent
366           
367         if (!e.tree || !e.dropNode) {
368         
369             // form palete...
370             var data  = e.source.dragData.selections[0].data;
371     
372             var xar = data.name.split('.');
373     
374             var cfg = {
375                 'xtype' : xar.pop(),
376                 '|xns' : xar.join('.')
377                 
378             };
379             if (this.dragProp.length > 1) {
380                 cfg['*prop'] = this.dragProp;
381             }
382             // at this point it should of a set of options...
383              var cls = cfg['|xns'] + '.' + cfg['xtype'];
384                 
385     
386             if (typeof(Pman.Builder.Wizard[cls]) != 'undefined') {
387                 Pman.Dialog.BuilderAdd.show( cfg , function(fdata ) {
388      
389                     _t.appendNode(e.target, fdata , e.point);
390                      Pman.Tab.BuilderView.panel.redraw();
391              
392                  });
393                  return false;
394              }
395              this.appendNode(e.target, cfg, e.point);
396               Pman.Tab.BuilderView.panel.redraw();
397              
398             return false; // fixme drop of elements from palete..
399         }
400     
401         // always drop onto own parent
402         if (np == e.dropNode.parentNode) {
403             if (e.rawEvent.ctrlKey) {
404                 e.dropNode = this.dupeNode(e.dropNode);
405             }
406             return true;
407         }
408         // can append has to use palete...
409         // this code should be in nodedragover.
410         if (e.dropNode) {
411             var cfg = this.toJS(e.dropNode);
412             this.appendNode(e.target, cfg, e.point);
413         }
414         Roo.log('no e.dropNode?');
415         return false; // do not allow movement..
416         
417         if (_this.canAppend(np, e.dropNode.elConfig)) {
418             if (e.rawEvent.ctrlKey) {
419                 e.dropNode = _this.dupeNode(e.dropNode);
420                   
421                 if (np.elConfig.xtype == 'GridEditor') {
422                     e.dropNode.elConfig['*prop'] = 'field';
423                 }
424                 
425             }
426             return true;
427         }  
428         Roo.log('can not drop ' + e.dropNode.elConfig.xtype + ' ontop of ' + np.elConfig.xtype);
429         
430         
431         
432         return false;
433                             
434     
435     },
436     handleDragOver : function(e)
437     {
438         Roo.log('nodedragover');
439         Roo.log(e);
440         // e.cancel..
441         // if we have within the same tree:
442           // dropNode (the node being dragged !!important!!) 
443           // point: below, append
444           // target - node 
445        // for palete
446            // dropNode = false;
447            // grid = the grid...
448            // source.dragData.selections[..] 
449       
450        
451        // we can only check parents... (we in theory can check dupe properties.. but let's ignore that for the time being.)
452        
453        // ok off we go.
454        
455        if (!e.dropNode) {
456            // drag from palete..
457            if (!e.source.dragData.selections.length) {
458                e.cancel = true;
459                return;
460            }
461            var drop_rec = e.source.dragData.selections[0];
462            var drop_xtype = drop_rec.data.name;
463            var ok_parents = drop_rec.json.parents;
464            
465            Roo.log("TEST PARENTS: " + ok_parents.join(', '));
466            var new_parent = this.nodeXtype((e.point == 'append') ? e.target :  e.target.parentNode);
467            Roo.log("NEW PARENT: " + e.point + " = " + new_parent);
468            
469            // see if the new_parent is actually in the list of ok_parents
470            e.cancel = true;
471            this.dragProp = '';
472            var _t = this;
473            Roo.each(ok_parents, function(n) {
474                 Roo.log("compare "+n+" ? " + new_parent);
475                 if (n == new_parent || n.split(':').shift() == new_parent) {
476                    Roo.log("got match!");
477                    e.cancel = false;
478                    _t.dragProp = (n == new_parent) ?  '' : n.split(':').pop();
479                    return true;
480                 }
481                return null;
482             });
483    
484            // done all the checks...
485            return;
486            
487         }
488         
489         
490         // have a drop node - hence comming from the same object..
491         var drop_xtype = this.nodeXtype(e.dropNode);
492         // currently we can not determine validity..
493         e.cancel = false;
494         return ;
495         
496         
497         
498     },
499     save : function() 
500     {
501        // first see if first element has a name.. - we can not save otherwise..
502         var t = Pman.Tab.BuilderTree.tree;
503         if (!t.root.elConfig.name.length) {
504             Roo.MessageBox.alert("Error", "No name set for form");
505             return;
506         }
507      
508         var  sid = (typeof(sid) == 'undefined') ? 
509              (Pman.Tab.BuilderTop.filesel.lastData ? Pman.Tab.BuilderTop.filesel.lastData.id : 0) : sid;
510         
511
512         var js = this.toJS();
513         var render = new Pman.Builder.JsRender(js); 
514          
515         // console.log(js);
516         // console.log(json);
517         
518         // check the select box to see if that has been set... - save it with that id..
519         
520         //var _this = this;
521         
522         Pman.request({
523             url: baseURL + '/Roo/Builder_part.php',
524             method : 'POST',
525             params : {
526                 json : Roo.encode(js, null, 4),
527                 jsource : render.toSource(),
528                 name :   js.name,
529                 module_id : Pman.Tab.BuilderTop.modsel.getValue(),
530                 id : sid
531             }, 
532             success : function(data) {
533                 // set the fileSel!!
534                 console.log(data);
535                 //if (data) {
536                 //    _this.filesel.setFromData(data);
537 //                    if (cb) {
538 //                        cb.call(_this,data);
539   //                  }
540 //                    _this.postCode(data);
541 //                }
542             }
543         });
544 }
545
546     
547     
548 }