/**
* Base class for all Ext components.
*
* The Component base class has built-in support for basic hide/show and enable/disable and size control behavior.
*
* ## xtypes
*
* Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the xtype
* like {@link #getXType} and {@link #isXType}. See the [Component Guide][1] for more information on xtypes and the
* Component hierarchy.
*
* ## Finding components
*
* All Components are registered with the {@link Ext.ComponentManager} on construction so that they can be referenced at
* any time via {@link Ext#getCmp Ext.getCmp}, passing the {@link #id}.
*
* Additionally the {@link Ext.ComponentQuery} provides a CSS-selectors-like way to look up components by their xtype
* and many other attributes. For example the following code will find all textfield components inside component with
* `id: 'myform'`:
*
* Ext.ComponentQuery.query('#myform textfield');
*
* ## Extending Ext.Component
*
* All subclasses of Component may participate in the automated Ext component
* lifecycle of creation, rendering and destruction which is provided by the {@link Ext.container.Container Container}
* class. Components may be added to a Container through the {@link Ext.container.Container#cfg-items items} config option
* at the time the Container is created, or they may be added dynamically via the
* {@link Ext.container.Container#method-add add} method.
*
* All user-developed visual widgets that are required to participate in automated lifecycle and size management should
* subclass Component.
*
* See the Creating new UI controls chapter in [Component Guide][1] for details on how and to either extend
* or augment Ext JS base classes to create custom Components.
*
* ## The Ext.Component class by itself
*
* Usually one doesn't need to instantiate the Ext.Component class. There are subclasses which implement
* specialized use cases, covering most application needs. However it is possible to instantiate a base
* Component, and it can be rendered to document, or handled by layouts as the child item of a Container:
*
* @example
* Ext.create('Ext.Component', {
* html: 'Hello world!',
* width: 300,
* height: 200,
* padding: 20,
* style: {
* color: '#FFFFFF',
* backgroundColor:'#000000'
* },
* renderTo: Ext.getBody()
* });
*
* The Component above creates its encapsulating `div` upon render, and use the configured HTML as content. More complex
* internal structure may be created using the {@link #renderTpl} configuration, although to display database-derived
* mass data, it is recommended that an ExtJS data-backed Component such as a {@link Ext.view.View View},
* {@link Ext.grid.Panel GridPanel}, or {@link Ext.tree.Panel TreePanel} be used.
*
* [1]: #!/guide/components
*/
Ext.define('Ext.Component', {
/* Begin Definitions */
alias: ['widget.component', 'widget.box'],
extend: 'Ext.AbstractComponent',
requires: [
'Ext.util.DelayedTask'
],
uses: [
'Ext.Layer',
'Ext.resizer.Resizer',
'Ext.util.ComponentDragger'
],
mixins: {
floating: 'Ext.util.Floating'
},
statics: {
// Collapse/expand directions
DIRECTION_TOP: 'top',
DIRECTION_RIGHT: 'right',
DIRECTION_BOTTOM: 'bottom',
DIRECTION_LEFT: 'left',
VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,
// RegExp whih specifies characters in an xtype which must be translated to '-' when generating auto IDs.
// This includes dot, comma and whitespace
INVALID_ID_CHARS_Re: /[\.,\s]/g
},
/* End Definitions */
/**
* @cfg {Boolean/Object} resizable
* Specify as `true` to apply a {@link Ext.resizer.Resizer Resizer} to this Component after rendering.
*
* May also be specified as a config object to be passed to the constructor of {@link Ext.resizer.Resizer Resizer}
* to override any defaults. By default the Component passes its minimum and maximum size, and uses
* `{@link Ext.resizer.Resizer#dynamic}: false`
*/
/**
* @cfg {String} resizeHandles
* A valid {@link Ext.resizer.Resizer} handles config string. Only applies when resizable = true.
*/
resizeHandles: 'all',
/**
* @cfg {Boolean} [autoScroll=false]
* `true` to use overflow:'auto' on the components layout element and show scroll bars automatically when necessary,
* `false` to clip any overflowing content.
* This should not be combined with {@link #overflowX} or {@link #overflowY}.
*/
/**
* @cfg {String} overflowX
* Possible values are:
* * `'auto'` to enable automatic horizontal scrollbar (overflow-x: 'auto').
* * `'scroll'` to always enable horizontal scrollbar (overflow-x: 'scroll').
* The default is overflow-x: 'hidden'. This should not be combined with {@link #autoScroll}.
*/
/**
* @cfg {String} overflowY
* Possible values are:
* * `'auto'` to enable automatic vertical scrollbar (overflow-y: 'auto').
* * `'scroll'` to always enable vertical scrollbar (overflow-y: 'scroll').
* The default is overflow-y: 'hidden'. This should not be combined with {@link #autoScroll}.
*/
/**
* @cfg {Boolean} floating
* Specify as true to float the Component outside of the document flow using CSS absolute positioning.
*
* Components such as {@link Ext.window.Window Window}s and {@link Ext.menu.Menu Menu}s are floating by default.
*
* Floating Components that are programatically {@link Ext.Component#method-render rendered} will register
* themselves with the global {@link Ext.WindowManager ZIndexManager}
*
* ### Floating Components as child items of a Container
*
* A floating Component may be used as a child item of a Container. This just allows the floating Component to seek
* a ZIndexManager by examining the ownerCt chain.
*
* When configured as floating, Components acquire, at render time, a {@link Ext.ZIndexManager ZIndexManager} which
* manages a stack of related floating Components. The ZIndexManager brings a single floating Component to the top
* of its stack when the Component's {@link #toFront} method is called.
*
* The ZIndexManager is found by traversing up the {@link #ownerCt} chain to find an ancestor which itself is
* floating. This is so that descendant floating Components of floating _Containers_ (Such as a ComboBox dropdown
* within a Window) can have its zIndex managed relative to any siblings, but always **above** that floating
* ancestor Container.
*
* If no floating ancestor is found, a floating Component registers itself with the default {@link Ext.WindowManager
* ZIndexManager}.
*
* Floating components _do not participate in the Container's layout_. Because of this, they are not rendered until
* you explicitly {@link #method-show} them.
*
* After rendering, the ownerCt reference is deleted, and the {@link #floatParent} property is set to the found
* floating ancestor Container. If no floating ancestor Container was found the {@link #floatParent} property will
* not be set.
*/
floating: false,
/**
* @cfg {Boolean} toFrontOnShow
* True to automatically call {@link #toFront} when the {@link #method-show} method is called on an already visible,
* floating component.
*/
toFrontOnShow: true,
/**
* @property {Ext.ZIndexManager} zIndexManager
* Only present for {@link #floating} Components after they have been rendered.
*
* A reference to the ZIndexManager which is managing this Component's z-index.
*
* The {@link Ext.ZIndexManager ZIndexManager} maintains a stack of floating Component z-indices, and also provides
* a single modal mask which is insert just beneath the topmost visible modal floating Component.
*
* Floating Components may be {@link #toFront brought to the front} or {@link #toBack sent to the back} of the
* z-index stack.
*
* This defaults to the global {@link Ext.WindowManager ZIndexManager} for floating Components that are
* programatically {@link Ext.Component#method-render rendered}.
*
* For {@link #floating} Components which are added to a Container, the ZIndexManager is acquired from the first
* ancestor Container found which is floating. If no floating ancestor is found, the global {@link Ext.WindowManager ZIndexManager} is
* used.
*
* See {@link #floating} and {@link #zIndexParent}
* @readonly
*/
/**
* @property {Ext.Container} floatParent
* Only present for {@link #floating} Components which were inserted as child items of Containers.
*
* Floating Components that are programatically {@link Ext.Component#method-render rendered} will not have a `floatParent`
* property.
*
* For {@link #floating} Components which are child items of a Container, the floatParent will be the owning Container.
*
* For example, the dropdown {@link Ext.view.BoundList BoundList} of a ComboBox which is in a Window will have the
* Window as its `floatParent`
*
* See {@link #floating} and {@link #zIndexManager}
* @readonly
*/
/**
* @property {Ext.Container} zIndexParent
* Only present for {@link #floating} Components which were inserted as child items of Containers, and which have a floating
* Container in their containment ancestry.
*
* For {@link #floating} Components which are child items of a Container, the zIndexParent will be a floating
* ancestor Container which is responsible for the base z-index value of all its floating descendants. It provides
* a {@link Ext.ZIndexManager ZIndexManager} which provides z-indexing services for all its descendant floating
* Components.
*
* Floating Components that are programatically {@link Ext.Component#method-render rendered} will not have a `zIndexParent`
* property.
*
* For example, the dropdown {@link Ext.view.BoundList BoundList} of a ComboBox which is in a Window will have the
* Window as its `zIndexParent`, and will always show above that Window, wherever the Window is placed in the z-index stack.
*
* See {@link #floating} and {@link #zIndexManager}
* @readonly
*/
/**
* @cfg {Boolean/Object} [draggable=false]
* Specify as true to make a {@link #floating} Component draggable using the Component's encapsulating element as
* the drag handle.
*
* This may also be specified as a config object for the {@link Ext.util.ComponentDragger ComponentDragger} which is
* instantiated to perform dragging.
*
* For example to create a Component which may only be dragged around using a certain internal element as the drag
* handle, use the delegate option:
*
* new Ext.Component({
* constrain: true,
* floating: true,
* style: {
* backgroundColor: '#fff',
* border: '1px solid black'
* },
* html: '
The title
The content
',
* draggable: {
* delegate: 'h1'
* }
* }).show();
*/
/**
* @cfg {Boolean} [formBind=false]
* When inside FormPanel, any component configured with `formBind: true` will
* be enabled/disabled depending on the validity state of the form.
* See {@link Ext.form.Panel} for more information and example.
*/
/**
* @cfg {Number/String} [columnWidth=undefined]
* Defines the column width inside {@link Ext.layout.container.Column column layout}.
*
* Can be specified as a number or as a percentage.
*/
/**
* @cfg {String} [region=undefined]
* Defines the region inside {@link Ext.layout.container.Border border layout}.
*
* Possible values:
*
* - center
* - north
* - south
* - east
* - west
*/
hideMode: 'display',
bubbleEvents: [],
monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
defaultComponentLayoutType: 'autocomponent',
//renderTpl: new Ext.XTemplate(
// ' {uiBase}-{ui}" style="{style}">
', {
// compiled: true,
// disableFormats: true
// }
//),
/**
* Creates new Component.
* @param {Ext.Element/String/Object} config The configuration options may be specified as either:
*
* - **an element** : it is set as the internal element and its id used as the component id
* - **a string** : it is assumed to be the id of an existing element and is used as the component id
* - **anything else** : it is assumed to be a standard config object and is applied to the component
*/
constructor: function(config) {
var me = this;
config = config || {};
if (config.initialConfig) {
// Being initialized from an Ext.Action instance...
if (config.isAction) {
me.baseAction = config;
}
config = config.initialConfig;
// component cloning / action set up
}
else if (config.tagName || config.dom || Ext.isString(config)) {
// element object
config = {
applyTo: config,
id: config.id || config
};
}
me.callParent([config]);
// If we were configured from an instance of Ext.Action, (or configured with a baseAction option),
// register this Component as one of its items
if (me.baseAction){
me.baseAction.addComponent(me);
}
},
/**
* The initComponent template method is an important initialization step for a Component. It is intended to be
* implemented by each subclass of Ext.Component to provide any needed constructor logic. The
* initComponent method of the class being created is called first, with each initComponent method
* up the hierarchy to Ext.Component being called thereafter. This makes it easy to implement and,
* if needed, override the constructor logic of the Component at any step in the hierarchy.
*
* The initComponent method **must** contain a call to {@link Ext.Base#callParent callParent} in order
* to ensure that the parent class' initComponent method is also called.
*
* All config options passed to the constructor are applied to `this` before initComponent is called,
* so you can simply access them with `this.someOption`.
*
* The following example demonstrates using a dynamic string for the text of a button at the time of
* instantiation of the class.
*
* Ext.define('DynamicButtonText', {
* extend: 'Ext.button.Button',
*
* initComponent: function() {
* this.text = new Date();
* this.renderTo = Ext.getBody();
* this.callParent();
* }
* });
*
* Ext.onReady(function() {
* Ext.create('DynamicButtonText');
* });
*
* @template
* @protected
*/
initComponent: function() {
var me = this;
me.callParent();
if (me.listeners) {
me.on(me.listeners);
me.listeners = null; //change the value to remove any on prototype
}
me.enableBubble(me.bubbleEvents);
me.mons = [];
},
// private
afterRender: function() {
var me = this;
me.callParent();
if (!(me.x && me.y) && (me.pageX || me.pageY)) {
me.setPagePosition(me.pageX, me.pageY);
}
},
/**
* Sets the overflow on the content element of the component.
* @param {Boolean} scroll True to allow the Component to auto scroll.
* @return {Ext.Component} this
*/
setAutoScroll : function(scroll) {
var me = this;
me.autoScroll = !!scroll;
// Scrolling styles must be applied to Component's main element.
// Layouts which use an innerCt (Box layout), shrinkwrap the innerCt round overflowing content,
// so the innerCt must be scrolled by the container, it does not scroll content.
if (me.rendered) {
me.getTargetEl().setStyle(me.getOverflowStyle());
}
me.updateLayout();
return me;
},
/**
* Sets the overflow x/y on the content element of the component. The x/y overflow
* values can be any valid CSS overflow (e.g., 'auto' or 'scroll'). By default, the
* value is 'hidden'. Passing null for one of the values will erase the inline style.
* Passing `undefined` will preserve the current value.
*
* @param {String} overflowX The overflow-x value.
* @param {String} overflowY The overflow-y value.
* @return {Ext.Component} this
*/
setOverflowXY: function(overflowX, overflowY) {
var me = this,
argCount = arguments.length;
if (argCount) {
me.overflowX = overflowX || '';
if (argCount > 1) {
me.overflowY = overflowY || '';
}
}
// Scrolling styles must be applied to Component's main element.
// Layouts which use an innerCt (Box layout), shrinkwrap the innerCt round overflowing content,
// so the innerCt must be scrolled by the container, it does not scroll content.
if (me.rendered) {
me.getTargetEl().setStyle(me.getOverflowStyle());
}
me.updateLayout();
return me;
},
beforeRender: function () {
var me = this,
floating = me.floating,
cls;
if (floating) {
me.addCls(Ext.baseCSSPrefix + 'layer');
cls = floating.cls;
if (cls) {
me.addCls(cls);
}
}
return me.callParent();
},
afterComponentLayout: function(){
this.callParent(arguments);
if (this.floating) {
this.onAfterFloatLayout();
}
},
// private
makeFloating : function (dom) {
this.mixins.floating.constructor.call(this, dom);
},
wrapPrimaryEl: function (dom) {
if (this.floating) {
this.makeFloating(dom);
} else {
this.callParent(arguments);
}
},
initResizable: function(resizable) {
var me = this;
resizable = Ext.apply({
target: me,
dynamic: false,
constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : null),
handles: me.resizeHandles
}, resizable);
resizable.target = me;
me.resizer = new Ext.resizer.Resizer(resizable);
},
getDragEl: function() {
return this.el;
},
initDraggable: function() {
var me = this,
// If we are resizable, and the resizer had to wrap this Component's el (eg an Img)
// Then we have to create a pseudo-Component out of the resizer to drag that,
// otherwise, we just drag this Component
dragTarget = (me.resizer && me.resizer.el !== me.el) ? me.resizerComponent = new Ext.Component({
el: me.resizer.el,
rendered: true,
container: me.container
}) : me,
ddConfig = Ext.applyIf({
el: dragTarget.getDragEl(),
constrainTo: me.constrain ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.el.getScopeParent())) : undefined
}, me.draggable);
// Add extra configs if Component is specified to be constrained
if (me.constrain || me.constrainDelegate) {
ddConfig.constrain = me.constrain;
ddConfig.constrainDelegate = me.constrainDelegate;
}
me.dd = new Ext.util.ComponentDragger(dragTarget, ddConfig);
},
/**
* Scrolls this Component's {@link #getTargetEl target element} by the passed delta values, optionally animating.
*
* All of the following are equivalent:
*
* comp.scrollBy(10, 10, true);
* comp.scrollBy([10, 10], true);
* comp.scrollBy({ x: 10, y: 10 }, true);
*
* @param {Number/Number[]/Object} deltaX Either the x delta, an Array specifying x and y deltas or
* an object with "x" and "y" properties.
* @param {Number/Boolean/Object} deltaY Either the y delta, or an animate flag or config object.
* @param {Boolean/Object} animate Animate flag/config object if the delta values were passed separately.
*/
scrollBy: function(deltaX, deltaY, animate) {
var el;
if ((el = this.getTargetEl()) && el.dom) {
el.scrollBy.apply(el, arguments);
}
},
/**
* This method allows you to show or hide a LoadMask on top of this component.
*
* @param {Boolean/Object/String} load True to show the default LoadMask, a config object that will be passed to the
* LoadMask constructor, or a message String to show. False to hide the current LoadMask.
* @param {Boolean} [targetEl=false] True to mask the targetEl of this Component instead of the `this.el`. For example,
* setting this to true on a Panel will cause only the body to be masked.
* @return {Ext.LoadMask} The LoadMask instance that has just been shown.
*/
setLoading : function(load, targetEl) {
var me = this,
config;
if (me.rendered) {
Ext.destroy(me.loadMask);
me.loadMask = null;
if (load !== false && !me.collapsed) {
if (Ext.isObject(load)) {
config = Ext.apply({}, load);
} else if (Ext.isString(load)) {
config = {msg: load};
} else {
config = {};
}
if (targetEl) {
Ext.applyIf(config, {
useTargetEl: true
});
}
me.loadMask = new Ext.LoadMask(me, config);
me.loadMask.show();
}
}
return me.loadMask;
},
beforeSetPosition: function () {
var me = this,
pos = me.callParent(arguments), // pass all args on for signature decoding
adj;
if (pos) {
adj = me.adjustPosition(pos.x, pos.y);
pos.x = adj.x;
pos.y = adj.y;
}
return pos || null;
},
afterSetPosition: function(ax, ay) {
this.onPosition(ax, ay);
this.fireEvent('move', this, ax, ay);
},
/**
* Displays component at specific xy position.
* A floating component (like a menu) is positioned relative to its ownerCt if any.
* Useful for popping up a context menu:
*
* listeners: {
* itemcontextmenu: function(view, record, item, index, event, options) {
* Ext.create('Ext.menu.Menu', {
* width: 100,
* height: 100,
* margin: '0 0 10 0',
* items: [{
* text: 'regular item 1'
* },{
* text: 'regular item 2'
* },{
* text: 'regular item 3'
* }]
* }).showAt(event.getXY());
* }
* }
*
* @param {Number} x The new x position
* @param {Number} y The new y position
* @param {Boolean/Object} [animate] True to animate the Component into its new position. You may also pass an
* animation configuration.
*/
showAt: function(x, y, animate) {
var me = this;
if (!me.rendered && (me.autoRender || me.floating)) {
me.doAutoRender();
// forcibly set hidden here, since we still want the initial beforeshow/show event to fire
me.hidden = true;
}
if (me.floating) {
me.setPosition(x, y, animate);
} else {
me.setPagePosition(x, y, animate);
}
me.show();
},
/**
* Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
* This method fires the {@link #event-move} event.
* @param {Number} x The new x position
* @param {Number} y The new y position
* @param {Boolean/Object} [animate] True to animate the Component into its new position. You may also pass an
* animation configuration.
* @return {Ext.Component} this
*/
setPagePosition: function(x, y, animate) {
var me = this,
p,
floatParentBox;
if (Ext.isArray(x)) {
y = x[1];
x = x[0];
}
me.pageX = x;
me.pageY = y;
if (me.floating) {
// Floating Components which are registered with a Container have to have their x and y properties made relative
if (me.isContainedFloater()) {
floatParentBox = me.floatParent.getTargetEl().getViewRegion();
if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) {
x -= floatParentBox.left;
}
if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) {
y -= floatParentBox.top;
}
} else {
p = me.el.translatePoints(x, y);
x = p.left;
y = p.top;
}
me.setPosition(x, y, animate);
} else {
p = me.el.translatePoints(x, y);
me.setPosition(p.left, p.top, animate);
}
return me;
},
// Utility method to determine if a Component is floating, and has an owning Container whose coordinate system
// it must be positioned in when using setPosition.
isContainedFloater: function() {
return (this.floating && this.floatParent);
},
/**
* Gets the current box measurements of the component's underlying element.
* @param {Boolean} [local=false] If true the element's left and top are returned instead of page XY.
* @return {Object} box An object in the format {x, y, width, height}
*/
getBox : function(local){
var pos = local ? this.getPosition(local) : this.el.getXY(),
size = this.getSize();
size.x = pos[0];
size.y = pos[1];
return size;
},
/**
* Sets the current box measurements of the component's underlying element.
* @param {Object} box An object in the format {x, y, width, height}
* @return {Ext.Component} this
*/
updateBox : function(box){
this.setSize(box.width, box.height);
this.setPagePosition(box.x, box.y);
return this;
},
// Include margins
getOuterSize: function() {
var el = this.el;
return {
width: el.getWidth() + el.getMargin('lr'),
height: el.getHeight() + el.getMargin('tb')
};
},
// private
adjustPosition: function(x, y) {
var me = this,
floatParentBox;
// Floating Components being positioned in their ownerCt have to be made absolute.
if (me.isContainedFloater()) {
floatParentBox = me.floatParent.getTargetEl().getViewRegion();
x += floatParentBox.left;
y += floatParentBox.top;
}
return {
x: x,
y: y
};
},
/**
* Gets the current XY position of the component's underlying element.
* @param {Boolean} [local=false] If true the element's left and top are returned instead of page XY.
* @return {Number[]} The XY position of the element (e.g., [100, 200])
*/
getPosition: function(local) {
var me = this,
el = me.el,
xy,
isContainedFloater = me.isContainedFloater(),
floatParentBox;
// Local position for non-floaters means element's local position
if ((local === true) && !isContainedFloater) {
return [el.getLocalX(), el.getLocalY()];
}
xy = me.el.getXY();
// Local position for floaters means position relative to the container's target element
if ((local === true) && isContainedFloater) {
floatParentBox = me.floatParent.getTargetEl().getViewRegion();
xy[0] -= floatParentBox.left;
xy[1] -= floatParentBox.top;
}
return xy;
},
getId: function() {
var me = this,
xtype;
if (!me.id) {
xtype = me.getXType();
if (xtype) {
xtype = xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-');
} else {
xtype = Ext.name.toLowerCase() + '-comp';
}
me.id = xtype + '-' + me.getAutoId();
}
return me.id;
},
/**
* Shows this Component, rendering it first if {@link #autoRender} or {@link #floating} are `true`.
*
* After being shown, a {@link #floating} Component (such as a {@link Ext.window.Window}), is activated it and
* brought to the front of its {@link #zIndexManager z-index stack}.
*
* @param {String/Ext.Element} [animateTarget=null] **only valid for {@link #floating} Components such as {@link
* Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have been configured
* with `floating: true`.** The target from which the Component should animate from while opening.
* @param {Function} [callback] A callback function to call after the Component is displayed.
* Only necessary if animation was specified.
* @param {Object} [scope] The scope (`this` reference) in which the callback is executed.
* Defaults to this Component.
* @return {Ext.Component} this
*/
show: function(animateTarget, cb, scope) {
var me = this,
rendered = me.rendered;
if (rendered && me.isVisible()) {
if (me.toFrontOnShow && me.floating) {
me.toFront();
}
} else {
if (me.fireEvent('beforeshow', me) !== false) {
// Render on first show if there is an autoRender config, or if this is a floater (Window, Menu, BoundList etc).
me.hidden = false;
if (!rendered && (me.autoRender || me.floating)) {
me.doAutoRender();
rendered = me.rendered;
}
if (rendered) {
me.beforeShow();
me.onShow.apply(me, arguments);
me.afterShow.apply(me, arguments);
}
} else {
me.onShowVeto();
}
}
return me;
},
onShowVeto: Ext.emptyFn,
/**
* Invoked before the Component is shown.
*
* @method
* @template
* @protected
*/
beforeShow: Ext.emptyFn,
/**
* Allows addition of behavior to the show operation. After
* calling the superclass's onShow, the Component will be visible.
*
* Override in subclasses where more complex behaviour is needed.
*
* Gets passed the same parameters as #show.
*
* @param {String/Ext.Element} [animateTarget]
* @param {Function} [callback]
* @param {Object} [scope]
*
* @template
* @protected
*/
onShow: function() {
var me = this;
me.el.show();
me.callParent(arguments);
// Constraining/containing element may have changed size while this Component was hidden
if (me.floating) {
if (me.maximized) {
me.fitContainer();
}
else if (me.constrain) {
me.doConstrain();
}
}
},
/**
* Invoked after the Component is shown (after #onShow is called).
*
* Gets passed the same parameters as #show.
*
* @param {String/Ext.Element} [animateTarget]
* @param {Function} [callback]
* @param {Object} [scope]
*
* @template
* @protected
*/
afterShow: function(animateTarget, cb, scope) {
var me = this,
fromBox,
toBox,
ghostPanel;
// Default to configured animate target if none passed
animateTarget = animateTarget || me.animateTarget;
// Need to be able to ghost the Component
if (!me.ghost) {
animateTarget = null;
}
// If we're animating, kick of an animation of the ghost from the target to the *Element* current box
if (animateTarget) {
animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
toBox = me.el.getBox();
fromBox = animateTarget.getBox();
me.el.addCls(Ext.baseCSSPrefix + 'hide-offsets');
ghostPanel = me.ghost();
ghostPanel.el.stopAnimation();
// Shunting it offscreen immediately, *before* the Animation class grabs it ensure no flicker.
ghostPanel.el.setX(-10000);
ghostPanel.el.animate({
from: fromBox,
to: toBox,
listeners: {
afteranimate: function() {
delete ghostPanel.componentLayout.lastComponentSize;
me.unghost();
me.el.removeCls(Ext.baseCSSPrefix + 'hide-offsets');
me.onShowComplete(cb, scope);
}
}
});
}
else {
me.onShowComplete(cb, scope);
}
},
/**
* Invoked after the #afterShow method is complete.
*
* Gets passed the same `callback` and `scope` parameters that #afterShow received.
*
* @param {Function} [callback]
* @param {Object} [scope]
*
* @template
* @protected
*/
onShowComplete: function(cb, scope) {
var me = this;
if (me.floating) {
me.toFront();
me.onFloatShow();
}
Ext.callback(cb, scope || me);
me.fireEvent('show', me);
delete me.hiddenByLayout;
},
/**
* Hides this Component, setting it to invisible using the configured {@link #hideMode}.
* @param {String/Ext.Element/Ext.Component} [animateTarget=null] **only valid for {@link #floating} Components
* such as {@link Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have
* been configured with `floating: true`.**. The target to which the Component should animate while hiding.
* @param {Function} [callback] A callback function to call after the Component is hidden.
* @param {Object} [scope] The scope (`this` reference) in which the callback is executed.
* Defaults to this Component.
* @return {Ext.Component} this
*/
hide: function() {
var me = this;
// Clear the flag which is set if a floatParent was hidden while this is visible.
// If a hide operation was subsequently called, that pending show must be hidden.
me.showOnParentShow = false;
if (!(me.rendered && !me.isVisible()) && me.fireEvent('beforehide', me) !== false) {
me.hidden = true;
if (me.rendered) {
me.onHide.apply(me, arguments);
}
}
return me;
},
/**
* Possibly animates down to a target element.
*
* Allows addition of behavior to the hide operation. After
* calling the superclass’s onHide, the Component will be hidden.
*
* Gets passed the same parameters as #hide.
*
* @param {String/Ext.Element/Ext.Component} [animateTarget]
* @param {Function} [callback]
* @param {Object} [scope]
*
* @template
* @protected
*/
onHide: function(animateTarget, cb, scope) {
var me = this,
ghostPanel,
toBox;
// Default to configured animate target if none passed
animateTarget = animateTarget || me.animateTarget;
// Need to be able to ghost the Component
if (!me.ghost) {
animateTarget = null;
}
// If we're animating, kick off an animation of the ghost down to the target
if (animateTarget) {
animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
ghostPanel = me.ghost();
ghostPanel.el.stopAnimation();
toBox = animateTarget.getBox();
toBox.width += 'px';
toBox.height += 'px';
ghostPanel.el.animate({
to: toBox,
listeners: {
afteranimate: function() {
delete ghostPanel.componentLayout.lastComponentSize;
ghostPanel.el.hide();
me.afterHide(cb, scope);
}
}
});
}
me.el.hide();
if (!animateTarget) {
me.afterHide(cb, scope);
}
},
/**
* Invoked after the Component has been hidden.
*
* Gets passed the same `callback` and `scope` parameters that #onHide received.
*
* @param {Function} [callback]
* @param {Object} [scope]
*
* @template
* @protected
*/
afterHide: function(cb, scope) {
var me = this;
delete me.hiddenByLayout;
// we are the back-end method of onHide at this level, but our call to our parent
// may need to be async... so callParent won't quite work here...
Ext.AbstractComponent.prototype.onHide.call(this);
Ext.callback(cb, scope || me);
me.fireEvent('hide', me);
},
/**
* Allows addition of behavior to the destroy operation.
* After calling the superclass’s onDestroy, the Component will be destroyed.
*
* @template
* @protected
*/
onDestroy: function() {
var me = this;
// Ensure that any ancillary components are destroyed.
if (me.rendered) {
Ext.destroy(
me.proxy,
me.proxyWrap,
me.resizer,
me.resizerComponent
);
}
delete me.focusTask;
me.callParent();
},
deleteMembers: function() {
var args = arguments,
len = args.length,
i = 0;
for (; i < len; ++i) {
delete this[args[i]];
}
},
/**
* Try to focus this component.
* @param {Boolean} [selectText] If applicable, true to also select the text in this component
* @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds).
* @return {Ext.Component} The focused Component. Usually this
Component. Some Containers may
* delegate focus to a descendant Component ({@link Ext.window.Window Window}s can do this through their
* {@link Ext.window.Window#defaultFocus defaultFocus} config option.
*/
focus: function(selectText, delay) {
var me = this,
focusEl,
focusElDom,
containerScrollTop;
// If delay is wanted, queue a call to this function.
if (delay) {
if (!me.focusTask) {
me.focusTask = new Ext.util.DelayedTask(me.focus);
}
me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
return me;
}
if (me.rendered && !me.isDestroyed && me.isVisible(true) && (focusEl = me.getFocusEl())) {
// getFocusEl might return a Component if a Container wishes to delegate focus to a descendant.
// Window can do this via its defaultFocus configuration which can reference a Button.
if (focusEl.isComponent) {
return focusEl.focus(selectText, delay);
}
// If it was an Element with a dom property
if ((focusElDom = focusEl.dom)) {
// Not a natural focus holding element, add a tab index to make it programatically focusable.
if (focusEl.needsTabIndex()) {
focusElDom.tabIndex = -1;
}
if (me.floating) {
containerScrollTop = me.container.dom.scrollTop;
}
// Focus the element.
// The focusEl has a DOM focus listener on it which invokes the Component's onFocus method
// to perform Component-specific focus processing
focusEl.focus();
if (selectText === true) {
focusElDom.select();
}
}
// Focusing a floating Component brings it to the front of its stack.
// this is performed by its zIndexManager. Pass preventFocus true to avoid recursion.
if (me.floating) {
me.toFront(true);
if (containerScrollTop !== undefined) {
me.container.dom.scrollTop = containerScrollTop;
}
}
}
return me;
},
/**
* Cancel any deferred focus on this component
* @protected
*/
cancelFocus: function() {
var task = this.focusTask;
if (task) {
task.cancel();
}
},
// private
blur: function() {
var focusEl;
if (this.rendered && (focusEl = this.getFocusEl())) {
focusEl.blur();
}
return this;
},
getEl: function() {
return this.el;
},
// Deprecate 5.0
getResizeEl: function() {
return this.el;
},
// Deprecate 5.0
getPositionEl: function() {
return this.el;
},
// Deprecate 5.0
getActionEl: function() {
return this.el;
},
// Deprecate 5.0
getVisibilityEl: function() {
return this.el;
},
// Deprecate 5.0
onResize: Ext.emptyFn,
// private
// Implements an upward event bubbilng policy. By default a Component bubbles events up to its ownerCt
// Floating Components target the floatParent.
// Some Component subclasses (such as Menu) might implement a different ownership hierarchy.
// The up() method uses this to find the immediate owner.
getBubbleTarget: function() {
return this.ownerCt || this.floatParent;
},
// private
getContentTarget: function() {
return this.el;
},
/**
* Clone the current component using the original config values passed into this instance by default.
* @param {Object} overrides A new config containing any properties to override in the cloned version.
* An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
* @return {Ext.Component} clone The cloned copy of this component
*/
cloneConfig: function(overrides) {
overrides = overrides || {};
var id = overrides.id || Ext.id(),
cfg = Ext.applyIf(overrides, this.initialConfig),
self;
cfg.id = id;
self = Ext.getClass(this);
// prevent dup id
return new self(cfg);
},
/**
* Gets the xtype for this component as registered with {@link Ext.ComponentManager}. For a list of all available
* xtypes, see the {@link Ext.Component} header. Example usage:
*
* var t = new Ext.form.field.Text();
* alert(t.getXType()); // alerts 'textfield'
*
* @return {String} The xtype
*/
getXType: function() {
return this.self.xtype;
},
/**
* Find a container above this component at any level by a custom function. If the passed function returns true, the
* container will be returned.
*
* See also the {@link Ext.Component#up up} method.
*
* @param {Function} fn The custom function to call with the arguments (container, this component).
* @return {Ext.container.Container} The first Container for which the custom function returns true
*/
findParentBy: function(fn) {
var p;
// Iterate up the ownerCt chain until there's no ownerCt, or we find an ancestor which matches using the selector function.
for (p = this.getBubbleTarget(); p && !fn(p, this); p = p.getBubbleTarget()) {
// do nothing
}
return p || null;
},
/**
* Find a container above this component at any level by xtype or class
*
* See also the {@link Ext.Component#up up} method.
*
* @param {String/Ext.Class} xtype The xtype string for a component, or the class of the component directly
* @return {Ext.container.Container} The first Container which matches the given xtype or class
*/
findParentByType: function(xtype) {
return Ext.isFunction(xtype) ?
this.findParentBy(function(p) {
return p.constructor === xtype;
})
:
this.up(xtype);
},
/**
* Bubbles up the component/container heirarchy, calling the specified function with each component. The scope
* (*this*) of function call will be the scope provided or the current component. The arguments to the function will
* be the args provided or the current component. If the function returns false at any point, the bubble is stopped.
*
* @param {Function} fn The function to call
* @param {Object} [scope] The scope of the function. Defaults to current node.
* @param {Array} [args] The args to call the function with. Defaults to passing the current component.
* @return {Ext.Component} this
*/
bubble: function(fn, scope, args) {
var p = this;
while (p) {
if (fn.apply(scope || p, args || [p]) === false) {
break;
}
p = p.getBubbleTarget();
}
return this;
},
getProxy: function() {
var me = this,
target;
if (!me.proxy) {
target = Ext.getBody();
if (Ext.scopeResetCSS) {
me.proxyWrap = target = Ext.getBody().createChild({
cls: Ext.resetCls
});
}
me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true);
}
return me.proxy;
}
});