Roo/Button.js
[roojs1] / Roo / Button.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.Button
14  * @extends Roo.util.Observable
15  * Simple Button class
16  * @cfg {String} text The button text
17  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
18  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
19  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
20  * @cfg {Object} scope The scope of the handler
21  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
22  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
23  * @cfg {Boolean} hidden True to start hidden (defaults to false)
24  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27    applies if enableToggle = true)
28  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30   an {@link Roo.util.ClickRepeater} config object (defaults to false).
31  * @constructor
32  * Create a new button
33  * @param {Object} config The config object
34  */
35 Roo.Button = function(renderTo, config)
36 {
37     if (!config) {
38         config = renderTo;
39         renderTo = config.renderTo || false;
40     }
41     
42     Roo.apply(this, config);
43     this.addEvents({
44         /**
45              * @event click
46              * Fires when this button is clicked
47              * @param {Button} this
48              * @param {EventObject} e The click event
49              */
50             "click" : true,
51         /**
52              * @event toggle
53              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
54              * @param {Button} this
55              * @param {Boolean} pressed
56              */
57             "toggle" : true,
58         /**
59              * @event mouseover
60              * Fires when the mouse hovers over the button
61              * @param {Button} this
62              * @param {Event} e The event object
63              */
64         'mouseover' : true,
65         /**
66              * @event mouseout
67              * Fires when the mouse exits the button
68              * @param {Button} this
69              * @param {Event} e The event object
70              */
71         'mouseout': true,
72          /**
73              * @event render
74              * Fires when the button is rendered
75              * @param {Button} this
76              */
77         'render': true
78     });
79     if(this.menu){
80         this.menu = Roo.menu.MenuMgr.get(this.menu);
81     }
82     Roo.util.Observable.call(this);
83     if(renderTo){
84         this.render(renderTo);
85     }
86     
87   
88 };
89
90 Roo.extend(Roo.Button, Roo.util.Observable, {
91     /**
92      * 
93      */
94     
95     /**
96      * Read-only. True if this button is hidden
97      * @type Boolean
98      */
99     hidden : false,
100     /**
101      * Read-only. True if this button is disabled
102      * @type Boolean
103      */
104     disabled : false,
105     /**
106      * Read-only. True if this button is pressed (only if enableToggle = true)
107      * @type Boolean
108      */
109     pressed : false,
110
111     /**
112      * @cfg {Number} tabIndex 
113      * The DOM tabIndex for this button (defaults to undefined)
114      */
115     tabIndex : undefined,
116
117     /**
118      * @cfg {Boolean} enableToggle
119      * True to enable pressed/not pressed toggling (defaults to false)
120      */
121     enableToggle: false,
122     /**
123      * @cfg {Mixed} menu
124      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
125      */
126     menu : undefined,
127     /**
128      * @cfg {String} menuAlign
129      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
130      */
131     menuAlign : "tl-bl?",
132
133     /**
134      * @cfg {String} iconCls
135      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
136      */
137     iconCls : undefined,
138     /**
139      * @cfg {String} type
140      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
141      */
142     type : 'button',
143
144     // private
145     menuClassTarget: 'tr',
146
147     /**
148      * @cfg {String} clickEvent
149      * The type of event to map to the button's event handler (defaults to 'click')
150      */
151     clickEvent : 'click',
152
153     /**
154      * @cfg {Boolean} handleMouseEvents
155      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
156      */
157     handleMouseEvents : true,
158
159     /**
160      * @cfg {String} tooltipType
161      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
162      */
163     tooltipType : 'qtip',
164
165     /**
166      * @cfg {String} cls
167      * A CSS class to apply to the button's main element.
168      */
169     
170     /**
171      * @cfg {Roo.Template} template (Optional)
172      * An {@link Roo.Template} with which to create the Button's main element. This Template must
173      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
174      * require code modifications if required elements (e.g. a button) aren't present.
175      */
176
177     // private
178     render : function(renderTo){
179         var btn;
180         if(this.hideParent){
181             this.parentEl = Roo.get(renderTo);
182         }
183         if(!this.dhconfig){
184             if(!this.template){
185                 if(!Roo.Button.buttonTemplate){
186                     // hideous table template
187                     Roo.Button.buttonTemplate = new Roo.Template(
188                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
189                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
190                         "</tr></tbody></table>");
191                 }
192                 this.template = Roo.Button.buttonTemplate;
193             }
194             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
195             var btnEl = btn.child("button:first");
196             btnEl.on('focus', this.onFocus, this);
197             btnEl.on('blur', this.onBlur, this);
198             if(this.cls){
199                 btn.addClass(this.cls);
200             }
201             if(this.icon){
202                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
203             }
204             if(this.iconCls){
205                 btnEl.addClass(this.iconCls);
206                 if(!this.cls){
207                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
208                 }
209             }
210             if(this.tabIndex !== undefined){
211                 btnEl.dom.tabIndex = this.tabIndex;
212             }
213             if(this.tooltip){
214                 if(typeof this.tooltip == 'object'){
215                     Roo.QuickTips.tips(Roo.apply({
216                           target: btnEl.id
217                     }, this.tooltip));
218                 } else {
219                     btnEl.dom[this.tooltipType] = this.tooltip;
220                 }
221             }
222         }else{
223             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
224         }
225         this.el = btn;
226         if(this.id){
227             this.el.dom.id = this.el.id = this.id;
228         }
229         if(this.menu){
230             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
231             this.menu.on("show", this.onMenuShow, this);
232             this.menu.on("hide", this.onMenuHide, this);
233         }
234         btn.addClass("x-btn");
235         if(Roo.isIE && !Roo.isIE7){
236             this.autoWidth.defer(1, this);
237         }else{
238             this.autoWidth();
239         }
240         if(this.handleMouseEvents){
241             btn.on("mouseover", this.onMouseOver, this);
242             btn.on("mouseout", this.onMouseOut, this);
243             btn.on("mousedown", this.onMouseDown, this);
244         }
245         btn.on(this.clickEvent, this.onClick, this);
246         //btn.on("mouseup", this.onMouseUp, this);
247         if(this.hidden){
248             this.hide();
249         }
250         if(this.disabled){
251             this.disable();
252         }
253         Roo.ButtonToggleMgr.register(this);
254         if(this.pressed){
255             this.el.addClass("x-btn-pressed");
256         }
257         if(this.repeat){
258             var repeater = new Roo.util.ClickRepeater(btn,
259                 typeof this.repeat == "object" ? this.repeat : {}
260             );
261             repeater.on("click", this.onClick,  this);
262         }
263         Roo.log("FIRING RENDER EVENT");
264         this.fireEvent('render', this);
265         
266     },
267     /**
268      * Returns the button's underlying element
269      * @return {Roo.Element} The element
270      */
271     getEl : function(){
272         return this.el;  
273     },
274     
275     /**
276      * Destroys this Button and removes any listeners.
277      */
278     destroy : function(){
279         Roo.ButtonToggleMgr.unregister(this);
280         this.el.removeAllListeners();
281         this.purgeListeners();
282         this.el.remove();
283     },
284
285     // private
286     autoWidth : function(){
287         if(this.el){
288             this.el.setWidth("auto");
289             if(Roo.isIE7 && Roo.isStrict){
290                 var ib = this.el.child('button');
291                 if(ib && ib.getWidth() > 20){
292                     ib.clip();
293                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
294                 }
295             }
296             if(this.minWidth){
297                 if(this.hidden){
298                     this.el.beginMeasure();
299                 }
300                 if(this.el.getWidth() < this.minWidth){
301                     this.el.setWidth(this.minWidth);
302                 }
303                 if(this.hidden){
304                     this.el.endMeasure();
305                 }
306             }
307         }
308     },
309
310     /**
311      * Assigns this button's click handler
312      * @param {Function} handler The function to call when the button is clicked
313      * @param {Object} scope (optional) Scope for the function passed in
314      */
315     setHandler : function(handler, scope){
316         this.handler = handler;
317         this.scope = scope;  
318     },
319     
320     /**
321      * Sets this button's text
322      * @param {String} text The button text
323      */
324     setText : function(text){
325         this.text = text;
326         if(this.el){
327             this.el.child("td.x-btn-center button.x-btn-text").update(text);
328         }
329         this.autoWidth();
330     },
331     
332     /**
333      * Gets the text for this button
334      * @return {String} The button text
335      */
336     getText : function(){
337         return this.text;  
338     },
339     
340     /**
341      * Show this button
342      */
343     show: function(){
344         this.hidden = false;
345         if(this.el){
346             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
347         }
348     },
349     
350     /**
351      * Hide this button
352      */
353     hide: function(){
354         this.hidden = true;
355         if(this.el){
356             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
357         }
358     },
359     
360     /**
361      * Convenience function for boolean show/hide
362      * @param {Boolean} visible True to show, false to hide
363      */
364     setVisible: function(visible){
365         if(visible) {
366             this.show();
367         }else{
368             this.hide();
369         }
370     },
371     
372     /**
373      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
374      * @param {Boolean} state (optional) Force a particular state
375      */
376     toggle : function(state){
377         state = state === undefined ? !this.pressed : state;
378         if(state != this.pressed){
379             if(state){
380                 this.el.addClass("x-btn-pressed");
381                 this.pressed = true;
382                 this.fireEvent("toggle", this, true);
383             }else{
384                 this.el.removeClass("x-btn-pressed");
385                 this.pressed = false;
386                 this.fireEvent("toggle", this, false);
387             }
388             if(this.toggleHandler){
389                 this.toggleHandler.call(this.scope || this, this, state);
390             }
391         }
392     },
393     
394     /**
395      * Focus the button
396      */
397     focus : function(){
398         this.el.child('button:first').focus();
399     },
400     
401     /**
402      * Disable this button
403      */
404     disable : function(){
405         if(this.el){
406             this.el.addClass("x-btn-disabled");
407         }
408         this.disabled = true;
409     },
410     
411     /**
412      * Enable this button
413      */
414     enable : function(){
415         if(this.el){
416             this.el.removeClass("x-btn-disabled");
417         }
418         this.disabled = false;
419     },
420
421     /**
422      * Convenience function for boolean enable/disable
423      * @param {Boolean} enabled True to enable, false to disable
424      */
425     setDisabled : function(v){
426         this[v !== true ? "enable" : "disable"]();
427     },
428
429     // private
430     onClick : function(e){
431         if(e){
432             e.preventDefault();
433         }
434         if(e.button != 0){
435             return;
436         }
437         if(!this.disabled){
438             if(this.enableToggle){
439                 this.toggle();
440             }
441             if(this.menu && !this.menu.isVisible()){
442                 this.menu.show(this.el, this.menuAlign);
443             }
444             this.fireEvent("click", this, e);
445             if(this.handler){
446                 this.el.removeClass("x-btn-over");
447                 this.handler.call(this.scope || this, this, e);
448             }
449         }
450     },
451     // private
452     onMouseOver : function(e){
453         if(!this.disabled){
454             this.el.addClass("x-btn-over");
455             this.fireEvent('mouseover', this, e);
456         }
457     },
458     // private
459     onMouseOut : function(e){
460         if(!e.within(this.el,  true)){
461             this.el.removeClass("x-btn-over");
462             this.fireEvent('mouseout', this, e);
463         }
464     },
465     // private
466     onFocus : function(e){
467         if(!this.disabled){
468             this.el.addClass("x-btn-focus");
469         }
470     },
471     // private
472     onBlur : function(e){
473         this.el.removeClass("x-btn-focus");
474     },
475     // private
476     onMouseDown : function(e){
477         if(!this.disabled && e.button == 0){
478             this.el.addClass("x-btn-click");
479             Roo.get(document).on('mouseup', this.onMouseUp, this);
480         }
481     },
482     // private
483     onMouseUp : function(e){
484         if(e.button == 0){
485             this.el.removeClass("x-btn-click");
486             Roo.get(document).un('mouseup', this.onMouseUp, this);
487         }
488     },
489     // private
490     onMenuShow : function(e){
491         this.el.addClass("x-btn-menu-active");
492     },
493     // private
494     onMenuHide : function(e){
495         this.el.removeClass("x-btn-menu-active");
496     }   
497 });
498
499 // Private utility class used by Button
500 Roo.ButtonToggleMgr = function(){
501    var groups = {};
502    
503    function toggleGroup(btn, state){
504        if(state){
505            var g = groups[btn.toggleGroup];
506            for(var i = 0, l = g.length; i < l; i++){
507                if(g[i] != btn){
508                    g[i].toggle(false);
509                }
510            }
511        }
512    }
513    
514    return {
515        register : function(btn){
516            if(!btn.toggleGroup){
517                return;
518            }
519            var g = groups[btn.toggleGroup];
520            if(!g){
521                g = groups[btn.toggleGroup] = [];
522            }
523            g.push(btn);
524            btn.on("toggle", toggleGroup);
525        },
526        
527        unregister : function(btn){
528            if(!btn.toggleGroup){
529                return;
530            }
531            var g = groups[btn.toggleGroup];
532            if(g){
533                g.remove(btn);
534                btn.un("toggle", toggleGroup);
535            }
536        }
537    };
538 }();