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 * @class Ext.button.Button
17 * @extends Ext.Component
19 Create simple buttons with this component. Customisations include {@link #iconAlign aligned}
20 {@link #iconCls icons}, {@link #menu dropdown menus}, {@link #tooltip tooltips}
21 and {@link #scale sizing options}. Specify a {@link #handler handler} to run code when
22 a user clicks the button, or use {@link #listeners listeners} for other events such as
23 {@link #mouseover mouseover}.
25 {@img Ext.button.Button/Ext.button.Button1.png Ext.button.Button component}
28 Ext.create('Ext.Button', {
30 renderTo: Ext.getBody(),
32 alert('You clicked the button!')
36 The {@link #handler} configuration can also be updated dynamically using the {@link #setHandler} method.
39 Ext.create('Ext.Button', {
40 text : 'Dyanmic Handler Button',
41 renderTo: Ext.getBody(),
42 handler : function() {
43 //this button will spit out a different number every time you click it.
44 //so firstly we must check if that number is already set:
45 if (this.clickCount) {
46 //looks like the property is already set, so lets just add 1 to that number and alert the user
48 alert('You have clicked the button "' + this.clickCount + '" times.\n\nTry clicking it again..');
50 //if the clickCount property is not set, we will set it and alert the user
52 alert('You just clicked the button for the first time!\n\nTry pressing it again..');
57 A button within a container:
59 Ext.create('Ext.Container', {
60 renderTo: Ext.getBody(),
69 A useful option of Button is the {@link #scale} configuration. This configuration has three different options:
74 {@img Ext.button.Button/Ext.button.Button2.png Ext.button.Button component}
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`.
84 {@img Ext.button.Button/Ext.button.Button3.png Ext.button.Button component}
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 can either be a reference to a {@link Ext.menu.Menu menu}
94 object, a {@link Ext.menu.Menu menu} id or a {@link Ext.menu.Menu menu} config blob. When assigning a menu to a button, an arrow is automatically added to the button.
95 You can change the alignment of the arrow using the {@link #arrowAlign} configuration on button.
96 {@img Ext.button.Button/Ext.button.Button4.png Ext.button.Button component}
99 Ext.create('Ext.Button', {
100 text : 'Menu button',
101 renderTo : Ext.getBody(),
102 arrowAlign: 'bottom',
111 Using listeners, you can easily listen to events fired by any component, using the {@link #listeners} configuration or using the {@link #addListener} method.
112 Button has a variety of different listeners:
124 Ext.create('Ext.Button', {
126 renderTo : Ext.getBody(),
129 //this == the button, as we are in the local scope
130 this.setText('I was clicked!');
132 mouseover: function() {
133 //set a new config which says we moused over, if not already set
134 if (!this.mousedOver) {
135 this.mousedOver = true;
136 alert('You moused over a button!\n\nI wont do this again.');
143 * @docauthor Robert Dougan <rob@sencha.com>
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 * Read-only. True if this button is hidden
172 * Read-only. True if this button is disabled
178 * Read-only. True if this button is pressed (only if enableToggle = true)
184 * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
188 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
189 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
193 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
194 * The handler is passed the following parameters:<div class="mdetail-params"><ul>
195 * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
196 * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
201 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
202 * See also {@link Ext.panel.Panel}.<tt>{@link Ext.panel.Panel#minButtonWidth minButtonWidth}</tt>.
206 * @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
210 * @cfg {Boolean} hidden True to start hidden (defaults to false)
214 * @cfg {Boolean} disabled True to start disabled (defaults to false)
218 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
222 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
226 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
227 * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
231 * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
235 * @cfg {Boolean} allowDepress
236 * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
240 * @cfg {Boolean} enableToggle
241 * True to enable pressed/not pressed toggling (defaults to false)
246 * @cfg {Function} toggleHandler
247 * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
248 * <li><b>button</b> : Ext.button.Button<div class="sub-desc">this Button object</div></li>
249 * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>
255 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
259 * @cfg {String} menuAlign
260 * The position to align the menu to (see {@link Ext.core.Element#alignTo} for more details, defaults to 'tl-bl?').
265 * @cfg {String} overflowText If used in a {@link Ext.toolbar.Toolbar Toolbar}, the
266 * text to be used if this item is shown in the overflow menu. See also
267 * {@link Ext.toolbar.Item}.<code>{@link Ext.toolbar.Item#overflowText overflowText}</code>.
271 * @cfg {String} iconCls
272 * A css class which sets a background image to be used as the icon for this button
277 * submit, reset or button - defaults to 'button'
282 * @cfg {String} clickEvent
283 * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
284 * Defaults to <tt>'click'</tt>.
289 * @cfg {Boolean} preventDefault
290 * True to prevent the default action when the {@link #clickEvent} is processed. Defaults to true.
292 preventDefault: true,
295 * @cfg {Boolean} handleMouseEvents
296 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
298 handleMouseEvents: true,
301 * @cfg {String} tooltipType
302 * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
307 * @cfg {String} baseCls
308 * The base CSS class to add to all buttons. (Defaults to 'x-btn')
310 baseCls: Ext.baseCSSPrefix + 'btn',
313 * @cfg {String} pressedCls
314 * The CSS class to add to a button when it is in the pressed state. (Defaults to 'x-btn-pressed')
316 pressedCls: 'pressed',
319 * @cfg {String} overCls
320 * The CSS class to add to a button when it is in the over (hovered) state. (Defaults to 'x-btn-over')
325 * @cfg {String} focusCls
326 * The CSS class to add to a button when it is in the focussed state. (Defaults to 'x-btn-focus')
331 * @cfg {String} menuActiveCls
332 * The CSS class to add to a button when it's menu is active. (Defaults to 'x-btn-menu-active')
334 menuActiveCls: 'menu-active',
337 * @cfg {Object} baseParams
338 * An object literal of parameters to pass to the url when the {@link #href} property is specified.
342 * @cfg {Object} params
343 * An object literal of parameters to pass to the url when the {@link #href} property is specified.
344 * Any params override {@link #baseParams}. New params can be set using the {@link #setParams} method.
351 '<em class="{splitCls}">' +
353 '<a href="{href}" target="{target}"<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="link">' +
354 '<span class="{baseCls}-inner">' +
357 '<span class="{baseCls}-icon"></span>' +
361 '<button type="{type}" hidefocus="true"' +
362 // the autocomplete="off" is required to prevent Firefox from remembering
363 // the button's disabled state between page reloads.
364 '<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="button" autocomplete="off">' +
365 '<span class="{baseCls}-inner" style="{innerSpanStyle}">' +
368 '<span class="{baseCls}-icon"></span>' +
374 * @cfg {String} scale
375 * <p>(Optional) The size of the Button. Three values are allowed:</p>
376 * <ul class="mdetail-params">
377 * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
378 * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
379 * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
381 * <p>Defaults to <b><tt>'small'</tt></b>.</p>
386 * @private An array of allowed scales.
388 allowedScales: ['small', 'medium', 'large'],
391 * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
392 * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
393 * executed. Defaults to this Button.
397 * @cfg {String} iconAlign
398 * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
399 * <ul class="mdetail-params">
400 * <li>'top'<div class="sub-desc"></div></li>
401 * <li>'right'<div class="sub-desc"></div></li>
402 * <li>'bottom'<div class="sub-desc"></div></li>
403 * <li>'left'<div class="sub-desc"></div></li>
405 * <p>Defaults to <b><tt>'left'</tt></b>.</p>
410 * @cfg {String} arrowAlign
411 * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
412 * Two values are allowed:</p>
413 * <ul class="mdetail-params">
414 * <li>'right'<div class="sub-desc"></div></li>
415 * <li>'bottom'<div class="sub-desc"></div></li>
417 * <p>Defaults to <b><tt>'right'</tt></b>.</p>
422 * @cfg {String} arrowCls
423 * <p>(Optional) The className used for the inner arrow element if the button has a menu.</p>
428 * @cfg {Ext.Template} template (Optional)
429 * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
430 * Instances, or subclasses which need a different DOM structure may provide a different
431 * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
438 * A CSS class string to apply to the button's main element.
444 * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
448 * @cfg {Boolean} autoWidth
449 * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content.
450 * If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent
451 * the button from doing this automatic sizing.
452 * Defaults to <tt>undefined</tt>.
455 maskOnDisable: false,
458 initComponent: function() {
460 me.callParent(arguments);
465 * Fires when this button is clicked
466 * @param {Button} this
467 * @param {EventObject} e The click event
473 * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
474 * @param {Button} this
475 * @param {Boolean} pressed
481 * Fires when the mouse hovers over the button
482 * @param {Button} this
483 * @param {Event} e The event object
489 * Fires when the mouse exits the button
490 * @param {Button} this
491 * @param {Event} e The event object
497 * If this button has a menu, this event fires when it is shown
498 * @param {Button} this
505 * If this button has a menu, this event fires when it is hidden
506 * @param {Button} this
512 * @event menutriggerover
513 * If this button has a menu, this event fires when the mouse enters the menu triggering element
514 * @param {Button} this
516 * @param {EventObject} e
521 * @event menutriggerout
522 * If this button has a menu, this event fires when the mouse leaves the menu triggering element
523 * @param {Button} this
525 * @param {EventObject} e
531 // Flag that we'll have a splitCls
534 // retrieve menu by id or instantiate instance if needed
535 me.menu = Ext.menu.Manager.get(me.menu);
536 me.menu.ownerCt = me;
539 // Accept url as a synonym for href
544 // preventDefault defaults to false for links
545 if (me.href && !me.hasOwnProperty('preventDefault')) {
546 me.preventDefault = false;
549 if (Ext.isString(me.toggleGroup)) {
550 me.enableToggle = true;
556 initAria: function() {
558 var actionEl = this.getActionEl();
560 actionEl.dom.setAttribute('aria-haspopup', true);
565 getActionEl: function() {
570 getFocusEl: function() {
575 setButtonCls: function() {
580 if (me.useSetClass) {
581 if (!Ext.isEmpty(me.oldCls)) {
582 me.removeClsWithUI(me.oldCls);
583 me.removeClsWithUI(me.pressedCls);
586 // Check whether the button has an icon or not, and if it has an icon, what is th alignment
587 if (me.iconCls || me.icon) {
589 cls.push('icon-text-' + me.iconAlign);
593 } else if (me.text) {
598 me.addClsWithUI(cls);
599 me.addClsWithUI(me.pressed ? me.pressedCls : null);
604 onRender: function(ct, position) {
605 // classNames for the button
609 // Apply the renderData to the template args
610 Ext.applyIf(me.renderData, me.getTemplateArgs());
612 // Extract the button and the button wrapping element
613 Ext.applyIf(me.renderSelectors, {
614 btnEl : me.href ? 'a' : 'button',
616 btnInnerEl: '.' + me.baseCls + '-inner',
617 btnIconEl: '.'+ me.baseCls + '-icon'
621 me.ui = me.ui + '-' + me.scale;
624 // Render internal structure
625 me.callParent(arguments);
627 // If it is a split button + has a toolip for the arrow
628 if (me.split && me.arrowTooltip) {
629 me.arrowEl.dom[me.tooltipType] = me.arrowTooltip;
632 // Add listeners to the focus and blur events on the element
639 // Set btn as a local variable for easy access
647 me.setIconCls(me.iconCls);
651 me.setTooltip(me.tooltip, true);
654 // Add the mouse events to the button
655 if (me.handleMouseEvents) {
658 mouseover: me.onMouseOver,
659 mouseout: me.onMouseOut,
660 mousedown: me.onMouseDown
665 mousemove: me.onMouseMove,
671 // Check if the button has a menu
679 me.keyMap = Ext.create('Ext.util.KeyMap', me.el, {
680 key: Ext.EventObject.DOWN,
681 handler: me.onDownKey,
686 // Check if it is a repeat button
688 repeater = Ext.create('Ext.util.ClickRepeater', btn, Ext.isObject(me.repeat) ? me.repeat: {});
689 me.mon(repeater, 'click', me.onRepeatClick, me);
691 me.mon(btn, me.clickEvent, me.onClick, me);
694 // Register the button in the toggle manager
695 Ext.ButtonToggleManager.register(me);
699 * <p>This method returns an object which provides substitution parameters for the {@link #renderTpl XTemplate} used
700 * to create this Button's DOM structure.</p>
701 * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
702 * own implementation of this method.</p>
703 * <p>The default implementation which provides data for the default {@link #template} returns an Object containing the
704 * following properties:</p><div class="mdetail-params"><ul>
705 * <li><code>type</code> : The <button>'s {@link #type}</li>
706 * <li><code>splitCls</code> : A CSS class to determine the presence and position of an arrow icon. (<code>'x-btn-arrow'</code> or <code>'x-btn-arrow-bottom'</code> or <code>''</code>)</li>
707 * <li><code>cls</code> : A CSS class name applied to the Button's main <tbody> element which determines the button's scale and icon alignment.</li>
708 * <li><code>text</code> : The {@link #text} to display ion the Button.</li>
709 * <li><code>tabIndex</code> : The tab index within the input flow.</li>
711 * @return {Array} Substitution data for a Template.
713 getTemplateArgs: function() {
715 persistentPadding = me.getPersistentBtnPadding(),
718 // Create negative margin offsets to counteract persistent button padding if needed
719 if (Math.max.apply(Math, persistentPadding) > 0) {
720 innerSpanStyle = 'margin:' + Ext.Array.map(persistentPadding, function(pad) {
727 target : me.target || '_blank',
729 splitCls : me.getSplitCls(),
731 text : me.text || ' ',
732 tabIndex : me.tabIndex,
733 innerSpanStyle: innerSpanStyle
739 * If there is a configured href for this Button, returns the href with parameters appended.
740 * @returns The href string with parameters appended.
742 getHref: function() {
744 params = Ext.apply({}, me.baseParams);
746 // write baseParams first, then write any params
747 params = Ext.apply(params, me.params);
748 return me.href ? Ext.urlAppend(me.href, Ext.Object.toQueryString(params)) : false;
752 * <p><b>Only valid if the Button was originally configured with a {@link #url}</b></p>
753 * <p>Sets the href of the link dynamically according to the params passed, and any {@link #baseParams} configured.</p>
754 * @param {Object} params Parameters to use in the href URL.
756 setParams: function(params) {
757 this.params = params;
758 this.btnEl.dom.href = this.getHref();
761 getSplitCls: function() {
763 return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
767 afterRender: function() {
769 me.useSetClass = true;
771 me.doc = Ext.getDoc();
772 this.callParent(arguments);
776 * Sets the CSS class that provides a background image to use as the button's icon. This method also changes
777 * the value of the {@link #iconCls} config internally.
778 * @param {String} cls The CSS class providing the icon image
779 * @return {Ext.button.Button} this
781 setIconCls: function(cls) {
783 btnIconEl = me.btnIconEl;
785 // Remove the previous iconCls from the button
786 btnIconEl.removeCls(me.iconCls);
787 btnIconEl.addCls(cls || '');
795 * Sets the tooltip for this Button.
796 * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
797 * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
798 * <li><b>Object</b> : A configuration object for {@link Ext.tip.QuickTipManager#register}.</li>
800 * @return {Ext.button.Button} this
802 setTooltip: function(tooltip, initial) {
809 if (Ext.isObject(tooltip)) {
810 Ext.tip.QuickTipManager.register(Ext.apply({
814 me.tooltip = tooltip;
816 me.btnEl.dom.setAttribute('data-' + this.tooltipType, tooltip);
819 me.tooltip = tooltip;
825 getRefItems: function(deep){
826 var menu = this.menu,
830 items = menu.getRefItems(deep);
837 clearTip: function() {
838 if (Ext.isObject(this.tooltip)) {
839 Ext.tip.QuickTipManager.unregister(this.btnEl);
844 beforeDestroy: function() {
849 if (me.menu && me.destroyMenu !== false) {
850 Ext.destroy(me.btnEl, me.btnInnerEl, me.menu);
852 Ext.destroy(me.repeater);
856 onDestroy: function() {
859 me.doc.un('mouseover', me.monitorMouseOver, me);
860 me.doc.un('mouseup', me.onMouseUp, me);
863 delete me.btnInnerEl;
864 Ext.ButtonToggleManager.unregister(me);
866 Ext.destroy(me.keyMap);
873 * Assigns this Button's click handler
874 * @param {Function} handler The function to call when the button is clicked
875 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
876 * Defaults to this Button.
877 * @return {Ext.button.Button} this
879 setHandler: function(handler, scope) {
880 this.handler = handler;
886 * Sets this Button's text
887 * @param {String} text The button text
888 * @return {Ext.button.Button} this
890 setText: function(text) {
894 me.btnInnerEl.update(text || ' ');
897 me.doComponentLayout();
902 * Sets the background image (inline style) of the button. This method also changes
903 * the value of the {@link #icon} config internally.
904 * @param {String} icon The path to an image to display in the button
905 * @return {Ext.button.Button} this
907 setIcon: function(icon) {
909 btnInnerEl = me.btnInnerEl;
912 btnInnerEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
919 * Gets the text for this Button
920 * @return {String} The button text
922 getText: function() {
927 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
928 * @param {Boolean} state (optional) Force a particular state
929 * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
930 * @return {Ext.button.Button} this
932 toggle: function(state, suppressEvent) {
934 state = state === undefined ? !me.pressed: !!state;
935 if (state !== me.pressed) {
937 me[state ? 'addClsWithUI': 'removeClsWithUI'](me.pressedCls);
939 me.btnEl.dom.setAttribute('aria-pressed', state);
941 if (!suppressEvent) {
942 me.fireEvent('toggle', me, state);
943 Ext.callback(me.toggleHandler, me.scope || me, [me, state]);
950 * Show this button's menu (if it has one)
952 showMenu: function() {
954 if (me.rendered && me.menu) {
956 Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.btnEl);
958 if (me.menu.isVisible()) {
962 me.menu.showBy(me.el, me.menuAlign);
968 * Hide this button's menu (if it has one)
970 hideMenu: function() {
971 if (this.hasVisibleMenu()) {
978 * Returns true if the button has a menu and it is visible
981 hasVisibleMenu: function() {
982 var menu = this.menu;
983 return menu && menu.rendered && menu.isVisible();
987 onRepeatClick: function(repeat, e) {
992 onClick: function(e) {
994 if (me.preventDefault || (me.disabled && me.getHref()) && e) {
997 if (e.button !== 0) {
1001 if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
1004 if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) {
1007 me.fireEvent('click', me, e);
1009 me.handler.call(me.scope || me, me, e);
1016 * @private mouseover handler called when a mouseover event occurs anywhere within the encapsulating element.
1017 * The targets are interrogated to see what is being entered from where.
1020 onMouseOver: function(e) {
1022 if (!me.disabled && !e.within(me.el, true, true)) {
1028 * @private mouseout handler called when a mouseout event occurs anywhere within the encapsulating element -
1029 * or the mouse leaves the encapsulating element.
1030 * The targets are interrogated to see what is being exited to where.
1033 onMouseOut: function(e) {
1035 if (!e.within(me.el, true, true)) {
1036 if (me.overMenuTrigger) {
1037 me.onMenuTriggerOut(e);
1044 * @private mousemove handler called when the mouse moves anywhere within the encapsulating element.
1045 * The position is checked to determine if the mouse is entering or leaving the trigger area. Using
1046 * mousemove to check this is more resource intensive than we'd like, but it is necessary because
1047 * the trigger area does not line up exactly with sub-elements so we don't always get mouseover/out
1048 * events when needed. In the future we should consider making the trigger a separate element that
1049 * is absolutely positioned and sized over the trigger area.
1051 onMouseMove: function(e) {
1054 over = me.overMenuTrigger,
1058 if (me.arrowAlign === 'right') {
1059 overlap = e.getX() - el.getX();
1060 btnSize = el.getWidth();
1062 overlap = e.getY() - el.getY();
1063 btnSize = el.getHeight();
1066 if (overlap > (btnSize - me.getTriggerSize())) {
1068 me.onMenuTriggerOver(e);
1072 me.onMenuTriggerOut(e);
1079 * @private Measures the size of the trigger area for menu and split buttons. Will be a width for
1080 * a right-aligned trigger and a height for a bottom-aligned trigger. Cached after first measurement.
1082 getTriggerSize: function() {
1084 size = me.triggerSize,
1085 side, sideFirstLetter, undef;
1087 if (size === undef) {
1088 side = me.arrowAlign;
1089 sideFirstLetter = side.charAt(0);
1090 size = me.triggerSize = me.el.getFrameWidth(sideFirstLetter) + me.btnWrap.getFrameWidth(sideFirstLetter) + (me.frameSize && me.frameSize[side] || 0);
1096 * @private virtual mouseenter handler called when it is detected that the mouseout event
1097 * signified the mouse entering the encapsulating element.
1100 onMouseEnter: function(e) {
1102 me.addClsWithUI(me.overCls);
1103 me.fireEvent('mouseover', me, e);
1107 * @private virtual mouseleave handler called when it is detected that the mouseover event
1108 * signified the mouse entering the encapsulating element.
1111 onMouseLeave: function(e) {
1113 me.removeClsWithUI(me.overCls);
1114 me.fireEvent('mouseout', me, e);
1118 * @private virtual mouseenter handler called when it is detected that the mouseover event
1119 * signified the mouse entering the arrow area of the button - the <em>.
1122 onMenuTriggerOver: function(e) {
1124 me.overMenuTrigger = true;
1125 me.fireEvent('menutriggerover', me, me.menu, e);
1129 * @private virtual mouseleave handler called when it is detected that the mouseout event
1130 * signified the mouse leaving the arrow area of the button - the <em>.
1133 onMenuTriggerOut: function(e) {
1135 delete me.overMenuTrigger;
1136 me.fireEvent('menutriggerout', me, me.menu, e);
1140 enable : function(silent) {
1143 me.callParent(arguments);
1145 me.removeClsWithUI('disabled');
1151 disable : function(silent) {
1154 me.callParent(arguments);
1156 me.addClsWithUI('disabled');
1162 * Method to change the scale of the button. See {@link #scale} for allowed configurations.
1163 * @param {String} scale The scale to change to.
1165 setScale: function(scale) {
1167 ui = me.ui.replace('-' + me.scale, '');
1169 //check if it is an allowed scale
1170 if (!Ext.Array.contains(me.allowedScales, scale)) {
1171 throw('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')');
1179 setUI: function(ui) {
1182 //we need to append the scale to the UI, if not already done
1183 if (me.scale && !ui.match(me.scale)) {
1184 ui = ui + '-' + me.scale;
1187 me.callParent([ui]);
1189 // Set all the state classNames, as they need to include the UI
1190 // me.disabledCls += ' ' + me.baseCls + '-' + me.ui + '-disabled';
1194 onFocus: function(e) {
1197 me.addClsWithUI(me.focusCls);
1202 onBlur: function(e) {
1204 me.removeClsWithUI(me.focusCls);
1208 onMouseDown: function(e) {
1210 if (!me.disabled && e.button === 0) {
1211 me.addClsWithUI(me.pressedCls);
1212 me.doc.on('mouseup', me.onMouseUp, me);
1216 onMouseUp: function(e) {
1218 if (e.button === 0) {
1220 me.removeClsWithUI(me.pressedCls);
1222 me.doc.un('mouseup', me.onMouseUp, me);
1226 onMenuShow: function(e) {
1228 me.ignoreNextClick = 0;
1229 me.addClsWithUI(me.menuActiveCls);
1230 me.fireEvent('menushow', me, me.menu);
1234 onMenuHide: function(e) {
1236 me.removeClsWithUI(me.menuActiveCls);
1237 me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
1238 me.fireEvent('menuhide', me, me.menu);
1242 restoreClick: function() {
1243 this.ignoreNextClick = 0;
1247 onDownKey: function() {
1258 * @private Some browsers (notably Safari and older Chromes on Windows) add extra "padding" inside the button
1259 * element that cannot be removed. This method returns the size of that padding with a one-time detection.
1260 * @return Array [top, right, bottom, left]
1262 getPersistentBtnPadding: function() {
1263 var cls = Ext.button.Button,
1264 padding = cls.persistentPadding,
1265 btn, leftTop, btnEl, btnInnerEl;
1268 padding = cls.persistentPadding = [0, 0, 0, 0]; //set early to prevent recursion
1270 if (!Ext.isIE) { //short-circuit IE as it sometimes gives false positive for padding
1271 // Create auto-size button offscreen and measure its insides
1272 btn = Ext.create('Ext.button.Button', {
1273 renderTo: Ext.getBody(),
1275 style: 'position:absolute;top:-999px;'
1278 btnInnerEl = btn.btnInnerEl;
1279 btnEl.setSize(null, null); //clear any hard dimensions on the button el to see what it does naturally
1281 leftTop = btnInnerEl.getOffsetsTo(btnEl);
1282 padding[0] = leftTop[1];
1283 padding[1] = btnEl.getWidth() - btnInnerEl.getWidth() - leftTop[0];
1284 padding[2] = btnEl.getHeight() - btnInnerEl.getHeight() - leftTop[1];
1285 padding[3] = leftTop[0];
1298 function toggleGroup(btn, state) {
1300 g = groups[btn.toggleGroup];
1301 for (i = 0, l = g.length; i < l; i++) {
1308 // Private utility class used by Button
1309 Ext.ButtonToggleManager = {
1310 register: function(btn) {
1311 if (!btn.toggleGroup) {
1314 var group = groups[btn.toggleGroup];
1316 group = groups[btn.toggleGroup] = [];
1319 btn.on('toggle', toggleGroup);
1322 unregister: function(btn) {
1323 if (!btn.toggleGroup) {
1326 var group = groups[btn.toggleGroup];
1328 Ext.Array.remove(group, btn);
1329 btn.un('toggle', toggleGroup);
1334 * Gets the pressed button in the passed group or null
1335 * @param {String} group
1338 getPressed: function(group) {
1339 var g = groups[group],
1343 for (len = g.length; i < len; i++) {
1344 if (g[i].pressed === true) {