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