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