Roo/XComponent.js
[roojs1] / Roo / XComponent.js
1 /*
2  * Original code for Roojs - LGPL
3  * <script type="text/javascript">
4  */
5  
6 /**
7  * @class Roo.XComponent
8  * A delayed Element creator...
9  * Or a way to group chunks of interface together.
10  * 
11  * Mypart.xyx = new Roo.XComponent({
12
13     parent : 'Mypart.xyz', // empty == document.element.!!
14     order : '001',
15     name : 'xxxx'
16     region : 'xxxx'
17     disabled : function() {} 
18      
19     tree : function() { // return an tree of xtype declared components
20         var MODULE = this;
21         return 
22         {
23             xtype : 'NestedLayoutPanel',
24             // technicall
25         }
26      ]
27  *})
28  *
29  *
30  * It can be used to build a big heiracy, with parent etc.
31  * or you can just use this to render a single compoent to a dom element
32  * MYPART.render(Roo.Element | String(id) | dom_element )
33  * 
34  * @extends Roo.util.Observable
35  * @constructor
36  * @param cfg {Object} configuration of component
37  * 
38  */
39 Roo.XComponent = function(cfg) {
40     Roo.apply(this, cfg);
41     this.addEvents({ 
42         /**
43              * @event built
44              * Fires when this the componnt is built
45              * @param {Roo.XComponent} c the component
46              */
47         'built' : true
48         
49     });
50     this.region = this.region || 'center'; // default..
51     Roo.XComponent.register(this);
52     this.modules = false;
53     this.el = false; // where the layout goes..
54     
55     
56 }
57 Roo.extend(Roo.XComponent, Roo.util.Observable, {
58     /**
59      * @property el
60      * The created element (with Roo.factory())
61      * @type {Roo.Layout}
62      */
63     el  : false,
64     
65     /**
66      * @property el
67      * for BC  - use el in new code
68      * @type {Roo.Layout}
69      */
70     panel : false,
71     
72     /**
73      * @property layout
74      * for BC  - use el in new code
75      * @type {Roo.Layout}
76      */
77     layout : false,
78     
79      /**
80      * @cfg {Function|boolean} disabled
81      * If this module is disabled by some rule, return true from the funtion
82      */
83     disabled : false,
84     
85     /**
86      * @cfg {String} parent 
87      * Name of parent element which it get xtype added to..
88      */
89     parent: false,
90     
91     /**
92      * @cfg {String} order
93      * Used to set the order in which elements are created (usefull for multiple tabs)
94      */
95     
96     order : false,
97     /**
98      * @cfg {String} name
99      * String to display while loading.
100      */
101     name : false,
102     /**
103      * @cfg {String} region
104      * Region to render component to (defaults to center)
105      */
106     region : 'center',
107     
108     /**
109      * @cfg {Array} items
110      * A single item array - the first element is the root of the tree..
111      * It's done this way to stay compatible with the Xtype system...
112      */
113     items : false,
114     
115     /**
116      * @property _tree
117      * The method that retuns the tree of parts that make up this compoennt 
118      * @type {function}
119      */
120     _tree  : false,
121     
122      /**
123      * render
124      * render element to dom or tree
125      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
126      */
127     
128     render : function(el)
129     {
130         
131         el = el || false;
132         var hp = this.parent ? 1 : 0;
133         
134         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
135             // if parent is a '#.....' string, then let's use that..
136             var ename = this.parent.substr(1)
137             this.parent = false;
138             el = Roo.get(ename);
139             if (!el) {
140                 Roo.log("Warning - element can not be found :#" + ename );
141                 return;
142             }
143         }
144         
145         
146         if (!this.parent) {
147             
148             el = el ? Roo.get(el) : false;      
149             
150             // it's a top level one..
151             this.parent =  {
152                 el : new Roo.BorderLayout(el || document.body, {
153                 
154                      center: {
155                          titlebar: false,
156                          autoScroll:false,
157                          closeOnTab: true,
158                          tabPosition: 'top',
159                           //resizeTabs: true,
160                          alwaysShowTabs: el && hp? false :  true,
161                          hideTabs: el || !hp ? true :  false,
162                          minTabWidth: 140
163                      }
164                  })
165             }
166         }
167         
168                 
169                 // The 'tree' method is  '_tree now' 
170             
171         var tree = this._tree ? this._tree() : this.tree();
172         tree.region = tree.region || this.region;
173         this.el = this.parent.el.addxtype(tree);
174         this.fireEvent('built', this);
175         
176         this.panel = this.el;
177         this.layout = this.panel.layout;
178                 this.parentLayout = this.parent.layout  || false;  
179          
180     }
181     
182 });
183
184 Roo.apply(Roo.XComponent, {
185     /**
186      * @property  hideProgress
187      * true to disable the building progress bar.. usefull on single page renders.
188      * @type Boolean
189      */
190     hideProgress : false,
191     /**
192      * @property  buildCompleted
193      * True when the builder has completed building the interface.
194      * @type Boolean
195      */
196     buildCompleted : false,
197      
198     /**
199      * @property  topModule
200      * the upper most module - uses document.element as it's constructor.
201      * @type Object
202      */
203      
204     topModule  : false,
205       
206     /**
207      * @property  modules
208      * array of modules to be created by registration system.
209      * @type {Array} of Roo.XComponent
210      */
211     
212     modules : [],
213     /**
214      * @property  elmodules
215      * array of modules to be created by which use #ID 
216      * @type {Array} of Roo.XComponent
217      */
218      
219     elmodules : [],
220
221     
222     /**
223      * Register components to be built later.
224      *
225      * This solves the following issues
226      * - Building is not done on page load, but after an authentication process has occured.
227      * - Interface elements are registered on page load
228      * - Parent Interface elements may not be loaded before child, so this handles that..
229      * 
230      *
231      * example:
232      * 
233      * MyApp.register({
234           order : '000001',
235           module : 'Pman.Tab.projectMgr',
236           region : 'center',
237           parent : 'Pman.layout',
238           disabled : false,  // or use a function..
239         })
240      
241      * * @param {Object} details about module
242      */
243     register : function(obj) {
244                 
245                 Roo.XComponent.event.fireEvent('register', obj);
246                 switch(typeof(obj.disabled) ) {
247                         
248                         case 'undefined':
249                                 break;
250                         
251                         case 'function':
252                                 if ( obj.disabled() ) {
253                                         return;
254                                 }
255                                 break;
256                         default:
257                                 if (obj.disabled) {
258                                         return;
259                                 }
260                                 break;
261                 }
262                 
263         this.modules.push(obj);
264          
265     },
266     /**
267      * convert a string to an object..
268      * eg. 'AAA.BBB' -> finds AAA.BBB
269
270      */
271     
272     toObject : function(str)
273     {
274         if (!str || typeof(str) == 'object') {
275             return str;
276         }
277         if (str.substring(0,1) == '#') {
278             return str;
279         }
280
281         var ar = str.split('.');
282         var rt, o;
283         rt = ar.shift();
284             /** eval:var:o */
285         try {
286             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
287         } catch (e) {
288             throw "Module not found : " + str;
289         }
290         
291         if (o === false) {
292             throw "Module not found : " + str;
293         }
294         Roo.each(ar, function(e) {
295             if (typeof(o[e]) == 'undefined') {
296                 throw "Module not found : " + str;
297             }
298             o = o[e];
299         });
300         
301         return o;
302         
303     },
304     
305     
306     /**
307      * move modules into their correct place in the tree..
308      * 
309      */
310     preBuild : function ()
311     {
312         var _t = this;
313         Roo.each(this.modules , function (obj)
314         {
315             var opar = obj.parent;
316             try { 
317                 obj.parent = this.toObject(opar);
318             } catch(e) {
319                 Roo.log("parent:toObject failed: " + e.toString());
320                 return;
321             }
322             
323             if (!obj.parent) {
324                                 Roo.debug && Roo.log("GOT top level module");
325                                 Roo.debug && Roo.log(obj);
326                                 obj.modules = new Roo.util.MixedCollection(false, 
327                     function(o) { return o.order + '' }
328                 );
329                 this.topModule = obj;
330                 return;
331             }
332                         // parent is a string (usually a dom element name..)
333             if (typeof(obj.parent) == 'string') {
334                 this.elmodules.push(obj);
335                 return;
336             }
337             if (obj.parent.constructor != Roo.XComponent) {
338                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
339             }
340             if (!obj.parent.modules) {
341                 obj.parent.modules = new Roo.util.MixedCollection(false, 
342                     function(o) { return o.order + '' }
343                 );
344             }
345             
346             obj.parent.modules.add(obj);
347         }, this);
348     },
349     
350      /**
351      * make a list of modules to build.
352      * @return {Array} list of modules. 
353      */ 
354     
355     buildOrder : function()
356     {
357         var _this = this;
358         var cmp = function(a,b) {   
359             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
360         };
361         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
362             throw "No top level modules to build";
363         }
364         
365         // make a flat list in order of modules to build.
366         var mods = this.topModule ? [ this.topModule ] : [];
367                 
368                 // elmodules (is a list of DOM based modules )
369         Roo.each(this.elmodules,function(e) { mods.push(e) });
370
371         
372         // add modules to their parents..
373         var addMod = function(m) {
374                         Roo.debug && Roo.log("build Order: add: " + m.name);
375             
376             mods.push(m);
377             if (m.modules) {
378                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
379                 m.modules.keySort('ASC',  cmp );
380                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
381
382                 m.modules.each(addMod);
383             } else {
384                                 Roo.debug && Roo.log("build Order: no child modules");
385                         }
386             // not sure if this is used any more..
387             if (m.finalize) {
388                 m.finalize.name = m.name + " (clean up) ";
389                 mods.push(m.finalize);
390             }
391             
392         }
393         if (this.topModule) { 
394             this.topModule.modules.keySort('ASC',  cmp );
395             this.topModule.modules.each(addMod);
396         }
397         return mods;
398     },
399     
400      /**
401      * Build the registered modules.
402      * @param {Object} parent element.
403      * @param {Function} optional method to call after module has been added.
404      * 
405      */ 
406    
407     build : function() 
408     {
409         
410         this.preBuild();
411         var mods = this.buildOrder();
412       
413         //this.allmods = mods;
414         //Roo.debug && Roo.log(mods);
415         //return;
416         if (!mods.length) { // should not happen
417             throw "NO modules!!!";
418         }
419         
420         
421         var msg = "Building Interface...";
422         // flash it up as modal - so we store the mask!?
423         Roo.MessageBox.show({ title: 'loading' });
424         Roo.MessageBox.show({
425            title: "Please wait...",
426            msg: msg,
427            width:450,
428            progress:true,
429            closable:false,
430            modal: false
431           
432         });
433         var total = mods.length;
434         
435         var _this = this;
436         var progressRun = function() {
437             if (!mods.length) {
438                 Roo.debug && Roo.log('hide?');
439                 Roo.MessageBox.hide();
440                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
441                 
442                 // THE END...
443                 return false;   
444             }
445             
446             var m = mods.shift();
447             
448             
449             Roo.debug && Roo.log(m);
450             // not sure if this is supported any more.. - modules that are are just function
451             if (typeof(m) == 'function') { 
452                 m.call(this);
453                 return progressRun.defer(10, _this);
454             } 
455             
456             
457             msg = "Building Interface " + (total  - mods.length) + 
458                     " of " + total + 
459                     (m.name ? (' - ' + m.name) : '');
460                         Roo.debug && Roo.log(msg);
461             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
462             
463          
464             // is the module disabled?
465             var disabled = (typeof(m.disabled) == 'function') ?
466                 m.disabled.call(m.module.disabled) : m.disabled;    
467             
468             
469             if (disabled) {
470                 return progressRun(); // we do not update the display!
471             }
472             
473             // now build 
474             
475                         
476                         
477             m.render();
478             // it's 10 on top level, and 1 on others??? why...
479             return progressRun.defer(10, _this);
480              
481         }
482         progressRun.defer(1, _this);
483      
484         
485         
486     },
487         
488         
489         /**
490          * Event Object.
491          *
492          *
493          */
494         event: false, 
495     /**
496          * wrapper for event.on - aliased later..  
497          * Typically use to register a event handler for register:
498          *
499          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
500          *
501          */
502     on : false
503    
504     
505     
506 });
507
508 Roo.XComponent.event = new Roo.util.Observable({
509                 events : { 
510                         /**
511                          * @event register
512                          * Fires when an Component is registered,
513                          * set the disable property on the Component to stop registration.
514                          * @param {Roo.XComponent} c the component being registerd.
515                          * 
516                          */
517                         'register' : true,
518                         /**
519                          * @event buildcomplete
520                          * Fires on the top level element when all elements have been built
521                          * @param {Roo.XComponent} the top level component.
522                          */
523                         'buildcomplete' : true
524                         
525                 }
526 });
527
528 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
529