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