remove debugging code
[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  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
11  *  used in conjunction with XComponent.build() it will create an instance of each element,
12  *  then call addxtype() to build the User interface.
13  * 
14  * Mypart.xyx = new Roo.XComponent({
15
16     parent : 'Mypart.xyz', // empty == document.element.!!
17     order : '001',
18     name : 'xxxx'
19     region : 'xxxx'
20     disabled : function() {} 
21      
22     tree : function() { // return an tree of xtype declared components
23         var MODULE = this;
24         return 
25         {
26             xtype : 'NestedLayoutPanel',
27             // technicall
28         }
29      ]
30  *})
31  *
32  *
33  * It can be used to build a big heiracy, with parent etc.
34  * or you can just use this to render a single compoent to a dom element
35  * MYPART.render(Roo.Element | String(id) | dom_element )
36  *
37  *
38  * Usage patterns.
39  *
40  * Classic Roo
41  *
42  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
43  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
44  *
45  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
46  *
47  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
48  * - if mulitple topModules exist, the last one is defined as the top module.
49  *
50  * Embeded Roo
51  * 
52  * When the top level or multiple modules are to embedded into a existing HTML page,
53  * the parent element can container '#id' of the element where the module will be drawn.
54  *
55  * Bootstrap Roo
56  *
57  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
58  * it relies more on a include mechanism, where sub modules are included into an outer page.
59  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
60  * 
61  * Bootstrap Roo Included elements
62  *
63  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
64  * hence confusing the component builder as it thinks there are multiple top level elements. 
65  *
66  * String Over-ride & Translations
67  *
68  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
69  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
70  * are needed. @see Roo.XComponent.overlayString  
71  * 
72  * 
73  * 
74  * @extends Roo.util.Observable
75  * @constructor
76  * @param cfg {Object} configuration of component
77  * 
78  */
79 Roo.XComponent = function(cfg) {
80     Roo.apply(this, cfg);
81     this.addEvents({ 
82         /**
83              * @event built
84              * Fires when this the componnt is built
85              * @param {Roo.XComponent} c the component
86              */
87         'built' : true
88         
89     });
90     this.region = this.region || 'center'; // default..
91     Roo.XComponent.register(this);
92     this.modules = false;
93     this.el = false; // where the layout goes..
94     
95     
96 }
97 Roo.extend(Roo.XComponent, Roo.util.Observable, {
98     /**
99      * @property el
100      * The created element (with Roo.factory())
101      * @type {Roo.Layout}
102      */
103     el  : false,
104     
105     /**
106      * @property el
107      * for BC  - use el in new code
108      * @type {Roo.Layout}
109      */
110     panel : false,
111     
112     /**
113      * @property layout
114      * for BC  - use el in new code
115      * @type {Roo.Layout}
116      */
117     layout : false,
118     
119      /**
120      * @cfg {Function|boolean} disabled
121      * If this module is disabled by some rule, return true from the funtion
122      */
123     disabled : false,
124     
125     /**
126      * @cfg {String} parent 
127      * Name of parent element which it get xtype added to..
128      */
129     parent: false,
130     
131     /**
132      * @cfg {String} order
133      * Used to set the order in which elements are created (usefull for multiple tabs)
134      */
135     
136     order : false,
137     /**
138      * @cfg {String} name
139      * String to display while loading.
140      */
141     name : false,
142     /**
143      * @cfg {String} region
144      * Region to render component to (defaults to center)
145      */
146     region : 'center',
147     
148     /**
149      * @cfg {Array} items
150      * A single item array - the first element is the root of the tree..
151      * It's done this way to stay compatible with the Xtype system...
152      */
153     items : false,
154     
155     /**
156      * @property _tree
157      * The method that retuns the tree of parts that make up this compoennt 
158      * @type {function}
159      */
160     _tree  : false,
161     
162      /**
163      * render
164      * render element to dom or tree
165      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
166      */
167     
168     render : function(el)
169     {
170         
171         el = el || false;
172         var hp = this.parent ? 1 : 0;
173         Roo.debug &&  Roo.log(this);
174         
175         var tree = this._tree ? this._tree() : this.tree();
176
177         
178         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
179             // if parent is a '#.....' string, then let's use that..
180             var ename = this.parent.substr(1);
181             this.parent = false;
182             Roo.debug && Roo.log(ename);
183             switch (ename) {
184                 case 'bootstrap-body':
185                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
186                         // this is the BorderLayout standard?
187                        this.parent = { el : true };
188                        break;
189                     }
190                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
191                         // need to insert stuff...
192                         this.parent =  {
193                              el : new Roo.bootstrap.layout.Border({
194                                  el : document.body, 
195                      
196                                  center: {
197                                     titlebar: false,
198                                     autoScroll:false,
199                                     closeOnTab: true,
200                                     tabPosition: 'top',
201                                       //resizeTabs: true,
202                                     alwaysShowTabs: true,
203                                     hideTabs: false
204                                      //minTabWidth: 140
205                                  }
206                              })
207                         
208                          };
209                          break;
210                     }
211                          
212                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
213                         this.parent = { el :  new  Roo.bootstrap.Body() };
214                         Roo.debug && Roo.log("setting el to doc body");
215                          
216                     } else {
217                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
218                     }
219                     break;
220                 case 'bootstrap':
221                     this.parent = { el : true};
222                     // fall through
223                 default:
224                     el = Roo.get(ename);
225                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
226                         this.parent = { el : true};
227                     }
228                     
229                     break;
230             }
231                 
232             
233             if (!el && !this.parent) {
234                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
235                 return;
236             }
237         }
238         
239         Roo.debug && Roo.log("EL:");
240         Roo.debug && Roo.log(el);
241         Roo.debug && Roo.log("this.parent.el:");
242         Roo.debug && Roo.log(this.parent.el);
243         
244
245         // altertive root elements ??? - we need a better way to indicate these.
246         var is_alt = Roo.XComponent.is_alt ||
247                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
248                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
249                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
250         
251         
252         
253         if (!this.parent && is_alt) {
254             //el = Roo.get(document.body);
255             this.parent = { el : true };
256         }
257             
258             
259         
260         if (!this.parent) {
261             
262             Roo.debug && Roo.log("no parent - creating one");
263             
264             el = el ? Roo.get(el) : false;      
265             
266             if (typeof(Roo.BorderLayout) == 'undefined' ) {
267                 
268                 this.parent =  {
269                     el : new Roo.bootstrap.layout.Border({
270                         el: el || document.body,
271                     
272                         center: {
273                             titlebar: false,
274                             autoScroll:false,
275                             closeOnTab: true,
276                             tabPosition: 'top',
277                              //resizeTabs: true,
278                             alwaysShowTabs: false,
279                             hideTabs: true,
280                             minTabWidth: 140,
281                             overflow: 'visible'
282                          }
283                      })
284                 };
285             } else {
286             
287                 // it's a top level one..
288                 this.parent =  {
289                     el : new Roo.BorderLayout(el || document.body, {
290                         center: {
291                             titlebar: false,
292                             autoScroll:false,
293                             closeOnTab: true,
294                             tabPosition: 'top',
295                              //resizeTabs: true,
296                             alwaysShowTabs: el && hp? false :  true,
297                             hideTabs: el || !hp ? true :  false,
298                             minTabWidth: 140
299                          }
300                     })
301                 };
302             }
303         }
304         
305         if (!this.parent.el) {
306                 // probably an old style ctor, which has been disabled.
307                 return;
308
309         }
310                 // The 'tree' method is  '_tree now' 
311             
312         tree.region = tree.region || this.region;
313         var is_body = false;
314         if (this.parent.el === true) {
315             // bootstrap... - body..
316             if (el) {
317                 tree.el = el;
318             }
319             this.parent.el = Roo.factory(tree);
320             is_body = true;
321         }
322         
323         this.el = this.parent.el.addxtype(tree, undefined, is_body);
324         this.fireEvent('built', this);
325         
326         this.panel = this.el;
327         this.layout = this.panel.layout;
328         this.parentLayout = this.parent.layout  || false;  
329          
330     }
331     
332 });
333
334 Roo.apply(Roo.XComponent, {
335     /**
336      * @property  hideProgress
337      * true to disable the building progress bar.. usefull on single page renders.
338      * @type Boolean
339      */
340     hideProgress : false,
341     /**
342      * @property  buildCompleted
343      * True when the builder has completed building the interface.
344      * @type Boolean
345      */
346     buildCompleted : false,
347      
348     /**
349      * @property  topModule
350      * the upper most module - uses document.element as it's constructor.
351      * @type Object
352      */
353      
354     topModule  : false,
355       
356     /**
357      * @property  modules
358      * array of modules to be created by registration system.
359      * @type {Array} of Roo.XComponent
360      */
361     
362     modules : [],
363     /**
364      * @property  elmodules
365      * array of modules to be created by which use #ID 
366      * @type {Array} of Roo.XComponent
367      */
368      
369     elmodules : [],
370
371      /**
372      * @property  is_alt
373      * Is an alternative Root - normally used by bootstrap or other systems,
374      *    where the top element in the tree can wrap 'body' 
375      * @type {boolean}  (default false)
376      */
377      
378     is_alt : false,
379     /**
380      * @property  build_from_html
381      * Build elements from html - used by bootstrap HTML stuff 
382      *    - this is cleared after build is completed
383      * @type {boolean}    (default false)
384      */
385      
386     build_from_html : false,
387     /**
388      * Register components to be built later.
389      *
390      * This solves the following issues
391      * - Building is not done on page load, but after an authentication process has occured.
392      * - Interface elements are registered on page load
393      * - Parent Interface elements may not be loaded before child, so this handles that..
394      * 
395      *
396      * example:
397      * 
398      * MyApp.register({
399           order : '000001',
400           module : 'Pman.Tab.projectMgr',
401           region : 'center',
402           parent : 'Pman.layout',
403           disabled : false,  // or use a function..
404         })
405      
406      * * @param {Object} details about module
407      */
408     register : function(obj) {
409                 
410         Roo.XComponent.event.fireEvent('register', obj);
411         switch(typeof(obj.disabled) ) {
412                 
413             case 'undefined':
414                 break;
415             
416             case 'function':
417                 if ( obj.disabled() ) {
418                         return;
419                 }
420                 break;
421             
422             default:
423                 if (obj.disabled || obj.region == '#disabled') {
424                         return;
425                 }
426                 break;
427         }
428                 
429         this.modules.push(obj);
430          
431     },
432     /**
433      * convert a string to an object..
434      * eg. 'AAA.BBB' -> finds AAA.BBB
435
436      */
437     
438     toObject : function(str)
439     {
440         if (!str || typeof(str) == 'object') {
441             return str;
442         }
443         if (str.substring(0,1) == '#') {
444             return str;
445         }
446
447         var ar = str.split('.');
448         var rt, o;
449         rt = ar.shift();
450             /** eval:var:o */
451         try {
452             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
453         } catch (e) {
454             throw "Module not found : " + str;
455         }
456         
457         if (o === false) {
458             throw "Module not found : " + str;
459         }
460         Roo.each(ar, function(e) {
461             if (typeof(o[e]) == 'undefined') {
462                 throw "Module not found : " + str;
463             }
464             o = o[e];
465         });
466         
467         return o;
468         
469     },
470     
471     
472     /**
473      * move modules into their correct place in the tree..
474      * 
475      */
476     preBuild : function ()
477     {
478         var _t = this;
479         Roo.each(this.modules , function (obj)
480         {
481             Roo.XComponent.event.fireEvent('beforebuild', obj);
482             
483             var opar = obj.parent;
484             try { 
485                 obj.parent = this.toObject(opar);
486             } catch(e) {
487                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
488                 return;
489             }
490             
491             if (!obj.parent) {
492                 Roo.debug && Roo.log("GOT top level module");
493                 Roo.debug && Roo.log(obj);
494                 obj.modules = new Roo.util.MixedCollection(false, 
495                     function(o) { return o.order + '' }
496                 );
497                 this.topModule = obj;
498                 return;
499             }
500                         // parent is a string (usually a dom element name..)
501             if (typeof(obj.parent) == 'string') {
502                 this.elmodules.push(obj);
503                 return;
504             }
505             if (obj.parent.constructor != Roo.XComponent) {
506                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
507             }
508             if (!obj.parent.modules) {
509                 obj.parent.modules = new Roo.util.MixedCollection(false, 
510                     function(o) { return o.order + '' }
511                 );
512             }
513             if (obj.parent.disabled) {
514                 obj.disabled = true;
515             }
516             obj.parent.modules.add(obj);
517         }, this);
518     },
519     
520      /**
521      * make a list of modules to build.
522      * @return {Array} list of modules. 
523      */ 
524     
525     buildOrder : function()
526     {
527         var _this = this;
528         var cmp = function(a,b) {   
529             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
530         };
531         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
532             throw "No top level modules to build";
533         }
534         
535         // make a flat list in order of modules to build.
536         var mods = this.topModule ? [ this.topModule ] : [];
537                 
538         
539         // elmodules (is a list of DOM based modules )
540         Roo.each(this.elmodules, function(e) {
541             mods.push(e);
542             if (!this.topModule &&
543                 typeof(e.parent) == 'string' &&
544                 e.parent.substring(0,1) == '#' &&
545                 Roo.get(e.parent.substr(1))
546                ) {
547                 
548                 _this.topModule = e;
549             }
550             
551         });
552
553         
554         // add modules to their parents..
555         var addMod = function(m) {
556             Roo.debug && Roo.log("build Order: add: " + m.name);
557                 
558             mods.push(m);
559             if (m.modules && !m.disabled) {
560                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
561                 m.modules.keySort('ASC',  cmp );
562                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
563     
564                 m.modules.each(addMod);
565             } else {
566                 Roo.debug && Roo.log("build Order: no child modules");
567             }
568             // not sure if this is used any more..
569             if (m.finalize) {
570                 m.finalize.name = m.name + " (clean up) ";
571                 mods.push(m.finalize);
572             }
573             
574         }
575         if (this.topModule && this.topModule.modules) { 
576             this.topModule.modules.keySort('ASC',  cmp );
577             this.topModule.modules.each(addMod);
578         } 
579         return mods;
580     },
581     
582      /**
583      * Build the registered modules.
584      * @param {Object} parent element.
585      * @param {Function} optional method to call after module has been added.
586      * 
587      */ 
588    
589     build : function(opts) 
590     {
591         
592         if (typeof(opts) != 'undefined') {
593             Roo.apply(this,opts);
594         }
595         
596         this.preBuild();
597         var mods = this.buildOrder();
598       
599         //this.allmods = mods;
600         //Roo.debug && Roo.log(mods);
601         //return;
602         if (!mods.length) { // should not happen
603             throw "NO modules!!!";
604         }
605         
606         
607         var msg = "Building Interface...";
608         // flash it up as modal - so we store the mask!?
609         if (!this.hideProgress && Roo.MessageBox) {
610             Roo.MessageBox.show({ title: 'loading' });
611             Roo.MessageBox.show({
612                title: "Please wait...",
613                msg: msg,
614                width:450,
615                progress:true,
616                buttons : false,
617                closable:false,
618                modal: false
619               
620             });
621         }
622         var total = mods.length;
623         
624         var _this = this;
625         var progressRun = function() {
626             if (!mods.length) {
627                 Roo.debug && Roo.log('hide?');
628                 if (!this.hideProgress && Roo.MessageBox) {
629                     Roo.MessageBox.hide();
630                 }
631                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
632                 
633                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
634                 
635                 // THE END...
636                 return false;   
637             }
638             
639             var m = mods.shift();
640             
641             
642             Roo.debug && Roo.log(m);
643             // not sure if this is supported any more.. - modules that are are just function
644             if (typeof(m) == 'function') { 
645                 m.call(this);
646                 return progressRun.defer(10, _this);
647             } 
648             
649             
650             msg = "Building Interface " + (total  - mods.length) + 
651                     " of " + total + 
652                     (m.name ? (' - ' + m.name) : '');
653                         Roo.debug && Roo.log(msg);
654             if (!_this.hideProgress &&  Roo.MessageBox) { 
655                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
656             }
657             
658          
659             // is the module disabled?
660             var disabled = (typeof(m.disabled) == 'function') ?
661                 m.disabled.call(m.module.disabled) : m.disabled;    
662             
663             
664             if (disabled) {
665                 return progressRun(); // we do not update the display!
666             }
667             
668             // now build 
669             
670                         
671                         
672             m.render();
673             // it's 10 on top level, and 1 on others??? why...
674             return progressRun.defer(10, _this);
675              
676         }
677         progressRun.defer(1, _this);
678      
679         
680         
681     },
682     /**
683      * Overlay a set of modified strings onto a component
684      * This is dependant on our builder exporting the strings and 'named strings' elements.
685      * 
686      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
687      * @param {Object} associative array of 'named' string and it's new value.
688      * 
689      */
690         overlayStrings : function( component, strings )
691     {
692         if (typeof(component['_named_strings']) == 'undefined') {
693             throw "ERROR: component does not have _named_strings";
694         }
695         for ( var k in strings ) {
696             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
697             if (md !== false) {
698                 component['_strings'][md] = strings[k];
699             } else {
700                 Roo.log('could not find named string: ' + k + ' in');
701                 Roo.log(component);
702             }
703             
704         }
705         
706     },
707     
708         
709         /**
710          * Event Object.
711          *
712          *
713          */
714         event: false, 
715     /**
716          * wrapper for event.on - aliased later..  
717          * Typically use to register a event handler for register:
718          *
719          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
720          *
721          */
722     on : false
723    
724     
725     
726 });
727
728 Roo.XComponent.event = new Roo.util.Observable({
729                 events : { 
730                         /**
731                          * @event register
732                          * Fires when an Component is registered,
733                          * set the disable property on the Component to stop registration.
734                          * @param {Roo.XComponent} c the component being registerd.
735                          * 
736                          */
737                         'register' : true,
738             /**
739                          * @event beforebuild
740                          * Fires before each Component is built
741                          * can be used to apply permissions.
742                          * @param {Roo.XComponent} c the component being registerd.
743                          * 
744                          */
745                         'beforebuild' : true,
746                         /**
747                          * @event buildcomplete
748                          * Fires on the top level element when all elements have been built
749                          * @param {Roo.XComponent} the top level component.
750                          */
751                         'buildcomplete' : true
752                         
753                 }
754 });
755
756 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
757