Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / docs / source / Button.html
1 <html>
2 <head>
3   <title>The source code</title>
4     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
5     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
6 </head>
7 <body  onload="prettyPrint();">
8     <pre class="prettyprint lang-js">/*!
9  * Ext JS Library 3.0.3
10  * Copyright(c) 2006-2009 Ext JS, LLC
11  * licensing@extjs.com
12  * http://www.extjs.com/license
13  */
14 <div id="cls-Ext.Button"></div>/**
15  * @class Ext.Button
16  * @extends Ext.BoxComponent
17  * Simple Button class
18  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
19  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
20  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
21  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
22  * The handler is passed the following parameters:<div class="mdetail-params"><ul>
23  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
24  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
25  * </ul></div>
26  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
27  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
28  * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object
29  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30  * @cfg {Boolean} disabled True to start disabled (defaults to false)
31  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
32  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
33  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
34  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
35  * @constructor
36  * Create a new button
37  * @param {Object} config The config object
38  * @xtype button
39  */
40 Ext.Button = Ext.extend(Ext.BoxComponent, {
41     /**
42      * Read-only. True if this button is hidden
43      * @type Boolean
44      */
45     hidden : false,
46     /**
47      * Read-only. True if this button is disabled
48      * @type Boolean
49      */
50     disabled : false,
51     /**
52      * Read-only. True if this button is pressed (only if enableToggle = true)
53      * @type Boolean
54      */
55     pressed : false,
56
57     /**
58      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
59      */
60
61     /**
62      * @cfg {Boolean} allowDepress
63      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
64      */
65
66     /**
67      * @cfg {Boolean} enableToggle
68      * True to enable pressed/not pressed toggling (defaults to false)
69      */
70     enableToggle : false,
71     /**
72      * @cfg {Function} toggleHandler
73      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
74      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
75      * <li><b>state</b> : Boolean<div class="sub-desc">The next state if the Button, true means pressed.</div></li>
76      * </ul>
77      */
78     /**
79      * @cfg {Mixed} menu
80      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
81      */
82     /**
83      * @cfg {String} menuAlign
84      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
85      */
86     menuAlign : 'tl-bl?',
87
88     /**
89      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
90      * text to be used if this item is shown in the overflow menu. See also
91      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
92      */
93     /**
94      * @cfg {String} iconCls
95      * A css class which sets a background image to be used as the icon for this button
96      */
97     /**
98      * @cfg {String} type
99      * submit, reset or button - defaults to 'button'
100      */
101     type : 'button',
102
103     // private
104     menuClassTarget : 'tr:nth(2)',
105
106     /**
107      * @cfg {String} clickEvent
108      * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu). 
109      * Defaults to <tt>'click'</tt>.
110      */
111     clickEvent : 'click',
112
113     /**
114      * @cfg {Boolean} handleMouseEvents
115      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
116      */
117     handleMouseEvents : true,
118
119     /**
120      * @cfg {String} tooltipType
121      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
122      */
123     tooltipType : 'qtip',
124
125     /**
126      * @cfg {String} buttonSelector
127      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
128      * DOM structure created.</p>
129      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
130      * a focussable element.</p>
131      * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
132      */
133     buttonSelector : 'button:first-child',
134
135     /**
136      * @cfg {String} scale
137      * <p>(Optional) The size of the Button. Three values are allowed:</p>
138      * <ul class="mdetail-params">
139      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
140      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
141      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
142      * </ul>
143      * <p>Defaults to <b><tt>'small'</tt></b>.</p>
144      */
145     scale : 'small',
146
147     /**
148      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
149      * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
150      * executed. Defaults to this Button.
151      */
152
153     /**
154      * @cfg {String} iconAlign
155      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
156      * <ul class="mdetail-params">
157      * <li>'top'<div class="sub-desc"></div></li>
158      * <li>'right'<div class="sub-desc"></div></li>
159      * <li>'bottom'<div class="sub-desc"></div></li>
160      * <li>'left'<div class="sub-desc"></div></li>
161      * </ul>
162      * <p>Defaults to <b><tt>'left'</tt></b>.</p>
163      */
164     iconAlign : 'left',
165
166     /**
167      * @cfg {String} arrowAlign
168      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
169      * Two values are allowed:</p>
170      * <ul class="mdetail-params">
171      * <li>'right'<div class="sub-desc"></div></li>
172      * <li>'bottom'<div class="sub-desc"></div></li>
173      * </ul>
174      * <p>Defaults to <b><tt>'right'</tt></b>.</p>
175      */
176     arrowAlign : 'right',
177
178     /**
179      * @cfg {Ext.Template} template (Optional)
180      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
181      * Instances, or subclasses which need a different DOM structure may provide a different
182      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
183      * @type Ext.Template
184      * @property template
185      */
186     /**
187      * @cfg {String} cls
188      * A CSS class string to apply to the button's main element.
189      */
190     /**
191      * @property menu
192      * @type Menu
193      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
194      */
195
196     initComponent : function(){
197         Ext.Button.superclass.initComponent.call(this);
198
199         this.addEvents(
200             /**
201              * @event click
202              * Fires when this button is clicked
203              * @param {Button} this
204              * @param {EventObject} e The click event
205              */
206             'click',
207             /**
208              * @event toggle
209              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
210              * @param {Button} this
211              * @param {Boolean} pressed
212              */
213             'toggle',
214             /**
215              * @event mouseover
216              * Fires when the mouse hovers over the button
217              * @param {Button} this
218              * @param {Event} e The event object
219              */
220             'mouseover',
221             /**
222              * @event mouseout
223              * Fires when the mouse exits the button
224              * @param {Button} this
225              * @param {Event} e The event object
226              */
227             'mouseout',
228             /**
229              * @event menushow
230              * If this button has a menu, this event fires when it is shown
231              * @param {Button} this
232              * @param {Menu} menu
233              */
234             'menushow',
235             /**
236              * @event menuhide
237              * If this button has a menu, this event fires when it is hidden
238              * @param {Button} this
239              * @param {Menu} menu
240              */
241             'menuhide',
242             /**
243              * @event menutriggerover
244              * If this button has a menu, this event fires when the mouse enters the menu triggering element
245              * @param {Button} this
246              * @param {Menu} menu
247              * @param {EventObject} e
248              */
249             'menutriggerover',
250             /**
251              * @event menutriggerout
252              * If this button has a menu, this event fires when the mouse leaves the menu triggering element
253              * @param {Button} this
254              * @param {Menu} menu
255              * @param {EventObject} e
256              */
257             'menutriggerout'
258         );
259         if(this.menu){
260             this.menu = Ext.menu.MenuMgr.get(this.menu);
261         }
262         if(Ext.isString(this.toggleGroup)){
263             this.enableToggle = true;
264         }
265     },
266
267 /**
268   * <p>This method returns an object which provides substitution parameters for the {@link #template Template} used
269   * to create this Button's DOM structure.</p>
270   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
271   * own implementation of this method.</p>
272   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
273   * following items:</p><div class="mdetail-params"><ul>
274   * <li>The Button's {@link #text}</li>
275   * <li>The &lt;button&gt;'s {@link #type}</li>
276   * <li>The {@link iconCls} applied to the &lt;button&gt; {@link #btnEl element}</li>
277   * <li>The {@link #cls} applied to the Button's main {@link #getEl Element}</li>
278   * <li>A CSS class name controlling the Button's {@link #scale} and {@link #iconAlign icon alignment}</li>
279   * <li>A CSS class name which applies an arrow to the Button if configured with a {@link #menu}</li>
280   * </ul></div>
281   * @return {Object} Substitution data for a Template.
282  */
283     getTemplateArgs : function(){
284         var cls = (this.cls || '');
285         cls += (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon';
286         if(this.pressed){
287             cls += ' x-btn-pressed';
288         }
289         return [this.text || '&#160;', this.type, this.iconCls || '', cls, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass()];
290     },
291
292     // protected
293     getMenuClass : function(){
294         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
295     },
296
297     // private
298     onRender : function(ct, position){
299         if(!this.template){
300             if(!Ext.Button.buttonTemplate){
301                 // hideous table template
302                 Ext.Button.buttonTemplate = new Ext.Template(
303                     '<table cellspacing="0" class="x-btn {3}"><tbody class="{4}">',
304                     '<tr><td class="x-btn-tl"><i>&#160;</i></td><td class="x-btn-tc"></td><td class="x-btn-tr"><i>&#160;</i></td></tr>',
305                     '<tr><td class="x-btn-ml"><i>&#160;</i></td><td class="x-btn-mc"><em class="{5}" unselectable="on"><button class="x-btn-text {2}" type="{1}">{0}</button></em></td><td class="x-btn-mr"><i>&#160;</i></td></tr>',
306                     '<tr><td class="x-btn-bl"><i>&#160;</i></td><td class="x-btn-bc"></td><td class="x-btn-br"><i>&#160;</i></td></tr>',
307                     "</tbody></table>");
308                 Ext.Button.buttonTemplate.compile();
309             }
310             this.template = Ext.Button.buttonTemplate;
311         }
312
313         var btn, targs = this.getTemplateArgs();
314
315         if(position){
316             btn = this.template.insertBefore(position, targs, true);
317         }else{
318             btn = this.template.append(ct, targs, true);
319         }
320         /**
321          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
322          * this references a <tt>&lt;button&gt;</tt> element. Read only.
323          * @type Ext.Element
324          * @property btnEl
325          */
326         this.btnEl = btn.child(this.buttonSelector);
327         this.mon(this.btnEl, {
328             scope: this,
329             focus: this.onFocus,
330             blur: this.onBlur
331         });
332
333         this.initButtonEl(btn, this.btnEl);
334
335         Ext.ButtonToggleMgr.register(this);
336     },
337
338     // private
339     initButtonEl : function(btn, btnEl){
340         this.el = btn;
341
342         if(this.id){
343             var d = this.el.dom,
344                 c = Ext.Element.cache;
345                 
346             delete c[d.id];
347             d.id = this.el.id = this.id;
348             c[d.id] = this.el;
349         }
350         if(this.icon){
351             btnEl.setStyle('background-image', 'url(' +this.icon +')');
352         }
353         if(this.tabIndex !== undefined){
354             btnEl.dom.tabIndex = this.tabIndex;
355         }
356         if(this.tooltip){
357             this.setTooltip(this.tooltip, true);
358         }
359
360         if(this.handleMouseEvents){
361             this.mon(btn, {
362                 scope: this,
363                 mouseover: this.onMouseOver,
364                 mousedown: this.onMouseDown
365             });
366
367             // new functionality for monitoring on the document level
368             //this.mon(btn, 'mouseout', this.onMouseOut, this);
369         }
370
371         if(this.menu){
372             this.mon(this.menu, {
373                 scope: this,
374                 show: this.onMenuShow,
375                 hide: this.onMenuHide
376             });
377         }
378
379         if(this.repeat){
380             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
381             this.mon(repeater, 'click', this.onClick, this);
382         }
383         this.mon(btn, this.clickEvent, this.onClick, this);
384     },
385
386     // private
387     afterRender : function(){
388         Ext.Button.superclass.afterRender.call(this);
389         this.doAutoWidth();
390     },
391
392     /**
393      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
394      * the value of the {@link iconCls} config internally.
395      * @param {String} cls The CSS class providing the icon image
396      * @return {Ext.Button} this
397      */
398     setIconClass : function(cls){
399         if(this.el){
400             this.btnEl.replaceClass(this.iconCls, cls);
401         }
402         this.iconCls = cls;
403         return this;
404     },
405
406     /**
407      * Sets the tooltip for this Button.
408      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
409      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
410      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
411      * </ul></div>
412      * @return {Ext.Button} this
413      */
414     setTooltip : function(tooltip, /* private */ initial){
415         if(this.rendered){
416             if(!initial){
417                 this.clearTip();
418             }
419             if(Ext.isObject(tooltip)){
420                 Ext.QuickTips.register(Ext.apply({
421                       target: this.btnEl.id
422                 }, tooltip));
423                 this.tooltip = tooltip;
424             }else{
425                 this.btnEl.dom[this.tooltipType] = tooltip;
426             }
427         }else{
428             this.tooltip = tooltip;
429         }
430         return this;
431     },
432
433     // private
434     clearTip : function(){
435         if(Ext.isObject(this.tooltip)){
436             Ext.QuickTips.unregister(this.btnEl);
437         }
438     },
439
440     // private
441     beforeDestroy : function(){
442         if(this.rendered){
443             this.clearTip();
444         }
445         Ext.destroy(this.menu, this.repeater);
446     },
447
448     // private
449     onDestroy : function(){
450         var doc = Ext.getDoc();
451         doc.un('mouseover', this.monitorMouseOver, this);
452         doc.un('mouseup', this.onMouseUp, this);
453         if(this.rendered){
454             Ext.ButtonToggleMgr.unregister(this);
455         }
456     },
457
458     // private
459     doAutoWidth : function(){
460         if(this.el && this.text && this.width === undefined){
461             this.el.setWidth('auto');
462             if(Ext.isIE7 && Ext.isStrict){
463                 var ib = this.btnEl;
464                 if(ib && ib.getWidth() > 20){
465                     ib.clip();
466                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
467                 }
468             }
469             if(this.minWidth){
470                 if(this.el.getWidth() < this.minWidth){
471                     this.el.setWidth(this.minWidth);
472                 }
473             }
474         }
475     },
476
477     /**
478      * Assigns this Button's click handler
479      * @param {Function} handler The function to call when the button is clicked
480      * @param {Object} scope (optional) Scope for the function passed in. Defaults to this Button.
481      * @return {Ext.Button} this
482      */
483     setHandler : function(handler, scope){
484         this.handler = handler;
485         this.scope = scope;
486         return this;
487     },
488
489     /**
490      * Sets this Button's text
491      * @param {String} text The button text
492      * @return {Ext.Button} this
493      */
494     setText : function(text){
495         this.text = text;
496         if(this.el){
497             this.el.child('td.x-btn-mc ' + this.buttonSelector).update(text);
498         }
499         this.doAutoWidth();
500         return this;
501     },
502
503     /**
504      * Gets the text for this Button
505      * @return {String} The button text
506      */
507     getText : function(){
508         return this.text;
509     },
510
511     /**
512      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
513      * @param {Boolean} state (optional) Force a particular state
514      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
515      * @return {Ext.Button} this
516      */
517     toggle : function(state, suppressEvent){
518         state = state === undefined ? !this.pressed : !!state;
519         if(state != this.pressed){
520             if(this.rendered){
521                 this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
522             }
523             this.pressed = state;
524             if(!suppressEvent){
525                 this.fireEvent('toggle', this, state);
526                 if(this.toggleHandler){
527                     this.toggleHandler.call(this.scope || this, this, state);
528                 }
529             }
530         }
531         return this;
532     },
533
534     /**
535      * Focus the button
536      */
537     focus : function(){
538         this.btnEl.focus();
539     },
540
541     // private
542     onDisable : function(){
543         this.onDisableChange(true);
544     },
545
546     // private
547     onEnable : function(){
548         this.onDisableChange(false);
549     },
550
551     onDisableChange : function(disabled){
552         if(this.el){
553             if(!Ext.isIE6 || !this.text){
554                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
555             }
556             this.el.dom.disabled = disabled;
557         }
558         this.disabled = disabled;
559     },
560
561     /**
562      * Show this button's menu (if it has one)
563      */
564     showMenu : function(){
565         if(this.rendered && this.menu){
566             if(this.tooltip){
567                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
568             }
569             this.menu.show(this.el, this.menuAlign);
570         }
571         return this;
572     },
573
574     /**
575      * Hide this button's menu (if it has one)
576      */
577     hideMenu : function(){
578         if(this.menu){
579             this.menu.hide();
580         }
581         return this;
582     },
583
584     /**
585      * Returns true if the button has a menu and it is visible
586      * @return {Boolean}
587      */
588     hasVisibleMenu : function(){
589         return this.menu && this.menu.isVisible();
590     },
591
592     // private
593     onClick : function(e){
594         if(e){
595             e.preventDefault();
596         }
597         if(e.button !== 0){
598             return;
599         }
600         if(!this.disabled){
601             if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){
602                 this.toggle();
603             }
604             if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
605                 this.showMenu();
606             }
607             this.fireEvent('click', this, e);
608             if(this.handler){
609                 //this.el.removeClass('x-btn-over');
610                 this.handler.call(this.scope || this, this, e);
611             }
612         }
613     },
614
615     // private
616     isMenuTriggerOver : function(e, internal){
617         return this.menu && !internal;
618     },
619
620     // private
621     isMenuTriggerOut : function(e, internal){
622         return this.menu && !internal;
623     },
624
625     // private
626     onMouseOver : function(e){
627         if(!this.disabled){
628             var internal = e.within(this.el,  true);
629             if(!internal){
630                 this.el.addClass('x-btn-over');
631                 if(!this.monitoringMouseOver){
632                     Ext.getDoc().on('mouseover', this.monitorMouseOver, this);
633                     this.monitoringMouseOver = true;
634                 }
635                 this.fireEvent('mouseover', this, e);
636             }
637             if(this.isMenuTriggerOver(e, internal)){
638                 this.fireEvent('menutriggerover', this, this.menu, e);
639             }
640         }
641     },
642
643     // private
644     monitorMouseOver : function(e){
645         if(e.target != this.el.dom && !e.within(this.el)){
646             if(this.monitoringMouseOver){
647                 Ext.getDoc().un('mouseover', this.monitorMouseOver, this);
648                 this.monitoringMouseOver = false;
649             }
650             this.onMouseOut(e);
651         }
652     },
653
654     // private
655     onMouseOut : function(e){
656         var internal = e.within(this.el) && e.target != this.el.dom;
657         this.el.removeClass('x-btn-over');
658         this.fireEvent('mouseout', this, e);
659         if(this.isMenuTriggerOut(e, internal)){
660             this.fireEvent('menutriggerout', this, this.menu, e);
661         }
662     },
663     // private
664     onFocus : function(e){
665         if(!this.disabled){
666             this.el.addClass('x-btn-focus');
667         }
668     },
669     // private
670     onBlur : function(e){
671         this.el.removeClass('x-btn-focus');
672     },
673
674     // private
675     getClickEl : function(e, isUp){
676        return this.el;
677     },
678
679     // private
680     onMouseDown : function(e){
681         if(!this.disabled && e.button === 0){
682             this.getClickEl(e).addClass('x-btn-click');
683             Ext.getDoc().on('mouseup', this.onMouseUp, this);
684         }
685     },
686     // private
687     onMouseUp : function(e){
688         if(e.button === 0){
689             this.getClickEl(e, true).removeClass('x-btn-click');
690             Ext.getDoc().un('mouseup', this.onMouseUp, this);
691         }
692     },
693     // private
694     onMenuShow : function(e){
695         this.ignoreNextClick = 0;
696         this.el.addClass('x-btn-menu-active');
697         this.fireEvent('menushow', this, this.menu);
698     },
699     // private
700     onMenuHide : function(e){
701         this.el.removeClass('x-btn-menu-active');
702         this.ignoreNextClick = this.restoreClick.defer(250, this);
703         this.fireEvent('menuhide', this, this.menu);
704     },
705
706     // private
707     restoreClick : function(){
708         this.ignoreNextClick = 0;
709     }
710
711
712
713     /**
714      * @cfg {String} autoEl @hide
715      */
716 });
717 Ext.reg('button', Ext.Button);
718
719 // Private utility class used by Button
720 Ext.ButtonToggleMgr = function(){
721    var groups = {};
722
723    function toggleGroup(btn, state){
724        if(state){
725            var g = groups[btn.toggleGroup];
726            for(var i = 0, l = g.length; i < l; i++){
727                if(g[i] != btn){
728                    g[i].toggle(false);
729                }
730            }
731        }
732    }
733
734    return {
735        register : function(btn){
736            if(!btn.toggleGroup){
737                return;
738            }
739            var g = groups[btn.toggleGroup];
740            if(!g){
741                g = groups[btn.toggleGroup] = [];
742            }
743            g.push(btn);
744            btn.on('toggle', toggleGroup);
745        },
746
747        unregister : function(btn){
748            if(!btn.toggleGroup){
749                return;
750            }
751            var g = groups[btn.toggleGroup];
752            if(g){
753                g.remove(btn);
754                btn.un('toggle', toggleGroup);
755            }
756        },
757
758        /**
759         * Gets the pressed button in the passed group or null
760         * @param {String} group
761         * @return Button
762         */
763        getPressed : function(group){
764            var g = groups[group];
765            if(g){
766                for(var i = 0, len = g.length; i < len; i++){
767                    if(g[i].pressed === true){
768                        return g[i];
769                    }
770                }
771            }
772            return null;
773        }
774    };
775 }();</pre>
776 </body>
777 </html>