Roo/Component.js
[roojs1] / Roo / Component.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12 /**
13  * @class Roo.Component
14  * @extends Roo.util.Observable
15  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
17  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
18  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
19  * All visual components (widgets) that require rendering into a layout should subclass Component.
20  * @constructor
21  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
23  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
24  */
25 Roo.Component = function(config){
26     console.log("COMPONENT CONSTRUCTOR");
27     console.log(config);
28     config = config || {};
29     if(config.tagName || config.dom || typeof config == "string"){ // element object
30         config = {el: config, id: config.id || config};
31     }
32     this.initialConfig = config;
33
34     Roo.apply(this, config);
35     this.addEvents({
36         /**
37          * @event disable
38          * Fires after the component is disabled.
39              * @param {Roo.Component} this
40              */
41         disable : true,
42         /**
43          * @event enable
44          * Fires after the component is enabled.
45              * @param {Roo.Component} this
46              */
47         enable : true,
48         /**
49          * @event beforeshow
50          * Fires before the component is shown.  Return false to stop the show.
51              * @param {Roo.Component} this
52              */
53         beforeshow : true,
54         /**
55          * @event show
56          * Fires after the component is shown.
57              * @param {Roo.Component} this
58              */
59         show : true,
60         /**
61          * @event beforehide
62          * Fires before the component is hidden. Return false to stop the hide.
63              * @param {Roo.Component} this
64              */
65         beforehide : true,
66         /**
67          * @event hide
68          * Fires after the component is hidden.
69              * @param {Roo.Component} this
70              */
71         hide : true,
72         /**
73          * @event beforerender
74          * Fires before the component is rendered. Return false to stop the render.
75              * @param {Roo.Component} this
76              */
77         beforerender : true,
78         /**
79          * @event render
80          * Fires after the component is rendered.
81              * @param {Roo.Component} this
82              */
83         render : true,
84         /**
85          * @event beforedestroy
86          * Fires before the component is destroyed. Return false to stop the destroy.
87              * @param {Roo.Component} this
88              */
89         beforedestroy : true,
90         /**
91          * @event destroy
92          * Fires after the component is destroyed.
93              * @param {Roo.Component} this
94              */
95         destroy : true
96     });
97     if(!this.id){
98         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
99     }
100     Roo.ComponentMgr.register(this);
101     Roo.Component.superclass.constructor.call(this);
102     this.initComponent();
103     if(this.renderTo){ // not supported by all components yet. use at your own risk!
104         this.render(this.renderTo);
105         delete this.renderTo;
106     }
107 };
108
109 /** @private */
110 Roo.Component.AUTO_ID = 1000;
111
112 Roo.extend(Roo.Component, Roo.util.Observable, {
113     /**
114      * @scope Roo.Component.prototype
115      * @type {Boolean}
116      * true if this component is hidden. Read-only.
117      */
118     hidden : false,
119     /**
120      * @type {Boolean}
121      * true if this component is disabled. Read-only.
122      */
123     disabled : false,
124     /**
125      * @type {Boolean}
126      * true if this component has been rendered. Read-only.
127      */
128     rendered : false,
129     
130     /** @cfg {String} disableClass
131      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
132      */
133     disabledClass : "x-item-disabled",
134         /** @cfg {Boolean} allowDomMove
135          * Whether the component can move the Dom node when rendering (defaults to true).
136          */
137     allowDomMove : true,
138     /** @cfg {String} hideMode (display|visibility)
139      * How this component should hidden. Supported values are
140      * "visibility" (css visibility), "offsets" (negative offset position) and
141      * "display" (css display) - defaults to "display".
142      */
143     hideMode: 'display',
144
145     /** @private */
146     ctype : "Roo.Component",
147
148     /**
149      * @cfg {String} actionMode 
150      * which property holds the element that used for  hide() / show() / disable() / enable()
151      * default is 'el' for forms you probably want to set this to fieldEl 
152      */
153     actionMode : "el",
154
155     /** @private */
156     getActionEl : function(){
157         return this[this.actionMode];
158     },
159
160     initComponent : Roo.emptyFn,
161     /**
162      * If this is a lazy rendering component, render it to its container element.
163      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
164      */
165     render : function(container, position){
166         
167         if(this.rendered){
168             return this;
169         }
170         
171         if(this.fireEvent("beforerender", this) === false){
172             return false;
173         }
174         
175         if(!container && this.el){
176             this.el = Roo.get(this.el);
177             container = this.el.dom.parentNode;
178             this.allowDomMove = false;
179         }
180         this.container = Roo.get(container);
181         this.rendered = true;
182         if(position !== undefined){
183             if(typeof position == 'number'){
184                 position = this.container.dom.childNodes[position];
185             }else{
186                 position = Roo.getDom(position);
187             }
188         }
189         this.onRender(this.container, position || null);
190         if(this.cls){
191             this.el.addClass(this.cls);
192             delete this.cls;
193         }
194         if(this.style){
195             this.el.applyStyles(this.style);
196             delete this.style;
197         }
198         this.fireEvent("render", this);
199         this.afterRender(this.container);
200         if(this.hidden){
201             this.hide();
202         }
203         if(this.disabled){
204             this.disable();
205         }
206
207         return this;
208         
209     },
210
211     /** @private */
212     // default function is not really useful
213     onRender : function(ct, position){
214         if(this.el){
215             this.el = Roo.get(this.el);
216             if(this.allowDomMove !== false){
217                 ct.dom.insertBefore(this.el.dom, position);
218             }
219         }
220     },
221
222     /** @private */
223     getAutoCreate : function(){
224         var cfg = typeof this.autoCreate == "object" ?
225                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
226         if(this.id && !cfg.id){
227             cfg.id = this.id;
228         }
229         return cfg;
230     },
231
232     /** @private */
233     afterRender : Roo.emptyFn,
234
235     /**
236      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
237      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
238      */
239     destroy : function(){
240         if(this.fireEvent("beforedestroy", this) !== false){
241             this.purgeListeners();
242             this.beforeDestroy();
243             if(this.rendered){
244                 this.el.removeAllListeners();
245                 this.el.remove();
246                 if(this.actionMode == "container"){
247                     this.container.remove();
248                 }
249             }
250             this.onDestroy();
251             Roo.ComponentMgr.unregister(this);
252             this.fireEvent("destroy", this);
253         }
254     },
255
256         /** @private */
257     beforeDestroy : function(){
258
259     },
260
261         /** @private */
262         onDestroy : function(){
263
264     },
265
266     /**
267      * Returns the underlying {@link Roo.Element}.
268      * @return {Roo.Element} The element
269      */
270     getEl : function(){
271         return this.el;
272     },
273
274     /**
275      * Returns the id of this component.
276      * @return {String}
277      */
278     getId : function(){
279         return this.id;
280     },
281
282     /**
283      * Try to focus this component.
284      * @param {Boolean} selectText True to also select the text in this component (if applicable)
285      * @return {Roo.Component} this
286      */
287     focus : function(selectText){
288         if(this.rendered){
289             this.el.focus();
290             if(selectText === true){
291                 this.el.dom.select();
292             }
293         }
294         return this;
295     },
296
297     /** @private */
298     blur : function(){
299         if(this.rendered){
300             this.el.blur();
301         }
302         return this;
303     },
304
305     /**
306      * Disable this component.
307      * @return {Roo.Component} this
308      */
309     disable : function(){
310         if(this.rendered){
311             this.onDisable();
312         }
313         this.disabled = true;
314         this.fireEvent("disable", this);
315         return this;
316     },
317
318         // private
319     onDisable : function(){
320         this.getActionEl().addClass(this.disabledClass);
321         this.el.dom.disabled = true;
322     },
323
324     /**
325      * Enable this component.
326      * @return {Roo.Component} this
327      */
328     enable : function(){
329         if(this.rendered){
330             this.onEnable();
331         }
332         this.disabled = false;
333         this.fireEvent("enable", this);
334         return this;
335     },
336
337         // private
338     onEnable : function(){
339         this.getActionEl().removeClass(this.disabledClass);
340         this.el.dom.disabled = false;
341     },
342
343     /**
344      * Convenience function for setting disabled/enabled by boolean.
345      * @param {Boolean} disabled
346      */
347     setDisabled : function(disabled){
348         this[disabled ? "disable" : "enable"]();
349     },
350
351     /**
352      * Show this component.
353      * @return {Roo.Component} this
354      */
355     show: function(){
356         if(this.fireEvent("beforeshow", this) !== false){
357             this.hidden = false;
358             if(this.rendered){
359                 this.onShow();
360             }
361             this.fireEvent("show", this);
362         }
363         return this;
364     },
365
366     // private
367     onShow : function(){
368         var ae = this.getActionEl();
369         if(this.hideMode == 'visibility'){
370             ae.dom.style.visibility = "visible";
371         }else if(this.hideMode == 'offsets'){
372             ae.removeClass('x-hidden');
373         }else{
374             ae.dom.style.display = "";
375         }
376     },
377
378     /**
379      * Hide this component.
380      * @return {Roo.Component} this
381      */
382     hide: function(){
383         if(this.fireEvent("beforehide", this) !== false){
384             this.hidden = true;
385             if(this.rendered){
386                 this.onHide();
387             }
388             this.fireEvent("hide", this);
389         }
390         return this;
391     },
392
393     // private
394     onHide : function(){
395         var ae = this.getActionEl();
396         if(this.hideMode == 'visibility'){
397             ae.dom.style.visibility = "hidden";
398         }else if(this.hideMode == 'offsets'){
399             ae.addClass('x-hidden');
400         }else{
401             ae.dom.style.display = "none";
402         }
403     },
404
405     /**
406      * Convenience function to hide or show this component by boolean.
407      * @param {Boolean} visible True to show, false to hide
408      * @return {Roo.Component} this
409      */
410     setVisible: function(visible){
411         if(visible) {
412             this.show();
413         }else{
414             this.hide();
415         }
416         return this;
417     },
418
419     /**
420      * Returns true if this component is visible.
421      */
422     isVisible : function(){
423         return this.getActionEl().isVisible();
424     },
425
426     cloneConfig : function(overrides){
427         overrides = overrides || {};
428         var id = overrides.id || Roo.id();
429         var cfg = Roo.applyIf(overrides, this.initialConfig);
430         cfg.id = id; // prevent dup id
431         return new this.constructor(cfg);
432     }
433 });