/** * @class Ext.Component * @extends Ext.AbstractComponent *

Base class for all Ext components. 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} class. * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created, * or they may be added dynamically via the {@link Ext.Container#add add} method.

*

The Component base class has built-in support for basic hide/show and enable/disable behavior.

*

All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via * {@link Ext#getCmp}, passing the {@link #id}.

*

All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).

*

See the Creating new UI controls tutorial for details on how * and to either extend or augment ExtJs base classes to create custom Components.

*

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}. This is the list of all valid xtypes:

*
xtype            Class
-------------    ------------------
button           {@link Ext.Button}
buttongroup      {@link Ext.ButtonGroup}
colorpalette     {@link Ext.ColorPalette}
component        {@link Ext.Component}
container        {@link Ext.Container}
cycle            {@link Ext.CycleButton}
dataview         {@link Ext.DataView}
datepicker       {@link Ext.DatePicker}
editor           {@link Ext.Editor}
editorgrid       {@link Ext.grid.EditorGridPanel}
flash            {@link Ext.FlashComponent}
grid             {@link Ext.grid.GridPanel}
listview         {@link Ext.ListView}
multislider      {@link Ext.slider.MultiSlider}
panel            {@link Ext.Panel}
progress         {@link Ext.ProgressBar}
propertygrid     {@link Ext.grid.PropertyGrid}
slider           {@link Ext.slider.SingleSlider}
spacer           {@link Ext.Spacer}
splitbutton      {@link Ext.SplitButton}
tabpanel         {@link Ext.TabPanel}
treepanel        {@link Ext.tree.TreePanel}
viewport         {@link Ext.ViewPort}
window           {@link Ext.Window}

Toolbar components
---------------------------------------
paging           {@link Ext.PagingToolbar}
toolbar          {@link Ext.Toolbar}
tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
tbfill           {@link Ext.Toolbar.Fill}
tbitem           {@link Ext.Toolbar.Item}
tbseparator      {@link Ext.Toolbar.Separator}
tbspacer         {@link Ext.Toolbar.Spacer}
tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
tbtext           {@link Ext.Toolbar.TextItem}

Menu components
---------------------------------------
menu             {@link Ext.menu.Menu}
colormenu        {@link Ext.menu.ColorMenu}
datemenu         {@link Ext.menu.DateMenu}
menubaseitem     {@link Ext.menu.BaseItem}
menucheckitem    {@link Ext.menu.CheckItem}
menuitem         {@link Ext.menu.Item}
menuseparator    {@link Ext.menu.Separator}
menutextitem     {@link Ext.menu.TextItem}

Form components
---------------------------------------
form             {@link Ext.form.FormPanel}
checkbox         {@link Ext.form.Checkbox}
checkboxgroup    {@link Ext.form.CheckboxGroup}
combo            {@link Ext.form.ComboBox}
compositefield   {@link Ext.form.CompositeField}
datefield        {@link Ext.form.DateField}
displayfield     {@link Ext.form.DisplayField}
field            {@link Ext.form.Field}
fieldset         {@link Ext.form.FieldSet}
hidden           {@link Ext.form.Hidden}
htmleditor       {@link Ext.form.HtmlEditor}
label            {@link Ext.form.Label}
numberfield      {@link Ext.form.NumberField}
radio            {@link Ext.form.Radio}
radiogroup       {@link Ext.form.RadioGroup}
textarea         {@link Ext.form.TextArea}
textfield        {@link Ext.form.TextField}
timefield        {@link Ext.form.TimeField}
trigger          {@link Ext.form.TriggerField}

Chart components
---------------------------------------
chart            {@link Ext.chart.Chart}
barchart         {@link Ext.chart.BarChart}
cartesianchart   {@link Ext.chart.CartesianChart}
columnchart      {@link Ext.chart.ColumnChart}
linechart        {@link Ext.chart.LineChart}
piechart         {@link Ext.chart.PieChart}

Store xtypes
---------------------------------------
arraystore       {@link Ext.data.ArrayStore}
directstore      {@link Ext.data.DirectStore}
groupingstore    {@link Ext.data.GroupingStore}
jsonstore        {@link Ext.data.JsonStore}
simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
store            {@link Ext.data.Store}
xmlstore         {@link Ext.data.XmlStore}
* @constructor * @param {Ext.Element/String/Object} config The configuration options may be specified as either: *
*/ Ext.define('Ext.Component', { /* Begin Definitions */ alias: ['widget.component', 'widget.box'], extend: 'Ext.AbstractComponent', uses: [ 'Ext.Layer', 'Ext.resizer.Resizer', 'Ext.util.ComponentDragger', 'Ext.fx.Anim', 'Ext.state.Manager', 'Ext.util.DelayedTask' ], mixins: { floating: 'Ext.util.Floating' }, statics: { // Collapse/expand directions DIRECTION_TOP: 'top', DIRECTION_RIGHT: 'right', DIRECTION_BOTTOM: 'bottom', DIRECTION_LEFT: 'left' }, /* End Definitions */
/** * @cfg {Mixed} resizable *

Specify as true to apply a {@link Ext.resize.Resizer Resizer} to this Component * after rendering.

*

May also be specified as a config object to be passed to the constructor of {@link Ext.resize.Resizer Resizer} * to override any defaults. By default the Component passes its minimum and maximum size, and uses * {@link Ext.resize.Resizer#dynamic}: false

*/
/** * @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: false,
/** * @cfg {Boolean} maintainFlex *

Only valid when a sibling element of a {@link Ext.resizer.Splitter Splitter} within a {@link Ext.layout.container.VBox VBox} or * {@link Ext.layout.container.HBox HBox} layout.

*

Specifies that if an immediate sibling Splitter is moved, the Component on the other side is resized, and this * Component maintains its configured {@link Ext.layout.container.Box#flex flex} value.

*/
/** * @property {Ext.ZIndexManager} zIndexManager *

Optional. Only valid for {@link #floating} Components. A reference to the ZIndexManager that should manage this Component.

*

This defaults to the global {@link Ext.WindowMgr} for floating Components that are programatically {@link Ext.AbstractComponent#render rendered}.

*

For {@link #floating} Components which are added at a Container, the Container assigns a ZIndexManager.

*/ hideMode: 'display', // Deprecate 5.0 hideParent: false, ariaRole: 'presentation', bubbleEvents: [], actionMode: 'el', monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/, //renderTpl: new Ext.XTemplate( // '
{uiBase}-{ui}" style="{style}">
', { // compiled: true, // disableFormats: true // } //), constructor: function(config) { config = config || {}; if (config.initialConfig) { /*if(config.isAction){ // actions this.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 }; } Ext.Component.superclass.constructor.call(this, config); /* if(this.baseAction){ this.baseAction.addComponent(this); }*/ if (this.stateful !== false) { this.initState(); } }, initComponent: function() { if (this.listeners) { this.on(this.listeners); delete this.listeners; } this.enableBubble(this.bubbleEvents); this.mons = []; }, render: function(ct, position, overwrite) { Ext.Component.superclass.render.apply(this, arguments); if (this.stateful !== false) { this.initStateEvents(); } return this; }, // private afterRender: function() { var me = this, // not sure which is spelled properly. lets use both resizeable = me.resizable || me.resizeable; if (me.floating) { me.makeFloating(me.floating); } else { me.el.setVisibilityMode(Ext.core.Element[me.hideMode.toUpperCase()]); } me.setAutoScroll(me.autoScroll); Ext.Component.superclass.afterRender.apply(me, arguments); if (!(me.x && me.y) && (me.pageX || me.pageY)) { me.setPagePosition(me.pageX, me.pageY); } if (resizeable) { me.initResizable(resizeable); } if (me.draggable) { me.initDraggable(); } me.initAria(); }, initAria: function() { var actionEl = this.getActionEl(), role = this.ariaRole; if (role) { actionEl.dom.setAttribute('role', role); } },
/** * Sets the overflow on the content element of the component. * @param {Boolean} scroll True to allow the Component to auto scroll. * @return {Ext.BoxComponent} this */ setAutoScroll : function(scroll){ scroll = !!scroll; if (this.rendered) { this.getTargetEl().setStyle('overflow', scroll ? 'auto' : ''); } this.autoScroll = scroll; return this; }, // private makeFloating : function(cfg){ this.mixins.floating.constructor.call(this, cfg); }, initResizable: function(resizeable) { resizeable = Ext.apply({ target: this, dynamic: false, constrainTo: this.constrainTo }, resizeable); resizeable.target = this; this.resizer = new Ext.resizer.Resizer(resizeable); }, getDragEl: function() { return this.el; }, initDraggable: function() { var me = this, ddConfig = Ext.applyIf({ el: this.getDragEl(), constrainTo: me.constrainTo || me.el.dom.parentNode }, this.draggable); // Add extra configs if Component is specified to be constrained if (me.constrain || me.constrainDelegate) { ddConfig.constrain = me.constrain; ddConfig.constrainDelegate = me.constrainDelegate; } this.dd = new Ext.util.ComponentDragger(this, ddConfig); },
/** * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}. * This method fires the {@link #move} event. * @param {Number} left The new left * @param {Number} top The new top * @param {Mixed} animate If passed, the Component is animated into its new position. If this parameter * is a number, it is used as the animation duration in milliseconds. * @return {Ext.Component} this */ setPosition: function(x, y, animate) { if (x && typeof x[1] == 'number') { animate = y; y = x[1]; x = x[0]; } this.x = x; this.y = y; if (!this.rendered) { return this; } var adj = this.adjustPosition(x, y), ax = adj.x, ay = adj.y; var el = this.el; if (ax !== undefined || ay !== undefined) { if (animate && Ext.fx) { animate = { target: this, duration: Ext.num(animate, 1000), listeners: { afteranimate: Ext.Function.bind(this.afterSetPosition, this, [ax, ay]) }, to: {} }; if (ax !== undefined) { animate.to.left = ax; } if (ay !== undefined) { animate.to.top = ay; } if (this.positionAnimation && this.positionAnimation.running) { this.positionAnimation.end(true); } this.positionAnimation = new Ext.fx.Anim(animate); } else { // TODO: possibly replace this block w/ this.el.setBox if (ax == undefined) { el.setTop(ay); } else if (ay == undefined) { el.setLeft(ax); } else { el.setLeftTop(ax, ay); } this.afterSetPosition(ax, ay); } } return this; }, /** * @private Template method called after a Component has been positioned. */ afterSetPosition: function(ax, ay) { delete this.positionAnimation; this.onPosition(ax, ay); this.fireEvent('move', this, ax, ay); }, showAt: function(x, y, animate) { // A floating Component is positioned relative to its ownerCt if any. if (this.floating) { this.setPosition(x, y, animate); } else { this.setPagePosition(x, y, animate); } this.show(); },
/** * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}. * This method fires the {@link #move} event. * @param {Number} x The new x position * @param {Number} y The new y position * @param {Mixed} animate If passed, the Component is animated into its new position. If this parameter * is a number, it is used as the animation duration in milliseconds. * @return {Ext.Component} this */ setPagePosition : function(x, y, animate){ if(x && typeof x[1] == 'number'){ y = x[1]; x = x[0]; } this.pageX = x; this.pageY = y; if (x === undefined || y === undefined) { // cannot translate undefined points return this; } if (this.floating && this.floatParent) { // Floating Components being positioned in their ownerCt have to be made absolute var o = this.floatParent.getTargetEl().getViewRegion(); x -= o.left; y -= o.top; this.setPosition(x, y, animate); } else { var p = this.el.translatePoints(x, y); this.setPosition(p.left, p.top, animate); } return this; },
/** * Gets the current box measurements of the component's underlying element. * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false) * @return {Object} box An object in the format {x, y, width, height} */ getBox : function(local){ var pos = this.getPosition(local); var s = this.getSize(); s.x = pos[0]; s.y = pos[1]; return s; },
/** * 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 adjustSize: function(w, h) { if (this.autoWidth) { w = 'auto'; } if (this.autoHeight) { h = 'auto'; } return { width: w, height: h }; }, // private adjustPosition: function(x, y) { // Floating Components being positioned in their ownerCt have to be made absolute if (this.floating && this.floatParent) { var o = this.floatParent.getTargetEl().getViewRegion(); x += o.left; y += o.top; } return { x: x, y: y }; },
/** * Gets the current XY position of the component's underlying element. * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false) * @return {Array} The XY position of the element (e.g., [100, 200]) */ getPosition: function(local) { var el = this.el, xy; if (local === true) { return [el.getLeft(true), el.getTop(true)]; } xy = this.xy || el.getXY(); // Floating Components in an ownerCt have to have their positions made relative if (this.floating && this.floatParent) { var o = this.floatParent.getTargetEl().getViewRegion(); xy[0] -= o.left; xy[1] -= o.top; } return xy; }, // Todo: add in xtype prefix support getId: function() { return this.id || (this.id = (this.getXType() || 'ext-comp') + '-' + this.getAutoId()); }, // overriden onEnable: function() { var actionEl = this.getActionEl(); actionEl.removeCls(this.disabledClass); actionEl.dom.removeAttribute('aria-disabled'); actionEl.dom.disabled = false; }, // overriden onDisable: function() { var actionEl = this.getActionEl(); actionEl.addCls(this.disabledClass); actionEl.dom.setAttribute('aria-disabled', true); actionEl.dom.disabled = true; },
/** *

Shows this Component, rendering it first if {@link Ext.AbstractComponent#autoRender} is true.

*

For a {@link Ext.window.Window Window}, it activates it and brings it to front if hidden.

* @param {String/Element} animateTarget (optional) The target element or id from which the Component should * animate while opening (defaults to null with no animation) * @param {Function} callback (optional) A callback function to call after the window is displayed. Only necessary is animation was specified. * @param {Object} scope (optional) The scope (this reference) in which the callback is executed. Defaults to this Component. * @return {Component} this */ show: function(animateTarget, cb, scope) { if (!(this.rendered && this.isVisible()) && this.fireEvent('beforeshow', this) !== false) { this.hidden = false; // Render on first show if there is an autoRender config, or if this is a floater (Window, Menu, BoundList etc). if (!this.rendered && (this.autoRender || this.floating)) { this.doAutoRender(); } if (this.rendered) { this.beforeShow(); this.onShow.apply(this, arguments); // Notify any owning Container unless it's suspended. // Floating Components do not participate in layouts. if (this.ownerCt && !this.ownerCt.suspendLayout && !this.floating) { this.ownerCt.doLayout(); } } this.afterShow(); } return this; }, beforeShow: Ext.emptyFn, afterShow: function() { if (this.floating) { this.toFront(); } this.fireEvent('show', this); }, hide: function() { if (!(this.rendered && !this.isVisible()) && this.fireEvent('beforehide', this) !== false) { this.hidden = true; if (this.rendered) { this.onHide(); // Notify any owning Container unless it's suspended. // Floating Components do not participate in layouts. if (this.ownerCt && !this.ownerCt.suspendLayout && !this.floating) { this.ownerCt.doLayout(); } } this.fireEvent('hide', this); } return this; }, // Private. Override in subclasses where more complex behaviour is needed. onShow: function() { this.el.show(); Ext.Component.superclass.onShow.call(this); }, // Private. Override in subclasses where more complex behaviour is needed. onHide: function() { this.el.hide(); }, destroy: function() { var me = this; if (!me.isDestroyed) { if (me.fireEvent('beforedestroy', me) !== false) { me.destroying = true; me.beforeDestroy(); if (me.ownerCt && me.ownerCt.remove) { me.ownerCt.remove(me, false); } delete me.floatParent; // Ensure that any ancillary components are destroyed. if (me.rendered) { Ext.destroy( me.proxy, me.resizer ); me.el.remove(); // Different from AbstractComponent if (me.actionMode == 'container' || me.removeMode == 'container') { me.container.remove(); } } // A zIndexManager is stamped into a *floating* Component when it is added to a Container. // If it has no zIndexManager at render time, it is assigned to the global Ext.WindowMgr instance. if (me.zIndexManager) { me.zIndexManager.unregister(me); } // Different from lib.Component // Stop any buffered tasks if (me.focusTask && me.focusTask.cancel) { me.focusTask.cancel(); } me.onDestroy(); Ext.ComponentMgr.unregister(me); me.fireEvent('destroy', me); me.clearListeners(); me.destroying = false; me.isDestroyed = true; } } }, // private initState: function() { if (Ext.state.Manager) { var id = this.getStateId(); if (id) { var state = Ext.state.Manager.get(id); if (state) { if (this.fireEvent('beforestaterestore', this, state) !== false) { this.applyState(Ext.apply({}, state)); this.fireEvent('staterestore', this, state); } } } } }, // private getStateId: function() { return this.stateId || ((/^(ext-comp-|ext-gen)/).test(String(this.id)) ? null: this.id); }, // private initStateEvents: function() { if (this.stateEvents) { for (var i = 0, e; e = this.stateEvents[i]; i++) { this.on(e, this.saveState, this, { delay: 100 }); } } }, // private applyState: function(state) { if (state) { Ext.apply(this, state); } }, // private getState: function() { return null; }, // private saveState: function() { if (Ext.state.Manager && this.stateful !== false) { var id = this.getStateId(); if (id) { var state = this.getState(); if (this.fireEvent('beforestatesave', this, state) !== false) { Ext.state.Manager.set(id, state); this.fireEvent('statesave', this, state); } } } }, // This needs to die a horrible death. For now it's a replaceMarkup, I can't see supporting this methodology going forward.
/** * Apply this component to existing markup that is valid. With this function, no call to render() is required. * @param {String/HTMLElement} el */ applyToMarkup: function(el) { this.allowDomMove = false; this.render(Ext.getDom(el).parentNode, null, true); }, 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 (optional) If applicable, true to also select the text in this component * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds) * @return {Ext.Component} this */ focus: function(selectText, delay) { var focusEl; if (delay) { this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]); this.focusTask.delay(Ext.isNumber(delay) ? delay: 10); return this; } if (this.rendered && !this.isDestroyed) { // getFocusEl could return a Component. focusEl = this.getFocusEl(); focusEl.focus(); if (focusEl.dom && selectText === true) { focusEl.dom.select(); } // Focusing a floating Component brings it to the front of its stack. // This is performed by its zIndexManager. if (this.floating) { this.toFront(); } } return this; }, /** * @private * Returns the focus holder element associated with this Component. By default, this is the Component's encapsulating * element. Subclasses which use embedded focusable elements (such as Window and Button) should override this for use * by the {@link #focus} method. * @returns {Ext.Element} the focus holing element. */ getFocusEl: function() { return this.el; }, // private blur: function() { if (this.rendered) { this.getFocusEl().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 getBubbleTarget: function() { return this.ownerCt; }, // 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(); var cfg = Ext.applyIf(overrides, this.initialConfig); cfg.id = id; var self = Ext.getClass(this); // prevent dup id return new self(cfg); },
/** * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all * available xtypes, see the {@link Ext.Component} header. Example usage: *

var t = new Ext.form.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. * @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; for (p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt); return p || null; },
/** *

Find a container above this component at any level by xtype or class

*

See also the {@link Ext.AbstractComponent#up up} method.

* @param {String/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 (optional) The scope of the function (defaults to current node) * @param {Array} args (optional) The args to call the function with (default 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.ownerCt; } return this; }, alignTo: function(el, position, offsets) { this.el.alignTo.apply(this.el, arguments); }, getProxy: function() { if (!this.proxy) { this.proxy = this.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', Ext.getBody(), true); } return this.proxy; } });