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