3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @docauthor Robert Dougan <rob@sencha.com>
18 * Create simple buttons with this component. Customisations include {@link #iconAlign aligned}
19 * {@link #iconCls icons}, {@link #menu dropdown menus}, {@link #tooltip tooltips}
20 * and {@link #scale sizing options}. Specify a {@link #handler handler} to run code when
21 * a user clicks the button, or use {@link #listeners listeners} for other events such as
22 * {@link #mouseover mouseover}. Example usage:
25 * Ext.create('Ext.Button', {
27 * renderTo: Ext.getBody(),
28 * handler: function() {
29 * alert('You clicked the button!')
33 * The {@link #handler} configuration can also be updated dynamically using the {@link #setHandler}
34 * method. Example usage:
37 * Ext.create('Ext.Button', {
38 * text : 'Dynamic Handler Button',
39 * renderTo: Ext.getBody(),
40 * handler : function() {
41 * // this button will spit out a different number every time you click it.
42 * // so firstly we must check if that number is already set:
43 * if (this.clickCount) {
44 * // looks like the property is already set, so lets just add 1 to that number and alert the user
46 * alert('You have clicked the button "' + this.clickCount + '" times.\n\nTry clicking it again..');
48 * // if the clickCount property is not set, we will set it and alert the user
49 * this.clickCount = 1;
50 * alert('You just clicked the button for the first time!\n\nTry pressing it again..');
55 * A button within a container:
58 * Ext.create('Ext.Container', {
59 * renderTo: Ext.getBody(),
68 * A useful option of Button is the {@link #scale} configuration. This configuration has three different options:
77 * Ext.create('Ext.Button', {
78 * renderTo: document.body,
83 * Buttons can also be toggled. To enable this, you simple set the {@link #enableToggle} property to `true`.
87 * Ext.create('Ext.Button', {
88 * renderTo: Ext.getBody(),
93 * You can assign a menu to a button by using the {@link #menu} configuration. This standard configuration
94 * can either be a reference to a {@link Ext.menu.Menu menu} object, a {@link Ext.menu.Menu menu} id or a
95 * {@link Ext.menu.Menu menu} config blob. When assigning a menu to a button, an arrow is automatically
96 * added to the button. You can change the alignment of the arrow using the {@link #arrowAlign} configuration
97 * on button. Example usage:
100 * Ext.create('Ext.Button', {
101 * text : 'Menu button',
102 * renderTo : Ext.getBody(),
103 * arrowAlign: 'bottom',
112 * Using listeners, you can easily listen to events fired by any component, using the {@link #listeners}
113 * configuration or using the {@link #addListener} method. Button has a variety of different listeners:
121 * - `menutriggerover`
127 * Ext.create('Ext.Button', {
129 * renderTo : Ext.getBody(),
131 * click: function() {
132 * // this == the button, as we are in the local scope
133 * this.setText('I was clicked!');
135 * mouseover: function() {
136 * // set a new config which says we moused over, if not already set
137 * if (!this.mousedOver) {
138 * this.mousedOver = true;
139 * alert('You moused over a button!\n\nI wont do this again.');
145 Ext.define('Ext.button.Button', {
147 /* Begin Definitions */
148 alias: 'widget.button',
149 extend: 'Ext.Component',
153 'Ext.util.ClickRepeater',
154 'Ext.layout.component.Button',
155 'Ext.util.TextMetrics',
159 alternateClassName: 'Ext.Button',
160 /* End Definitions */
163 componentLayout: 'button',
166 * @property {Boolean} hidden
167 * True if this button is hidden. Read-only.
172 * @property {Boolean} disabled
173 * True if this button is disabled. Read-only.
178 * @property {Boolean} pressed
179 * True if this button is pressed (only if enableToggle = true). Read-only.
185 * The button text to be used as innerHTML (html tags are accepted).
190 * The path to an image to display in the button (the image will be set as the background-image CSS property of the
191 * button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
195 * @cfg {Function} handler
196 * A function called when the button is clicked (can be used instead of click event).
197 * @cfg {Ext.button.Button} handler.button This button.
198 * @cfg {Ext.EventObject} handler.e The click event.
202 * @cfg {Number} minWidth
203 * The minimum width for this button (used to give a set of buttons a common width).
204 * See also {@link Ext.panel.Panel}.{@link Ext.panel.Panel#minButtonWidth minButtonWidth}.
208 * @cfg {String/Object} tooltip
209 * The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or
210 * QuickTips config object.
214 * @cfg {Boolean} [hidden=false]
215 * True to start hidden.
219 * @cfg {Boolean} [disabled=true]
220 * True to start disabled.
224 * @cfg {Boolean} [pressed=false]
225 * True to start pressed (only if enableToggle = true)
229 * @cfg {String} toggleGroup
230 * The group this toggle button is a member of (only 1 per group can be pressed)
234 * @cfg {Boolean/Object} [repeat=false]
235 * True to repeat fire the click event while the mouse is down. This can also be a
236 * {@link Ext.util.ClickRepeater ClickRepeater} config object.
240 * @cfg {Number} tabIndex
241 * Set a DOM tabIndex for this button.
245 * @cfg {Boolean} [allowDepress=true]
246 * False to not allow a pressed Button to be depressed. Only valid when {@link #enableToggle} is true.
250 * @cfg {Boolean} [enableToggle=false]
251 * True to enable pressed/not pressed toggling.
256 * @cfg {Function} toggleHandler
257 * Function called when a Button with {@link #enableToggle} set to true is clicked.
258 * @cfg {Ext.button.Button} toggleHandler.button This button.
259 * @cfg {Boolean} toggleHandler.state The next state of the Button, true means pressed.
263 * @cfg {Ext.menu.Menu/String/Object} menu
264 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob.
268 * @cfg {String} menuAlign
269 * The position to align the menu to (see {@link Ext.Element#alignTo} for more details).
274 * @cfg {String} textAlign
275 * The text alignment for this button (center, left, right).
280 * @cfg {String} overflowText
281 * If used in a {@link Ext.toolbar.Toolbar Toolbar}, the text to be used if this item is shown in the overflow menu.
282 * See also {@link Ext.toolbar.Item}.`{@link Ext.toolbar.Item#overflowText overflowText}`.
286 * @cfg {String} iconCls
287 * A css class which sets a background image to be used as the icon for this button.
292 * The type of `<input>` to create: submit, reset or button.
297 * @cfg {String} clickEvent
298 * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
303 * @cfg {Boolean} preventDefault
304 * True to prevent the default action when the {@link #clickEvent} is processed.
306 preventDefault: true,
309 * @cfg {Boolean} handleMouseEvents
310 * False to disable visual cues on mouseover, mouseout and mousedown.
312 handleMouseEvents: true,
315 * @cfg {String} tooltipType
316 * The type of tooltip to use. Either 'qtip' for QuickTips or 'title' for title attribute.
321 * @cfg {String} [baseCls='x-btn']
322 * The base CSS class to add to all buttons.
324 baseCls: Ext.baseCSSPrefix + 'btn',
327 * @cfg {String} pressedCls
328 * The CSS class to add to a button when it is in the pressed state.
330 pressedCls: 'pressed',
333 * @cfg {String} overCls
334 * The CSS class to add to a button when it is in the over (hovered) state.
339 * @cfg {String} focusCls
340 * The CSS class to add to a button when it is in the focussed state.
345 * @cfg {String} menuActiveCls
346 * The CSS class to add to a button when it's menu is active.
348 menuActiveCls: 'menu-active',
352 * The URL to visit when the button is clicked. Specifying this config is equivalent to specifying:
354 * handler: function() { window.location = "http://www.sencha.com" }
358 * @cfg {Object} baseParams
359 * An object literal of parameters to pass to the url when the {@link #href} property is specified.
363 * @cfg {Object} params
364 * An object literal of parameters to pass to the url when the {@link #href} property is specified. Any params
365 * override {@link #baseParams}. New params can be set using the {@link #setParams} method.
372 '<em id="{id}-btnWrap" class="{splitCls}">' +
374 '<a id="{id}-btnEl" href="{href}" target="{target}"<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="link">' +
375 '<span id="{id}-btnInnerEl" class="{baseCls}-inner">' +
378 '<span id="{id}-btnIconEl" class="{baseCls}-icon"></span>' +
382 '<button id="{id}-btnEl" type="{type}" hidefocus="true"' +
383 // the autocomplete="off" is required to prevent Firefox from remembering
384 // the button's disabled state between page reloads.
385 '<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="button" autocomplete="off">' +
386 '<span id="{id}-btnInnerEl" class="{baseCls}-inner" style="{innerSpanStyle}">' +
389 '<span id="{id}-btnIconEl" class="{baseCls}-icon {iconCls}"> </span>' +
395 * @cfg {String} scale
396 * The size of the Button. Three values are allowed:
398 * - 'small' - Results in the button element being 16px high.
399 * - 'medium' - Results in the button element being 24px high.
400 * - 'large' - Results in the button element being 32px high.
406 * An array of allowed scales.
408 allowedScales: ['small', 'medium', 'large'],
411 * @cfg {Object} scope
412 * The scope (**this** reference) in which the `{@link #handler}` and `{@link #toggleHandler}` is executed.
413 * Defaults to this Button.
417 * @cfg {String} iconAlign
418 * The side of the Button box to render the icon. Four values are allowed:
428 * @cfg {String} arrowAlign
429 * The side of the Button box to render the arrow if the button has an associated {@link #menu}. Two
430 * values are allowed:
438 * @cfg {String} arrowCls
439 * The className used for the inner arrow element if the button has a menu.
444 * @property {Ext.Template} template
445 * A {@link Ext.Template Template} used to create the Button's DOM structure.
447 * Instances, or subclasses which need a different DOM structure may provide a different template layout in
448 * conjunction with an implementation of {@link #getTemplateArgs}.
453 * A CSS class string to apply to the button's main element.
457 * @property {Ext.menu.Menu} menu
458 * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config
463 * @cfg {Boolean} autoWidth
464 * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content. If
465 * the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent the button
466 * from doing this automatic sizing.
469 maskOnDisable: false,
472 initComponent: function() {
474 me.callParent(arguments);
479 * Fires when this button is clicked
480 * @param {Ext.button.Button} this
481 * @param {Event} e The click event
487 * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
488 * @param {Ext.button.Button} this
489 * @param {Boolean} pressed
495 * Fires when the mouse hovers over the button
496 * @param {Ext.button.Button} this
497 * @param {Event} e The event object
503 * Fires when the mouse exits the button
504 * @param {Ext.button.Button} this
505 * @param {Event} e The event object
511 * If this button has a menu, this event fires when it is shown
512 * @param {Ext.button.Button} this
513 * @param {Ext.menu.Menu} menu
519 * If this button has a menu, this event fires when it is hidden
520 * @param {Ext.button.Button} this
521 * @param {Ext.menu.Menu} menu
526 * @event menutriggerover
527 * If this button has a menu, this event fires when the mouse enters the menu triggering element
528 * @param {Ext.button.Button} this
529 * @param {Ext.menu.Menu} menu
535 * @event menutriggerout
536 * If this button has a menu, this event fires when the mouse leaves the menu triggering element
537 * @param {Ext.button.Button} this
538 * @param {Ext.menu.Menu} menu
545 // Flag that we'll have a splitCls
548 // retrieve menu by id or instantiate instance if needed
549 me.menu = Ext.menu.Manager.get(me.menu);
550 me.menu.ownerCt = me;
553 // Accept url as a synonym for href
558 // preventDefault defaults to false for links
559 if (me.href && !me.hasOwnProperty('preventDefault')) {
560 me.preventDefault = false;
563 if (Ext.isString(me.toggleGroup)) {
564 me.enableToggle = true;
570 initAria: function() {
572 var actionEl = this.getActionEl();
574 actionEl.dom.setAttribute('aria-haspopup', true);
579 getActionEl: function() {
584 getFocusEl: function() {
589 setButtonCls: function() {
592 btnIconEl = me.btnIconEl,
593 hide = 'x-hide-display';
595 if (me.useSetClass) {
596 if (!Ext.isEmpty(me.oldCls)) {
597 me.removeClsWithUI(me.oldCls);
598 me.removeClsWithUI(me.pressedCls);
601 // Check whether the button has an icon or not, and if it has an icon, what is th alignment
602 if (me.iconCls || me.icon) {
604 cls.push('icon-text-' + me.iconAlign);
609 btnIconEl.removeCls(hide);
616 btnIconEl.addCls(hide);
621 me.addClsWithUI(cls);
622 me.addClsWithUI(me.pressed ? me.pressedCls : null);
627 onRender: function(ct, position) {
628 // classNames for the button
632 // Apply the renderData to the template args
633 Ext.applyIf(me.renderData, me.getTemplateArgs());
635 me.addChildEls('btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl');
638 me.ui = me.ui + '-' + me.scale;
641 // Render internal structure
642 me.callParent(arguments);
644 // If it is a split button + has a toolip for the arrow
645 if (me.split && me.arrowTooltip) {
646 me.arrowEl.dom.setAttribute(me.getTipAttr(), me.arrowTooltip);
649 // Add listeners to the focus and blur events on the element
656 // Set btn as a local variable for easy access
664 me.setIconCls(me.iconCls);
668 me.setTooltip(me.tooltip, true);
672 me.setTextAlign(me.textAlign);
675 // Add the mouse events to the button
676 if (me.handleMouseEvents) {
679 mouseover: me.onMouseOver,
680 mouseout: me.onMouseOut,
681 mousedown: me.onMouseDown
686 mousemove: me.onMouseMove,
692 // Check if the button has a menu
700 me.keyMap = Ext.create('Ext.util.KeyMap', me.el, {
701 key: Ext.EventObject.DOWN,
702 handler: me.onDownKey,
707 // Check if it is a repeat button
709 repeater = Ext.create('Ext.util.ClickRepeater', btn, Ext.isObject(me.repeat) ? me.repeat: {});
710 me.mon(repeater, 'click', me.onRepeatClick, me);
712 me.mon(btn, me.clickEvent, me.onClick, me);
715 // Register the button in the toggle manager
716 Ext.ButtonToggleManager.register(me);
720 * This method returns an object which provides substitution parameters for the {@link #renderTpl XTemplate} used to
721 * create this Button's DOM structure.
723 * Instances or subclasses which use a different Template to create a different DOM structure may need to provide
724 * their own implementation of this method.
726 * @return {Object} Substitution data for a Template. The default implementation which provides data for the default
727 * {@link #template} returns an Object containing the following properties:
728 * @return {String} return.type The `<button>`'s {@link #type}
729 * @return {String} return.splitCls A CSS class to determine the presence and position of an arrow icon.
730 * (`'x-btn-arrow'` or `'x-btn-arrow-bottom'` or `''`)
731 * @return {String} return.cls A CSS class name applied to the Button's main `<tbody>` element which determines the
732 * button's scale and icon alignment.
733 * @return {String} return.text The {@link #text} to display ion the Button.
734 * @return {Number} return.tabIndex The tab index within the input flow.
736 getTemplateArgs: function() {
738 persistentPadding = me.getPersistentBtnPadding(),
741 // Create negative margin offsets to counteract persistent button padding if needed
742 if (Math.max.apply(Math, persistentPadding) > 0) {
743 innerSpanStyle = 'margin:' + Ext.Array.map(persistentPadding, function(pad) {
750 target : me.target || '_blank',
752 splitCls : me.getSplitCls(),
754 iconCls : me.iconCls || '',
755 text : me.text || ' ',
756 tabIndex : me.tabIndex,
757 innerSpanStyle: innerSpanStyle
763 * If there is a configured href for this Button, returns the href with parameters appended.
764 * @returns The href string with parameters appended.
766 getHref: function() {
768 params = Ext.apply({}, me.baseParams);
770 // write baseParams first, then write any params
771 params = Ext.apply(params, me.params);
772 return me.href ? Ext.urlAppend(me.href, Ext.Object.toQueryString(params)) : false;
776 * Sets the href of the link dynamically according to the params passed, and any {@link #baseParams} configured.
778 * **Only valid if the Button was originally configured with a {@link #href}**
780 * @param {Object} params Parameters to use in the href URL.
782 setParams: function(params) {
783 this.params = params;
784 this.btnEl.dom.href = this.getHref();
787 getSplitCls: function() {
789 return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
793 afterRender: function() {
795 me.useSetClass = true;
797 me.doc = Ext.getDoc();
798 this.callParent(arguments);
802 * Sets the CSS class that provides a background image to use as the button's icon. This method also changes the
803 * value of the {@link #iconCls} config internally.
804 * @param {String} cls The CSS class providing the icon image
805 * @return {Ext.button.Button} this
807 setIconCls: function(cls) {
809 btnIconEl = me.btnIconEl,
814 // Remove the previous iconCls from the button
815 btnIconEl.removeCls(oldCls);
816 btnIconEl.addCls(cls || '');
823 * Sets the tooltip for this Button.
825 * @param {String/Object} tooltip This may be:
827 * - **String** : A string to be used as innerHTML (html tags are accepted) to show in a tooltip
828 * - **Object** : A configuration object for {@link Ext.tip.QuickTipManager#register}.
830 * @return {Ext.button.Button} this
832 setTooltip: function(tooltip, initial) {
839 if (Ext.isObject(tooltip)) {
840 Ext.tip.QuickTipManager.register(Ext.apply({
844 me.tooltip = tooltip;
846 me.btnEl.dom.setAttribute(me.getTipAttr(), tooltip);
849 me.tooltip = tooltip;
855 * Sets the text alignment for this button.
856 * @param {String} align The new alignment of the button text. See {@link #textAlign}.
858 setTextAlign: function(align) {
863 btnEl.removeCls(me.baseCls + '-' + me.textAlign);
864 btnEl.addCls(me.baseCls + '-' + align);
866 me.textAlign = align;
870 getTipAttr: function(){
871 return this.tooltipType == 'qtip' ? 'data-qtip' : 'title';
875 getRefItems: function(deep){
876 var menu = this.menu,
880 items = menu.getRefItems(deep);
887 clearTip: function() {
888 if (Ext.isObject(this.tooltip)) {
889 Ext.tip.QuickTipManager.unregister(this.btnEl);
894 beforeDestroy: function() {
899 if (me.menu && me.destroyMenu !== false) {
900 Ext.destroy(me.menu);
902 Ext.destroy(me.btnInnerEl, me.repeater);
907 onDestroy: function() {
910 me.doc.un('mouseover', me.monitorMouseOver, me);
911 me.doc.un('mouseup', me.onMouseUp, me);
913 Ext.ButtonToggleManager.unregister(me);
915 Ext.destroy(me.keyMap);
922 * Assigns this Button's click handler
923 * @param {Function} handler The function to call when the button is clicked
924 * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed.
925 * Defaults to this Button.
926 * @return {Ext.button.Button} this
928 setHandler: function(handler, scope) {
929 this.handler = handler;
935 * Sets this Button's text
936 * @param {String} text The button text
937 * @return {Ext.button.Button} this
939 setText: function(text) {
943 me.btnInnerEl.update(text || ' ');
946 me.doComponentLayout();
951 * Sets the background image (inline style) of the button. This method also changes the value of the {@link #icon}
953 * @param {String} icon The path to an image to display in the button
954 * @return {Ext.button.Button} this
956 setIcon: function(icon) {
958 iconEl = me.btnIconEl;
962 iconEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
969 * Gets the text for this Button
970 * @return {String} The button text
972 getText: function() {
977 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
978 * @param {Boolean} [state] Force a particular state
979 * @param {Boolean} [suppressEvent=false] True to stop events being fired when calling this method.
980 * @return {Ext.button.Button} this
982 toggle: function(state, suppressEvent) {
984 state = state === undefined ? !me.pressed : !!state;
985 if (state !== me.pressed) {
987 me[state ? 'addClsWithUI': 'removeClsWithUI'](me.pressedCls);
989 me.btnEl.dom.setAttribute('aria-pressed', state);
991 if (!suppressEvent) {
992 me.fireEvent('toggle', me, state);
993 Ext.callback(me.toggleHandler, me.scope || me, [me, state]);
999 maybeShowMenu: function(){
1001 if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) {
1007 * Shows this button's menu (if it has one)
1009 showMenu: function() {
1011 if (me.rendered && me.menu) {
1012 if (me.tooltip && me.getTipAttr() != 'title') {
1013 Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.btnEl);
1015 if (me.menu.isVisible()) {
1019 me.menu.showBy(me.el, me.menuAlign);
1025 * Hides this button's menu (if it has one)
1027 hideMenu: function() {
1028 if (this.hasVisibleMenu()) {
1035 * Returns true if the button has a menu and it is visible
1038 hasVisibleMenu: function() {
1039 var menu = this.menu;
1040 return menu && menu.rendered && menu.isVisible();
1044 onRepeatClick: function(repeat, e) {
1049 onClick: function(e) {
1051 if (me.preventDefault || (me.disabled && me.getHref()) && e) {
1054 if (e.button !== 0) {
1064 fireHandler: function(e){
1066 handler = me.handler;
1068 me.fireEvent('click', me, e);
1070 handler.call(me.scope || me, me, e);
1075 doToggle: function(){
1077 if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
1083 * @private mouseover handler called when a mouseover event occurs anywhere within the encapsulating element.
1084 * The targets are interrogated to see what is being entered from where.
1087 onMouseOver: function(e) {
1089 if (!me.disabled && !e.within(me.el, true, true)) {
1096 * mouseout handler called when a mouseout event occurs anywhere within the encapsulating element -
1097 * or the mouse leaves the encapsulating element.
1098 * The targets are interrogated to see what is being exited to where.
1101 onMouseOut: function(e) {
1103 if (!e.within(me.el, true, true)) {
1104 if (me.overMenuTrigger) {
1105 me.onMenuTriggerOut(e);
1113 * mousemove handler called when the mouse moves anywhere within the encapsulating element.
1114 * The position is checked to determine if the mouse is entering or leaving the trigger area. Using
1115 * mousemove to check this is more resource intensive than we'd like, but it is necessary because
1116 * the trigger area does not line up exactly with sub-elements so we don't always get mouseover/out
1117 * events when needed. In the future we should consider making the trigger a separate element that
1118 * is absolutely positioned and sized over the trigger area.
1120 onMouseMove: function(e) {
1123 over = me.overMenuTrigger,
1127 if (me.arrowAlign === 'right') {
1128 overlap = e.getX() - el.getX();
1129 btnSize = el.getWidth();
1131 overlap = e.getY() - el.getY();
1132 btnSize = el.getHeight();
1135 if (overlap > (btnSize - me.getTriggerSize())) {
1137 me.onMenuTriggerOver(e);
1141 me.onMenuTriggerOut(e);
1149 * Measures the size of the trigger area for menu and split buttons. Will be a width for
1150 * a right-aligned trigger and a height for a bottom-aligned trigger. Cached after first measurement.
1152 getTriggerSize: function() {
1154 size = me.triggerSize,
1155 side, sideFirstLetter, undef;
1157 if (size === undef) {
1158 side = me.arrowAlign;
1159 sideFirstLetter = side.charAt(0);
1160 size = me.triggerSize = me.el.getFrameWidth(sideFirstLetter) + me.btnWrap.getFrameWidth(sideFirstLetter) + (me.frameSize && me.frameSize[side] || 0);
1167 * virtual mouseenter handler called when it is detected that the mouseout event
1168 * signified the mouse entering the encapsulating element.
1171 onMouseEnter: function(e) {
1173 me.addClsWithUI(me.overCls);
1174 me.fireEvent('mouseover', me, e);
1179 * virtual mouseleave handler called when it is detected that the mouseover event
1180 * signified the mouse entering the encapsulating element.
1183 onMouseLeave: function(e) {
1185 me.removeClsWithUI(me.overCls);
1186 me.fireEvent('mouseout', me, e);
1191 * virtual mouseenter handler called when it is detected that the mouseover event
1192 * signified the mouse entering the arrow area of the button - the <em>.
1195 onMenuTriggerOver: function(e) {
1197 me.overMenuTrigger = true;
1198 me.fireEvent('menutriggerover', me, me.menu, e);
1203 * virtual mouseleave handler called when it is detected that the mouseout event
1204 * signified the mouse leaving the arrow area of the button - the <em>.
1207 onMenuTriggerOut: function(e) {
1209 delete me.overMenuTrigger;
1210 me.fireEvent('menutriggerout', me, me.menu, e);
1214 enable : function(silent) {
1217 me.callParent(arguments);
1219 me.removeClsWithUI('disabled');
1225 disable : function(silent) {
1228 me.callParent(arguments);
1230 me.addClsWithUI('disabled');
1231 me.removeClsWithUI(me.overCls);
1237 * Method to change the scale of the button. See {@link #scale} for allowed configurations.
1238 * @param {String} scale The scale to change to.
1240 setScale: function(scale) {
1242 ui = me.ui.replace('-' + me.scale, '');
1244 //check if it is an allowed scale
1245 if (!Ext.Array.contains(me.allowedScales, scale)) {
1246 throw('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')');
1254 setUI: function(ui) {
1257 //we need to append the scale to the UI, if not already done
1258 if (me.scale && !ui.match(me.scale)) {
1259 ui = ui + '-' + me.scale;
1262 me.callParent([ui]);
1264 // Set all the state classNames, as they need to include the UI
1265 // me.disabledCls += ' ' + me.baseCls + '-' + me.ui + '-disabled';
1269 onFocus: function(e) {
1272 me.addClsWithUI(me.focusCls);
1277 onBlur: function(e) {
1279 me.removeClsWithUI(me.focusCls);
1283 onMouseDown: function(e) {
1285 if (!me.disabled && e.button === 0) {
1286 me.addClsWithUI(me.pressedCls);
1287 me.doc.on('mouseup', me.onMouseUp, me);
1291 onMouseUp: function(e) {
1293 if (e.button === 0) {
1295 me.removeClsWithUI(me.pressedCls);
1297 me.doc.un('mouseup', me.onMouseUp, me);
1301 onMenuShow: function(e) {
1303 me.ignoreNextClick = 0;
1304 me.addClsWithUI(me.menuActiveCls);
1305 me.fireEvent('menushow', me, me.menu);
1309 onMenuHide: function(e) {
1311 me.removeClsWithUI(me.menuActiveCls);
1312 me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
1313 me.fireEvent('menuhide', me, me.menu);
1317 restoreClick: function() {
1318 this.ignoreNextClick = 0;
1322 onDownKey: function() {
1334 * Some browsers (notably Safari and older Chromes on Windows) add extra "padding" inside the button
1335 * element that cannot be removed. This method returns the size of that padding with a one-time detection.
1336 * @return {Number[]} [top, right, bottom, left]
1338 getPersistentBtnPadding: function() {
1339 var cls = Ext.button.Button,
1340 padding = cls.persistentPadding,
1341 btn, leftTop, btnEl, btnInnerEl;
1344 padding = cls.persistentPadding = [0, 0, 0, 0]; //set early to prevent recursion
1346 if (!Ext.isIE) { //short-circuit IE as it sometimes gives false positive for padding
1347 // Create auto-size button offscreen and measure its insides
1348 btn = Ext.create('Ext.button.Button', {
1349 renderTo: Ext.getBody(),
1351 style: 'position:absolute;top:-999px;'
1354 btnInnerEl = btn.btnInnerEl;
1355 btnEl.setSize(null, null); //clear any hard dimensions on the button el to see what it does naturally
1357 leftTop = btnInnerEl.getOffsetsTo(btnEl);
1358 padding[0] = leftTop[1];
1359 padding[1] = btnEl.getWidth() - btnInnerEl.getWidth() - leftTop[0];
1360 padding[2] = btnEl.getHeight() - btnInnerEl.getHeight() - leftTop[1];
1361 padding[3] = leftTop[0];
1373 function toggleGroup(btn, state) {
1376 g = groups[btn.toggleGroup];
1377 for (i = 0, l = g.length; i < l; i++) {
1386 * Private utility class used by Button
1389 Ext.ButtonToggleManager = {
1390 register: function(btn) {
1391 if (!btn.toggleGroup) {
1394 var group = groups[btn.toggleGroup];
1396 group = groups[btn.toggleGroup] = [];
1399 btn.on('toggle', toggleGroup);
1402 unregister: function(btn) {
1403 if (!btn.toggleGroup) {
1406 var group = groups[btn.toggleGroup];
1408 Ext.Array.remove(group, btn);
1409 btn.un('toggle', toggleGroup);
1414 * Gets the pressed button in the passed group or null
1415 * @param {String} group
1416 * @return {Ext.button.Button}
1418 getPressed: function(group) {
1419 var g = groups[group],
1423 for (len = g.length; i < len; i++) {
1424 if (g[i].pressed === true) {