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