/* This file is part of Ext JS 3.4 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage 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. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact. Build date: 2013-04-03 15:07:25 */ /** * @class Ext.Button * @extends Ext.BoxComponent * 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:
b
: Buttone
: EventObject{@link Ext.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',
// private
menuClassTarget : 'tr:nth(2)',
/**
* @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} buttonSelector
* (Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the * 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'.
*/ buttonSelector : 'button:first-child', /** * @cfg {String} scale *(Optional) The size of the Button. Three values are allowed:
*Defaults to '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
* (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 {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(){ if(this.menu){ // If array of items, turn it into an object config so we // can set the ownerCt property in the config if (Ext.isArray(this.menu)){ this.menu = { items: this.menu }; } // An object config will work here, but an instance of a menu // will have already setup its ref's and have no effect if (Ext.isObject(this.menu)){ this.menu.ownerCt = this; } this.menu = Ext.menu.MenuMgr.get(this.menu); this.menu.ownerCt = undefined; } Ext.Button.superclass.initComponent.call(this); this.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(Ext.isString(this.toggleGroup)){ this.enableToggle = true; } }, /** *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:
'x-btn-arrow'
or 'x-btn-arrow-bottom'
or ''
)this
reference) in which the handler function is executed.
* Defaults to this Button.
* @return {Ext.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} this
*/
setText : function(text){
this.text = text;
if(this.el){
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
*/
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} this
*/
toggle : function(state, suppressEvent){
state = state === undefined ? !this.pressed : !!state;
if(state != this.pressed){
if(this.rendered){
this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
}
this.pressed = state;
if(!suppressEvent){
this.fireEvent('toggle', this, state);
if(this.toggleHandler){
this.toggleHandler.call(this.scope || this, this, state);
}
}
}
return this;
},
// private
onDisable : function(){
this.onDisableChange(true);
},
// private
onEnable : function(){
this.onDisableChange(false);
},
onDisableChange : function(disabled){
if(this.el){
if(!Ext.isIE6 || !this.text){
this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
}
this.el.dom.disabled = disabled;
}
this.disabled = disabled;
},
/**
* Show this button's menu (if it has one)
*/
showMenu : function(){
if(this.rendered && this.menu){
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;
},
/**
* 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(){
return this.menu && this.menu.ownerCt == this && this.menu.isVisible();
},
// private
onRepeatClick : function(repeat, e){
this.onClick(e);
},
// private
onClick : function(e){
if(e){
e.preventDefault();
}
if(e.button !== 0){
return;
}
if(!this.disabled){
this.doToggle();
if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){
this.showMenu();
}
this.fireEvent('click', this, e);
if(this.handler){
//this.el.removeClass('x-btn-over');
this.handler.call(this.scope || this, this, e);
}
}
},
// private
doToggle: function(){
if (this.enableToggle && (this.allowDepress !== false || !this.pressed)) {
this.toggle();
}
},
// private
isMenuTriggerOver : function(e, internal){
return this.menu && !internal;
},
// private
isMenuTriggerOut : function(e, internal){
return this.menu && !internal;
},
// private
onMouseOver : function(e){
if(!this.disabled){
var internal = e.within(this.el, true);
if(!internal){
this.el.addClass('x-btn-over');
if(!this.monitoringMouseOver){
this.doc.on('mouseover', this.monitorMouseOver, this);
this.monitoringMouseOver = true;
}
this.fireEvent('mouseover', this, e);
}
if(this.isMenuTriggerOver(e, internal)){
this.fireEvent('menutriggerover', this, this.menu, e);
}
}
},
// private
monitorMouseOver : function(e){
if(e.target != this.el.dom && !e.within(this.el)){
if(this.monitoringMouseOver){
this.doc.un('mouseover', this.monitorMouseOver, this);
this.monitoringMouseOver = false;
}
this.onMouseOut(e);
}
},
// private
onMouseOut : function(e){
var internal = e.within(this.el) && e.target != this.el.dom;
this.el.removeClass('x-btn-over');
this.fireEvent('mouseout', this, e);
if(this.isMenuTriggerOut(e, internal)){
this.fireEvent('menutriggerout', this, this.menu, e);
}
},
focus : function() {
this.btnEl.focus();
},
blur : function() {
this.btnEl.blur();
},
// private
onFocus : function(e){
if(!this.disabled){
this.el.addClass('x-btn-focus');
}
},
// private
onBlur : function(e){
this.el.removeClass('x-btn-focus');
},
// private
getClickEl : function(e, isUp){
return this.el;
},
// private
onMouseDown : function(e){
if(!this.disabled && e.button === 0){
this.getClickEl(e).addClass('x-btn-click');
this.doc.on('mouseup', this.onMouseUp, this);
}
},
// private
onMouseUp : function(e){
if(e.button === 0){
this.getClickEl(e, true).removeClass('x-btn-click');
this.doc.un('mouseup', this.onMouseUp, this);
}
},
// private
onMenuShow : function(e){
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){
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
restoreClick : function(){
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);
// Private utility class used by Button
Ext.ButtonToggleMgr = function(){
var groups = {};
function toggleGroup(btn, state){
if(state){
var g = groups[btn.toggleGroup];
for(var 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];
if(g){
for(var i = 0, len = g.length; i < len; i++){
if(g[i].pressed === true){
return g[i];
}
}
}
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){
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{
this.doToggle();
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, suppressEvent);
}
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);