X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/c930e9176a5a85509c5b0230e2bff5c22a591432..b37ceabb82336ee82757cd32efe353cfab8ec267:/pkgs/pkg-buttons-debug.js diff --git a/pkgs/pkg-buttons-debug.js b/pkgs/pkg-buttons-debug.js index 86e62405..b1dd849d 100644 --- a/pkgs/pkg-buttons-debug.js +++ b/pkgs/pkg-buttons-debug.js @@ -1,6 +1,6 @@ /*! - * Ext JS Library 3.0.0 - * Copyright(c) 2006-2009 Ext JS, LLC + * Ext JS Library 3.2.2 + * Copyright(c) 2006-2010 Ext JS, Inc. * licensing@extjs.com * http://www.extjs.com/license */ @@ -16,7 +16,6 @@ *
  • b : Button
    This Button.
  • *
  • e : EventObject
    The click event.
  • * - * @cfg {Object} scope The scope (this reference) in which the handler is executed. Defaults to this Button. * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width). * See also {@link Ext.Panel}.{@link Ext.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 @@ -47,12 +46,6 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * @type Boolean */ pressed : false, - /** - * The Button's owner {@link Ext.Panel} (defaults to undefined, and is set automatically when - * the Button is added to a container). Read-only. - * @type Ext.Panel - * @property ownerCt - */ /** * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined) @@ -67,12 +60,12 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * @cfg {Boolean} enableToggle * True to enable pressed/not pressed toggling (defaults to false) */ - enableToggle: false, + enableToggle : false, /** * @cfg {Function} toggleHandler * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed: */ /** @@ -101,11 +94,12 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { type : 'button', // private - menuClassTarget: 'tr:nth(2)', + menuClassTarget : 'tr:nth(2)', /** * @cfg {String} clickEvent - * The type of event to map to the button's event handler (defaults to 'click') + * 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', @@ -127,7 +121,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * DOM structure created.

    *

    When a custom {@link #template} is used, you must ensure that this selector results in the selection of * a focussable element.

    - *

    Defaults to "button:first-child".

    + *

    Defaults to 'button:first-child'.

    */ buttonSelector : 'button:first-child', @@ -141,7 +135,13 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * *

    Defaults to 'small'.

    */ - scale: 'small', + scale : 'small', + + /** + * @cfg {Object} scope The scope (this reference) in which the + * {@link #handler} and {@link #toggleHandler} is + * executed. Defaults to this Button. + */ /** * @cfg {String} iconAlign @@ -185,6 +185,13 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * @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(){ Ext.Button.superclass.initComponent.call(this); @@ -258,28 +265,33 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { }, /** - *

    This method returns an object which provides substitution parameters for the {@link #template Template} used + *

    This method returns an Array which provides substitution parameters for the {@link #template Template} 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 Array containing the * following items:

    - * @return {Object} Substitution data for a Template. + * @return {Array} Substitution data for a Template. */ getTemplateArgs : function(){ - var cls = (this.cls || ''); - cls += (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon'; - if(this.pressed){ - cls += ' x-btn-pressed'; + return [this.type, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass(), this.cls, this.id]; + }, + + // private + setButtonClass : function(){ + if(this.useSetClass){ + if(!Ext.isEmpty(this.oldCls)){ + this.el.removeClass([this.oldCls, 'x-btn-pressed']); + } + this.oldCls = (this.iconCls || this.icon) ? (this.text ? 'x-btn-text-icon' : 'x-btn-icon') : 'x-btn-noicon'; + this.el.addClass([this.oldCls, this.pressed ? 'x-btn-pressed' : null]); } - return [this.text || ' ', this.type, this.iconCls || '', cls, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass()]; }, // protected @@ -293,11 +305,11 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { if(!Ext.Button.buttonTemplate){ // hideous table template Ext.Button.buttonTemplate = new Ext.Template( - '', + '
    ', '', - '', + '', '', - "
      
      
      
      
    "); + ''); Ext.Button.buttonTemplate.compile(); } this.template = Ext.Button.buttonTemplate; @@ -331,14 +343,10 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { // private initButtonEl : function(btn, btnEl){ this.el = btn; - - if(this.id){ - this.el.dom.id = this.el.id = this.id; - } - if(this.icon){ - btnEl.setStyle('background-image', 'url(' +this.icon +')'); - } - if(this.tabIndex !== undefined){ + this.setIcon(this.icon); + this.setText(this.text); + this.setIconClass(this.iconCls); + if(Ext.isDefined(this.tabIndex)){ btnEl.dom.tabIndex = this.tabIndex; } if(this.tooltip){ @@ -351,7 +359,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { mouseover: this.onMouseOver, mousedown: this.onMouseDown }); - + // new functionality for monitoring on the document level //this.mon(btn, 'mouseout', this.onMouseOut, this); } @@ -366,15 +374,18 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { if(this.repeat){ var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {}); - this.mon(repeater, 'click', this.onClick, this); + this.mon(repeater, 'click', this.onRepeatClick, this); + }else{ + this.mon(btn, this.clickEvent, this.onClick, this); } - - this.mon(btn, this.clickEvent, this.onClick, this); }, // private afterRender : function(){ Ext.Button.superclass.afterRender.call(this); + this.useSetClass = true; + this.setButtonClass(); + this.doc = Ext.getDoc(); this.doAutoWidth(); }, @@ -385,10 +396,12 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * @return {Ext.Button} this */ setIconClass : function(cls){ + this.iconCls = cls; if(this.el){ - this.btnEl.replaceClass(this.iconCls, cls); + this.btnEl.dom.className = ''; + this.btnEl.addClass(['x-btn-text', cls || '']); + this.setButtonClass(); } - this.iconCls = cls; return this; }, @@ -418,35 +431,40 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { } return this; }, - + // private - clearTip: function(){ + clearTip : function(){ if(Ext.isObject(this.tooltip)){ Ext.QuickTips.unregister(this.btnEl); } }, - + // private - beforeDestroy: function(){ + beforeDestroy : function(){ if(this.rendered){ this.clearTip(); } - Ext.destroy(this.menu, this.repeater); + if(this.menu && this.destroyMenu !== false) { + Ext.destroy(this.btnEl, this.menu); + } + Ext.destroy(this.repeater); }, // private onDestroy : function(){ - var doc = Ext.getDoc(); - doc.un('mouseover', this.monitorMouseOver, this); - doc.un('mouseup', this.onMouseUp, this); if(this.rendered){ + this.doc.un('mouseover', this.monitorMouseOver, this); + this.doc.un('mouseup', this.onMouseUp, this); + delete this.doc; + delete this.btnEl; Ext.ButtonToggleMgr.unregister(this); } + Ext.Button.superclass.onDestroy.call(this); }, // private doAutoWidth : function(){ - if(this.el && this.text && this.width === undefined){ + if(this.autoWidth !== false && this.el && this.text && this.width === undefined){ this.el.setWidth('auto'); if(Ext.isIE7 && Ext.isStrict){ var ib = this.btnEl; @@ -466,7 +484,8 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { /** * Assigns this Button's click handler * @param {Function} handler The function to call when the button is clicked - * @param {Object} scope (optional) Scope for the function passed in + * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. + * Defaults to this Button. * @return {Ext.Button} this */ setHandler : function(handler, scope){ @@ -483,12 +502,28 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { setText : function(text){ this.text = text; if(this.el){ - this.el.child('td.x-btn-mc ' + this.buttonSelector).update(text); + this.btnEl.update(text || ' '); + this.setButtonClass(); } this.doAutoWidth(); return this; }, + /** + * 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} this + */ + setIcon : function(icon){ + this.icon = icon; + if(this.el){ + this.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')' : ''); + this.setButtonClass(); + } + return this; + }, + /** * Gets the text for this Button * @return {String} The button text @@ -506,7 +541,9 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { toggle : function(state, suppressEvent){ state = state === undefined ? !this.pressed : !!state; if(state != this.pressed){ - this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed'); + if(this.rendered){ + this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed'); + } this.pressed = state; if(!suppressEvent){ this.fireEvent('toggle', this, state); @@ -518,13 +555,6 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { return this; }, - /** - * Focus the button - */ - focus : function(){ - this.btnEl.focus(); - }, - // private onDisable : function(){ this.onDisableChange(true); @@ -534,7 +564,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { onEnable : function(){ this.onDisableChange(false); }, - + onDisableChange : function(disabled){ if(this.el){ if(!Ext.isIE6 || !this.text){ @@ -553,6 +583,10 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { if(this.tooltip){ Ext.QuickTips.getQuickTip().cancelShow(this.btnEl); } + if(this.menu.isVisible()){ + this.menu.hide(); + } + this.menu.ownerCt = this; this.menu.show(this.el, this.menuAlign); } return this; @@ -562,7 +596,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * Hide this button's menu (if it has one) */ hideMenu : function(){ - if(this.menu){ + if(this.hasVisibleMenu()){ this.menu.hide(); } return this; @@ -573,7 +607,12 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { * @return {Boolean} */ hasVisibleMenu : function(){ - return this.menu && this.menu.isVisible(); + return this.menu && this.menu.ownerCt == this && this.menu.isVisible(); + }, + + // private + onRepeatClick : function(repeat, e){ + this.onClick(e); }, // private @@ -588,7 +627,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){ this.toggle(); } - if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){ + if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){ this.showMenu(); } this.fireEvent('click', this, e); @@ -616,7 +655,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { if(!internal){ this.el.addClass('x-btn-over'); if(!this.monitoringMouseOver){ - Ext.getDoc().on('mouseover', this.monitorMouseOver, this); + this.doc.on('mouseover', this.monitorMouseOver, this); this.monitoringMouseOver = true; } this.fireEvent('mouseover', this, e); @@ -631,7 +670,7 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { monitorMouseOver : function(e){ if(e.target != this.el.dom && !e.within(this.el)){ if(this.monitoringMouseOver){ - Ext.getDoc().un('mouseover', this.monitorMouseOver, this); + this.doc.un('mouseover', this.monitorMouseOver, this); this.monitoringMouseOver = false; } this.onMouseOut(e); @@ -647,6 +686,15 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { this.fireEvent('menutriggerout', this, this.menu, e); } }, + + focus : function() { + this.btnEl.focus(); + }, + + blur : function() { + this.btnEl.blur(); + }, + // private onFocus : function(e){ if(!this.disabled){ @@ -667,27 +715,33 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { onMouseDown : function(e){ if(!this.disabled && e.button === 0){ this.getClickEl(e).addClass('x-btn-click'); - Ext.getDoc().on('mouseup', this.onMouseUp, this); + this.doc.on('mouseup', this.onMouseUp, this); } }, // private onMouseUp : function(e){ if(e.button === 0){ this.getClickEl(e, true).removeClass('x-btn-click'); - Ext.getDoc().un('mouseup', this.onMouseUp, this); + this.doc.un('mouseup', this.onMouseUp, this); } }, // private onMenuShow : function(e){ - this.ignoreNextClick = 0; - this.el.addClass('x-btn-menu-active'); - this.fireEvent('menushow', this, this.menu); + if(this.menu.ownerCt == this){ + this.menu.ownerCt = this; + this.ignoreNextClick = 0; + this.el.addClass('x-btn-menu-active'); + this.fireEvent('menushow', this, this.menu); + } }, // private onMenuHide : function(e){ - this.el.removeClass('x-btn-menu-active'); - this.ignoreNextClick = this.restoreClick.defer(250, this); - this.fireEvent('menuhide', this, this.menu); + if(this.menu.ownerCt == this){ + this.el.removeClass('x-btn-menu-active'); + this.ignoreNextClick = this.restoreClick.defer(250, this); + this.fireEvent('menuhide', this, this.menu); + delete this.menu.ownerCt; + } }, // private @@ -695,11 +749,24 @@ Ext.Button = Ext.extend(Ext.BoxComponent, { this.ignoreNextClick = 0; } - - /** * @cfg {String} autoEl @hide */ + /** + * @cfg {String/Object} html @hide + */ + /** + * @cfg {String} contentEl @hide + */ + /** + * @cfg {Mixed} data @hide + */ + /** + * @cfg {Mixed} tpl @hide + */ + /** + * @cfg {String} tplWriteMode @hide + */ }); Ext.reg('button', Ext.Button); @@ -759,302 +826,312 @@ Ext.ButtonToggleMgr = function(){ return null; } }; -}();/** - * @class Ext.SplitButton - * @extends Ext.Button - * A split button that provides a built-in dropdown arrow that can fire an event separately from the default - * click event of the button. Typically this would be used to display a dropdown menu that provides additional - * options to the primary button action, but any custom handler can provide the arrowclick implementation. Example usage: - *
    
    -// display a dropdown menu:
    -new Ext.SplitButton({
    -	renderTo: 'button-ct', // the container id
    -   	text: 'Options',
    -   	handler: optionsHandler, // handle a click on the button itself
    -   	menu: new Ext.menu.Menu({
    -        items: [
    -        	// these items will render as dropdown menu items when the arrow is clicked:
    -	        {text: 'Item 1', handler: item1Handler},
    -	        {text: 'Item 2', handler: item2Handler}
    -        ]
    -   	})
    -});
    -
    -// Instead of showing a menu, you provide any type of custom
    -// functionality you want when the dropdown arrow is clicked:
    -new Ext.SplitButton({
    -	renderTo: 'button-ct',
    -   	text: 'Options',
    -   	handler: optionsHandler,
    -   	arrowHandler: myCustomHandler
    -});
    -
    - * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event) - * @cfg {String} arrowTooltip The title attribute of the arrow - * @constructor - * Create a new menu button - * @param {Object} config The config object - * @xtype splitbutton - */ -Ext.SplitButton = Ext.extend(Ext.Button, { - // private - arrowSelector : 'em', - split: true, - - // private - initComponent : function(){ - Ext.SplitButton.superclass.initComponent.call(this); - /** - * @event arrowclick - * Fires when this button's arrow is clicked - * @param {MenuButton} this - * @param {EventObject} e The click event - */ - this.addEvents("arrowclick"); - }, - - // private - onRender : function(){ - Ext.SplitButton.superclass.onRender.apply(this, arguments); - if(this.arrowTooltip){ - this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip; - } - }, - - /** - * Sets this button's arrow click handler. - * @param {Function} handler The function to call when the arrow is clicked - * @param {Object} scope (optional) Scope for the function passed above - */ - setArrowHandler : function(handler, scope){ - this.arrowHandler = handler; - this.scope = scope; - }, - - getMenuClass : function(){ - return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : ''); - }, - - isClickOnArrow : function(e){ - return this.arrowAlign != 'bottom' ? - e.getPageX() > this.el.child(this.buttonSelector).getRegion().right : - e.getPageY() > this.el.child(this.buttonSelector).getRegion().bottom; - }, - - // private - onClick : function(e, t){ - e.preventDefault(); - if(!this.disabled){ - if(this.isClickOnArrow(e)){ - if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){ - this.showMenu(); - } - this.fireEvent("arrowclick", this, e); - if(this.arrowHandler){ - this.arrowHandler.call(this.scope || this, this, e); - } - }else{ - if(this.enableToggle){ - this.toggle(); - } - this.fireEvent("click", this, e); - if(this.handler){ - this.handler.call(this.scope || this, this, e); - } - } - } - }, - - // private - isMenuTriggerOver : function(e){ - return this.menu && e.target.tagName == 'em'; - }, - - // private - isMenuTriggerOut : function(e, internal){ - return this.menu && e.target.tagName != 'em'; - } -}); - -Ext.reg('splitbutton', Ext.SplitButton);/** - * @class Ext.CycleButton - * @extends Ext.SplitButton - * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements. The button automatically - * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's - * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the - * button displays the dropdown menu just like a normal SplitButton. Example usage: - *
    
    -var btn = new Ext.CycleButton({
    -    showText: true,
    -    prependText: 'View as ',
    -    items: [{
    -        text:'text only',
    -        iconCls:'view-text',
    -        checked:true
    -    },{
    -        text:'HTML',
    -        iconCls:'view-html'
    -    }],
    -    changeHandler:function(btn, item){
    -        Ext.Msg.alert('Change View', item.text);
    -    }
    -});
    -
    - * @constructor - * Create a new split button - * @param {Object} config The config object - * @xtype cycle - */ -Ext.CycleButton = Ext.extend(Ext.SplitButton, { - /** - * @cfg {Array} items An array of {@link Ext.menu.CheckItem} config objects to be used when creating the - * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'}) - */ - /** - * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false) - */ - /** - * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the - * button's text (only applies when showText = true, defaults to '') - */ - /** - * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu - * item in the button's menu has changed. If this callback is not supplied, the SplitButton will instead - * fire the {@link #change} event on active item change. The changeHandler function will be called with the - * following argument list: (SplitButton this, Ext.menu.CheckItem item) - */ - /** - * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button. This - * icon will always be displayed regardless of which item is selected in the dropdown list. This overrides the - * default behavior of changing the button's icon to match the selected item's icon on change. - */ - /** - * @property menu - * @type Menu - * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices. - */ - - // private - getItemText : function(item){ - if(item && this.showText === true){ - var text = ''; - if(this.prependText){ - text += this.prependText; - } - text += item.text; - return text; - } - return undefined; - }, - - /** - * Sets the button's active menu item. - * @param {Ext.menu.CheckItem} item The item to activate - * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false) - */ - setActiveItem : function(item, suppressEvent){ - if(typeof item != 'object'){ - item = this.menu.items.get(item); - } - if(item){ - if(!this.rendered){ - this.text = this.getItemText(item); - this.iconCls = item.iconCls; - }else{ - var t = this.getItemText(item); - if(t){ - this.setText(t); - } - this.setIconClass(item.iconCls); - } - this.activeItem = item; - if(!item.checked){ - item.setChecked(true, true); - } - if(this.forceIcon){ - this.setIconClass(this.forceIcon); - } - if(!suppressEvent){ - this.fireEvent('change', this, item); - } - } - }, - - /** - * Gets the currently active menu item. - * @return {Ext.menu.CheckItem} The active item - */ - getActiveItem : function(){ - return this.activeItem; - }, - - // private - initComponent : function(){ - this.addEvents( - /** - * @event change - * Fires after the button's active menu item has changed. Note that if a {@link #changeHandler} function - * is set on this CycleButton, it will be called instead on active item change and this change event will - * not be fired. - * @param {Ext.CycleButton} this - * @param {Ext.menu.CheckItem} item The menu item that was selected - */ - "change" - ); - - if(this.changeHandler){ - this.on('change', this.changeHandler, this.scope||this); - delete this.changeHandler; - } - - this.itemCount = this.items.length; - - this.menu = {cls:'x-cycle-menu', items:[]}; - var checked; - for(var i = 0, len = this.itemCount; i < len; i++){ - var item = this.items[i]; - item.group = item.group || this.id; - item.itemIndex = i; - item.checkHandler = this.checkHandler; - item.scope = this; - item.checked = item.checked || false; - this.menu.items.push(item); - if(item.checked){ - checked = item; - } - } - this.setActiveItem(checked, true); - Ext.CycleButton.superclass.initComponent.call(this); - - this.on('click', this.toggleSelected, this); - }, - - // private - checkHandler : function(item, pressed){ - if(pressed){ - this.setActiveItem(item); - } - }, - - /** - * This is normally called internally on button click, but can be called externally to advance the button's - * active item programmatically to the next one in the menu. If the current item is the last one in the menu - * the active item will be set to the first item in the menu. - */ - toggleSelected : function(){ - this.menu.render(); - - var nextIdx, checkItem; - for (var i = 1; i < this.itemCount; i++) { - nextIdx = (this.activeItem.itemIndex + i) % this.itemCount; - // check the potential item - checkItem = this.menu.items.itemAt(nextIdx); - // if its not disabled then check it. - if (!checkItem.disabled) { - checkItem.setChecked(true); - break; - } - } - } -}); +}(); +/** + * @class Ext.SplitButton + * @extends Ext.Button + * A split button that provides a built-in dropdown arrow that can fire an event separately from the default + * click event of the button. Typically this would be used to display a dropdown menu that provides additional + * options to the primary button action, but any custom handler can provide the arrowclick implementation. Example usage: + *
    
    +// display a dropdown menu:
    +new Ext.SplitButton({
    +	renderTo: 'button-ct', // the container id
    +   	text: 'Options',
    +   	handler: optionsHandler, // handle a click on the button itself
    +   	menu: new Ext.menu.Menu({
    +        items: [
    +        	// these items will render as dropdown menu items when the arrow is clicked:
    +	        {text: 'Item 1', handler: item1Handler},
    +	        {text: 'Item 2', handler: item2Handler}
    +        ]
    +   	})
    +});
    +
    +// Instead of showing a menu, you provide any type of custom
    +// functionality you want when the dropdown arrow is clicked:
    +new Ext.SplitButton({
    +	renderTo: 'button-ct',
    +   	text: 'Options',
    +   	handler: optionsHandler,
    +   	arrowHandler: myCustomHandler
    +});
    +
    + * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event) + * @cfg {String} arrowTooltip The title attribute of the arrow + * @constructor + * Create a new menu button + * @param {Object} config The config object + * @xtype splitbutton + */ +Ext.SplitButton = Ext.extend(Ext.Button, { + // private + arrowSelector : 'em', + split: true, + + // private + initComponent : function(){ + Ext.SplitButton.superclass.initComponent.call(this); + /** + * @event arrowclick + * Fires when this button's arrow is clicked + * @param {MenuButton} this + * @param {EventObject} e The click event + */ + this.addEvents("arrowclick"); + }, + + // private + onRender : function(){ + Ext.SplitButton.superclass.onRender.apply(this, arguments); + if(this.arrowTooltip){ + this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip; + } + }, + + /** + * Sets this button's arrow click handler. + * @param {Function} handler The function to call when the arrow is clicked + * @param {Object} scope (optional) Scope for the function passed above + */ + setArrowHandler : function(handler, scope){ + this.arrowHandler = handler; + this.scope = scope; + }, + + getMenuClass : function(){ + return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : ''); + }, + + isClickOnArrow : function(e){ + if (this.arrowAlign != 'bottom') { + var visBtn = this.el.child('em.x-btn-split'); + var right = visBtn.getRegion().right - visBtn.getPadding('r'); + return e.getPageX() > right; + } else { + return e.getPageY() > this.btnEl.getRegion().bottom; + } + }, + + // private + onClick : function(e, t){ + e.preventDefault(); + if(!this.disabled){ + if(this.isClickOnArrow(e)){ + if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){ + this.showMenu(); + } + this.fireEvent("arrowclick", this, e); + if(this.arrowHandler){ + this.arrowHandler.call(this.scope || this, this, e); + } + }else{ + if(this.enableToggle){ + this.toggle(); + } + this.fireEvent("click", this, e); + if(this.handler){ + this.handler.call(this.scope || this, this, e); + } + } + } + }, + + // private + isMenuTriggerOver : function(e){ + return this.menu && e.target.tagName == this.arrowSelector; + }, + + // private + isMenuTriggerOut : function(e, internal){ + return this.menu && e.target.tagName != this.arrowSelector; + } +}); + +Ext.reg('splitbutton', Ext.SplitButton);/** + * @class Ext.CycleButton + * @extends Ext.SplitButton + * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements. The button automatically + * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's + * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the + * button displays the dropdown menu just like a normal SplitButton. Example usage: + *
    
    +var btn = new Ext.CycleButton({
    +    showText: true,
    +    prependText: 'View as ',
    +    items: [{
    +        text:'text only',
    +        iconCls:'view-text',
    +        checked:true
    +    },{
    +        text:'HTML',
    +        iconCls:'view-html'
    +    }],
    +    changeHandler:function(btn, item){
    +        Ext.Msg.alert('Change View', item.text);
    +    }
    +});
    +
    + * @constructor + * Create a new split button + * @param {Object} config The config object + * @xtype cycle + */ +Ext.CycleButton = Ext.extend(Ext.SplitButton, { + /** + * @cfg {Array} items An array of {@link Ext.menu.CheckItem} config objects to be used when creating the + * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'}) + */ + /** + * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false) + */ + /** + * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the + * button's text (only applies when showText = true, defaults to '') + */ + /** + * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu + * item in the button's menu has changed. If this callback is not supplied, the SplitButton will instead + * fire the {@link #change} event on active item change. The changeHandler function will be called with the + * following argument list: (SplitButton this, Ext.menu.CheckItem item) + */ + /** + * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button. This + * icon will always be displayed regardless of which item is selected in the dropdown list. This overrides the + * default behavior of changing the button's icon to match the selected item's icon on change. + */ + /** + * @property menu + * @type Menu + * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices. + */ + + // private + getItemText : function(item){ + if(item && this.showText === true){ + var text = ''; + if(this.prependText){ + text += this.prependText; + } + text += item.text; + return text; + } + return undefined; + }, + + /** + * Sets the button's active menu item. + * @param {Ext.menu.CheckItem} item The item to activate + * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false) + */ + setActiveItem : function(item, suppressEvent){ + if(!Ext.isObject(item)){ + item = this.menu.getComponent(item); + } + if(item){ + if(!this.rendered){ + this.text = this.getItemText(item); + this.iconCls = item.iconCls; + }else{ + var t = this.getItemText(item); + if(t){ + this.setText(t); + } + this.setIconClass(item.iconCls); + } + this.activeItem = item; + if(!item.checked){ + item.setChecked(true, false); + } + if(this.forceIcon){ + this.setIconClass(this.forceIcon); + } + if(!suppressEvent){ + this.fireEvent('change', this, item); + } + } + }, + + /** + * Gets the currently active menu item. + * @return {Ext.menu.CheckItem} The active item + */ + getActiveItem : function(){ + return this.activeItem; + }, + + // private + initComponent : function(){ + this.addEvents( + /** + * @event change + * Fires after the button's active menu item has changed. Note that if a {@link #changeHandler} function + * is set on this CycleButton, it will be called instead on active item change and this change event will + * not be fired. + * @param {Ext.CycleButton} this + * @param {Ext.menu.CheckItem} item The menu item that was selected + */ + "change" + ); + + if(this.changeHandler){ + this.on('change', this.changeHandler, this.scope||this); + delete this.changeHandler; + } + + this.itemCount = this.items.length; + + this.menu = {cls:'x-cycle-menu', items:[]}; + var checked = 0; + Ext.each(this.items, function(item, i){ + Ext.apply(item, { + group: item.group || this.id, + itemIndex: i, + checkHandler: this.checkHandler, + scope: this, + checked: item.checked || false + }); + this.menu.items.push(item); + if(item.checked){ + checked = i; + } + }, this); + Ext.CycleButton.superclass.initComponent.call(this); + this.on('click', this.toggleSelected, this); + this.setActiveItem(checked, true); + }, + + // private + checkHandler : function(item, pressed){ + if(pressed){ + this.setActiveItem(item); + } + }, + + /** + * This is normally called internally on button click, but can be called externally to advance the button's + * active item programmatically to the next one in the menu. If the current item is the last one in the menu + * the active item will be set to the first item in the menu. + */ + toggleSelected : function(){ + var m = this.menu; + m.render(); + // layout if we haven't before so the items are active + if(!m.hasLayout){ + m.doLayout(); + } + + var nextIdx, checkItem; + for (var i = 1; i < this.itemCount; i++) { + nextIdx = (this.activeItem.itemIndex + i) % this.itemCount; + // check the potential item + checkItem = m.items.itemAt(nextIdx); + // if its not disabled then check it. + if (!checkItem.disabled) { + checkItem.setChecked(true); + break; + } + } + } +}); Ext.reg('cycle', Ext.CycleButton); \ No newline at end of file