/** * @class Ext.button.Button * @extends Ext.Component * Simple Button class * @cfg {String} text The button text to be used as innerHTML (html tags are accepted) * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon') * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event). * The handler is passed the following parameters:
* @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width). * See also {@link Ext.panel.Panel}.{@link Ext.panel.Panel#minButtonWidth minButtonWidth}. * @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 * @cfg {Boolean} hidden True to start hidden (defaults to false) * @cfg {Boolean} disabled True to start disabled (defaults to false) * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true) * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed) * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false). * @constructor * Create a new button * @param {Object} config The config object * @xtype button */ Ext.define('Ext.button.Button', { /* Begin Definitions */ alias: 'widget.button', extend: 'Ext.Component', requires: [ 'Ext.menu.MenuMgr', 'Ext.util.ClickRepeater', 'Ext.layout.component.Button', 'Ext.util.TextMetrics' ], alternateClassName: 'Ext.Button', /* End Definitions */ isButton: true, componentLayout: 'button', /** * Read-only. True if this button is hidden * @type Boolean */ hidden: false, /** * Read-only. True if this button is disabled * @type Boolean */ disabled: false, /** * Read-only. True if this button is pressed (only if enableToggle = true) * @type Boolean */ pressed: false, /** * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined) */ /** * @cfg {Boolean} allowDepress * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true. */ /** * @cfg {Boolean} enableToggle * True to enable pressed/not pressed toggling (defaults to false) */ enableToggle: false, /** * @cfg {Function} toggleHandler * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed: */ /** * @cfg {Mixed} menu * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined). */ /** * @cfg {String} menuAlign * The position to align the menu to (see {@link Ext.core.Element#alignTo} for more details, defaults to 'tl-bl?'). */ menuAlign: 'tl-bl?', /** * @cfg {String} overflowText If used in a {@link Ext.toolbar.Toolbar Toolbar}, the * text to be used if this item is shown in the overflow menu. See also * {@link Ext.toolbar.Toolbar.Item}.{@link Ext.toolbar.Toolbar.Item#overflowText overflowText}. */ /** * @cfg {String} iconCls * A css class which sets a background image to be used as the icon for this button */ /** * @cfg {String} type * submit, reset or button - defaults to 'button' */ type: 'button', /** * @cfg {String} clickEvent * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu). * Defaults to 'click'. */ clickEvent: 'click', /** * @cfg {Boolean} handleMouseEvents * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true) */ handleMouseEvents: true, /** * @cfg {String} tooltipType * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute. */ tooltipType: 'qtip', /** * @cfg {String} baseCls * The base CSS class to add to all buttons. (Defaults to 'x-btn') */ baseCls: Ext.baseCSSPrefix + 'btn', /** * @cfg {String} pressedCls * The CSS class to add to a button when it is in the pressed state. (Defaults to 'x-btn-default-small-pressed') */ /** * @cfg {String} overCls * The CSS class to add to a button when it is in the over (hovered) state. (Defaults to 'x-btn-default-small-over') */ /** * @cfg {String} focusCls * The CSS class to add to a button when it is in the focussed state. (Defaults to 'x-btn-default-small-focus') */ ariaRole: 'button', // inherited renderTpl: '' + '' + '', /** * @cfg {String} scale *

(Optional) The size of the Button. Three values are allowed:

* *

Defaults to 'small'.

*/ scale: 'small', /** * @cfg {String} ui *

(Optional) The UI specified for the button.

*

Defaults to 'default'.

*/ ui: 'default', /** * @cfg {Object} scope The scope (this reference) in which the * {@link #handler} and {@link #toggleHandler} is * executed. Defaults to this Button. */ /** * @cfg {String} iconAlign *

(Optional) The side of the Button box to render the icon. Four values are allowed:

* *

Defaults to 'left'.

*/ iconAlign: 'left', /** * @cfg {String} arrowAlign *

(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}. * Two values are allowed:

* *

Defaults to 'right'.

*/ arrowAlign: 'right', /** * @cfg arrowCls {String} arrowCls *

(Optional) The className used for the inner arrow element if the button has a menu.

*/ arrowCls: 'arrow', /** * @cfg {Ext.Template} template (Optional) *

A {@link Ext.Template Template} used to create the Button's DOM structure.

* Instances, or subclasses which need a different DOM structure may provide a different * template layout in conjunction with an implementation of {@link #getTemplateArgs}. * @type Ext.Template * @property template */ /** * @cfg {String} cls * A CSS class string to apply to the button's main element. */ /** * @property menu * @type Menu * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option. */ /** * @cfg {Boolean} autoWidth * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content. * If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent * the button from doing this automatic sizing. * Defaults to undefined. */ initComponent: function() { var me = this; me.callParent(arguments); me.addEvents( /** * @event click * Fires when this button is clicked * @param {Button} this * @param {EventObject} e The click event */ 'click', /** * @event toggle * Fires when the 'pressed' state of this button changes (only if enableToggle = true) * @param {Button} this * @param {Boolean} pressed */ 'toggle', /** * @event mouseover * Fires when the mouse hovers over the button * @param {Button} this * @param {Event} e The event object */ 'mouseover', /** * @event mouseout * Fires when the mouse exits the button * @param {Button} this * @param {Event} e The event object */ 'mouseout', /** * @event menushow * If this button has a menu, this event fires when it is shown * @param {Button} this * @param {Menu} menu */ 'menushow', /** * @event menuhide * If this button has a menu, this event fires when it is hidden * @param {Button} this * @param {Menu} menu */ 'menuhide', /** * @event menutriggerover * If this button has a menu, this event fires when the mouse enters the menu triggering element * @param {Button} this * @param {Menu} menu * @param {EventObject} e */ 'menutriggerover', /** * @event menutriggerout * If this button has a menu, this event fires when the mouse leaves the menu triggering element * @param {Button} this * @param {Menu} menu * @param {EventObject} e */ 'menutriggerout' ); if (me.menu) { // Flag that we'll have a splitCls me.split = true; // retrieve menu by id or instantiate instance if needed me.menu = Ext.menu.MenuMgr.get(me.menu); } if (Ext.isString(me.toggleGroup)) { me.enableToggle = true; } // // me.baseCls += ('-' + me.ui + '-' + me.scale); }, initAria: function() { Ext.button.Button.superclass.initAria.call(this); var actionEl = this.getActionEl(); if (this.menu) { actionEl.dom.setAttribute('aria-haspopup', true); } }, getActionEl: function() { return this.btnEl; }, getFocusEl: function() { return this.btnEl; }, // private setButtonCls: function() { var me = this, el = me.el; if (me.useSetClass) { if (!Ext.isEmpty(me.oldCls)) { el.removeCls([me.oldCls, me.baseCls + '-pressed', me.pressedCls]); } me.oldCls = (me.iconCls || me.icon) ? (me.text ? me.baseCls + '-text-icon ' + me.baseCls + '-text-icon-' + me.iconAlign : me.baseCls + '-icon') : ''; el.addCls([me.oldCls, me.pressed ? (me.baseCls + '-pressed ' + me.pressedCls) : null]); } }, // private onRender: function(ct, position) { // classNames for the button var me = this, repeater, btn, btnIconCls; me.cls = (me.cls || '') + ' ' + me.baseCls + '-' + me.ui; if (me.scale) { me.ui += '-' + me.scale; me.addCls(me.baseCls + '-' + me.scale); } me.disabledCls = me.disabledCls + ' ' + me.baseCls + '-' + me.ui + '-disabled'; me.overCls = me.baseCls + '-' + me.ui + '-over'; me.pressedCls = me.baseCls + '-' + me.ui + '-pressed'; me.focusCls = me.baseCls + '-' + me.ui + '-focus'; // only add the icon class if the button has an icon if (me.iconCls || me.icon) { if (me.text) { btnIconCls = me.baseCls + '-' + me.ui + '-icon-text-' + me.iconAlign; } else { btnIconCls = me.baseCls + '-' + me.ui + '-icon'; } } else if (me.text) { me.addCls(me.baseCls + '-' + me.ui + '-noicon'); } Ext.applyIf(me.renderData, me.getTemplateArgs()); // Extract the button, and if we are split, the arrow element Ext.applyIf(me.renderSelectors, { btnEl: 'button' }); if (me.split) { me.renderSelectors.arrowEl = '.' + me.renderData.splitCls; } me.callParent(arguments); if (me.split && me.arrowTooltip) { me.arrowEl.dom[me.tooltipType] = me.arrowTooltip; } me.mon(me.btnEl, { scope: me, focus: me.onFocus, blur : me.onBlur }); btn = me.el; me.addCls(btnIconCls); if (me.icon) { me.setIcon(me.icon); } if (me.iconCls) { me.setIconClass(me.iconCls); } if (me.tooltip) { me.setTooltip(me.tooltip, true); } if (me.handleMouseEvents) { me.mon(btn, { scope: me, mouseover: me.onMouseOver, mousedown: me.onMouseDown }); // new functionality for monitoring on the document level //this.mon(btn, 'mouseout', this.onMouseOut, this); } if (me.menu) { me.mon(me.menu, { scope: me, show: me.onMenuShow, hide: me.onMenuHide }); } if (me.repeat) { repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(me.repeat) ? me.repeat: {}); me.mon(repeater, 'click', me.onRepeatClick, me); } else { me.mon(btn, me.clickEvent, me.onClick, me); } Ext.ButtonToggleMgr.register(me); }, /** *

This method returns an object which provides substitution parameters for the {@link #renderTpl XTemplate} used * to create this Button's DOM structure.

*

Instances or subclasses which use a different Template to create a different DOM structure may need to provide their * own implementation of this method.

*

The default implementation which provides data for the default {@link #template} returns an Object containing the * following properties:

* @return {Array} Substitution data for a Template. */ getTemplateArgs: function() { var me = this; return { type : me.type, splitCls : me.getSplitCls(), cls : me.cls, text : me.text || ' ', tabIndex : me.tabIndex }; }, getSplitCls: function() { return this.split ? (this.baseCls + '-' + this.arrowCls) + ' ' + (this.baseCls + '-' + this.arrowCls + '-' + this.arrowAlign) : ''; }, // private afterRender: function() { var me = this; me.useSetClass = true; me.setButtonCls(); me.doc = Ext.getDoc(); this.callParent(arguments); }, /** * Sets the CSS class that provides a background image to use as the button's icon. This method also changes * the value of the {@link iconCls} config internally. * @param {String} cls The CSS class providing the icon image * @return {Ext.button.Button} this */ setIconClass: function(cls) { var me = this; if (me.el) { // Remove the previous iconCls from the button me.btnEl.removeCls(me.iconCls); me.btnEl.addCls([me.baseCls + '-text', cls || '']); me.setButtonCls(); } me.iconCls = cls; return me; }, /** * Sets the tooltip for this Button. * @param {String/Object} tooltip. This may be:
* @return {Ext.button.Button} this */ setTooltip: function(tooltip, initial) { var me = this; if (me.rendered) { if (!initial) { me.clearTip(); } if (Ext.isObject(tooltip)) { Ext.tip.QuickTips.register(Ext.apply({ target: me.btnEl.id }, tooltip)); me.tooltip = tooltip; } else { me.btnEl.dom[me.tooltipType] = tooltip; } } else { me.tooltip = tooltip; } return me; }, // private getRefItems: function(deep){ var menu = this.menu, items; if (menu) { items = menu.getRefItems(deep); items.unshift(menu); } return items || []; }, // private clearTip: function() { if (Ext.isObject(this.tooltip)) { Ext.tip.QuickTips.unregister(this.btnEl); } }, // private beforeDestroy: function() { var me = this; if (me.rendered) { me.clearTip(); } if (me.menu && me.destroyMenu !== false) { Ext.destroy(me.btnEl, me.menu); } Ext.destroy(me.repeater); }, // private onDestroy: function() { var me = this; if (me.rendered) { me.doc.un('mouseover', me.monitorMouseOver, me); me.doc.un('mouseup', me.onMouseUp, me); delete me.doc; delete me.btnEl; Ext.ButtonToggleMgr.unregister(me); } Ext.button.Button.superclass.onDestroy.call(me); }, /** * Assigns this Button's click handler * @param {Function} handler The function to call when the button is clicked * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. * Defaults to this Button. * @return {Ext.button.Button} this */ setHandler: function(handler, scope) { this.handler = handler; this.scope = scope; return this; }, /** * Sets this Button's text * @param {String} text The button text * @return {Ext.button.Button} this */ setText: function(text) { var me = this; me.text = text; if (me.el) { me.btnEl.update(text || ' '); me.setButtonCls(); } me.doComponentLayout(); return me; }, /** * Sets the background image (inline style) of the button. This method also changes * the value of the {@link icon} config internally. * @param {String} icon The path to an image to display in the button * @return {Ext.button.Button} this */ setIcon: function(icon) { var me = this; me.icon = icon; if (me.el) { me.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')': ''); me.setButtonCls(); } return me; }, /** * Gets the text for this Button * @return {String} The button text */ getText: function() { return this.text; }, /** * If a state it passed, it becomes the pressed state otherwise the current state is toggled. * @param {Boolean} state (optional) Force a particular state * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method. * @return {Ext.button.Button} this */ toggle: function(state, suppressEvent) { var me = this; state = state === undefined ? !me.pressed: !!state; if (state !== me.pressed) { if (me.rendered) { me.el[state ? 'addCls': 'removeCls']([me.baseCls + '-pressed', me.pressedCls]); } me.btnEl.dom.setAttribute('aria-pressed', state); me.pressed = state; if (!suppressEvent) { me.fireEvent('toggle', me, state); Ext.callback(me.toggleHandler, me.scope || me, [me, state]); } } return me; }, // private onDisable: function() { this.onDisableChange(true); }, // private onEnable: function() { this.onDisableChange(false); }, onDisableChange: function(disabled) { var me = this; if (me.el) { if (!Ext.isIE6 || !me.text) { me.el[disabled ? 'addCls': 'removeCls'](me.disabledClass); } me.el.dom.disabled = disabled; } me.disabled = disabled; }, /** * Show this button's menu (if it has one) */ showMenu: function() { var me = this; if (me.rendered && me.menu) { if (me.tooltip) { Ext.tip.QuickTips.getQuickTip().cancelShow(me.btnEl); } if (me.menu.isVisible()) { me.menu.hide(); } // Allow the menu to find a z-index parent on render by examining its ownerCt chain if (!me.menu.ownerCt) { me.menu.ownerCt = me.ownerCt; } me.menu.showBy(me.el, me.menuAlign); } return me; }, /** * Hide this button's menu (if it has one) */ hideMenu: function() { if (this.hasVisibleMenu()) { this.menu.hide(); } return this; }, /** * Returns true if the button has a menu and it is visible * @return {Boolean} */ hasVisibleMenu: function() { var menu = this.menu; return menu && menu.rendered && menu.isVisible(); }, // private onRepeatClick: function(repeat, e) { this.onClick(e); }, // private onClick: function(e) { var me = this; if (e) { e.preventDefault(); } if (e.button !== 0) { return; } if (!me.disabled) { if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) { me.toggle(); } if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) { me.showMenu(); } me.fireEvent('click', me, e); if (me.handler) { //this.el.removeCls(me.baseCls + '-over'); me.handler.call(me.scope || me, me, e); } } }, // private isMenuTriggerOver: function(e, internal) { return this.menu && !internal; }, // private isMenuTriggerOut: function(e, internal) { return this.menu && !internal; }, // private onMouseOver: function(e) { var me = this, internal; if (!me.disabled) { internal = e.within(me.el, true); if (!internal) { me.el.addCls([me.baseCls + '-over', me.overCls]); if (!me.monitoringMouseOver) { me.doc.on('mouseover', me.monitorMouseOver, me); me.monitoringMouseOver = true; } me.fireEvent('mouseover', me, e); } if (me.isMenuTriggerOver(e, internal)) { me.fireEvent('menutriggerover', me, me.menu, e); } } }, // private monitorMouseOver: function(e) { var me = this; if (e.target !== me.el.dom && !e.within(me.el)) { if (me.monitoringMouseOver) { me.doc.un('mouseover', me.monitorMouseOver, me); me.monitoringMouseOver = false; } me.onMouseOut(e); } }, // private onMouseOut: function(e) { var me = this, internal = e.within(me.el) && e.target !== me.el.dom; me.el.removeCls([me.baseCls + '-over', me.overCls]); // if (!Ext.supports.CSS3BorderRadius) { // me.tableEl.removeCls(me.baseCls + '-over'); // me.tbodyEl.removeCls(me.baseCls + '-over'); // } me.fireEvent('mouseout', me, e); if (me.isMenuTriggerOut(e, internal)) { me.fireEvent('menutriggerout', me, me.menu, e); } }, focus: function() { this.btnEl.focus(); }, blur: function() { this.btnEl.blur(); }, // private onFocus: function(e) { var me = this; if (!me.disabled) { me.el.addCls([me.baseCls + '-focus', me.focusCls]); } }, // private onBlur: function(e) { var me = this; me.el.removeCls([me.baseCls + '-focus', me.focusCls]); }, // private onMouseDown: function(e) { var me = this; if (!me.disabled && e.button === 0) { me.el.addCls([me.baseCls + '-pressed', me.pressedCls]); me.doc.on('mouseup', me.onMouseUp, me); } }, // private onMouseUp: function(e) { var me = this; if (e.button === 0) { me.el.removeCls([me.baseCls + '-pressed', me.pressedCls]); me.doc.un('mouseup', me.onMouseUp, me); } }, // private onMenuShow: function(e) { var me = this; me.ignoreNextClick = 0; me.el.addCls(me.baseCls + '-menu-active'); me.fireEvent('menushow', me, me.menu); }, // private onMenuHide: function(e) { var me = this; me.el.removeCls(me.baseCls + '-menu-active'); me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me); me.fireEvent('menuhide', me, me.menu); }, // private restoreClick: function() { this.ignoreNextClick = 0; } }, function() { // Private utility class used by Button Ext.ButtonToggleMgr = (function() { var groups = {}, g, i, l; function toggleGroup(btn, state) { if (state) { g = groups[btn.toggleGroup]; for (i = 0, l = g.length; i < l; i++) { if (g[i] !== btn) { g[i].toggle(false); } } } } return { register: function(btn) { if (!btn.toggleGroup) { return; } var g = groups[btn.toggleGroup]; if (!g) { g = groups[btn.toggleGroup] = []; } g.push(btn); btn.on('toggle', toggleGroup); }, unregister: function(btn) { if (!btn.toggleGroup) { return; } var g = groups[btn.toggleGroup]; if (g) { g.remove(btn); btn.un('toggle', toggleGroup); } }, /** * Gets the pressed button in the passed group or null * @param {String} group * @return Button */ getPressed: function(group) { var g = groups[group], i = 0, len; if (g) { for (len = g.length; i < len; i++) { if (g[i].pressed === true) { return g[i]; } } } return null; } }; }()); });