/** * 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](../guides/core_concepts/components.html) 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 * life cycle 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 * life cycle and size management should subclass Component. * * See the Creating new UI controls chapter in * [Component Guide](../guides/core_concepts/components.html) 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. */Ext.define('Ext.Component', { alternateClassName: 'Ext.AbstractComponent', xtype: [ 'component', 'box' ], requires: [ 'Ext.ComponentQuery', 'Ext.ComponentManager', 'Ext.util.ProtoElement', 'Ext.dom.CompositeElement', 'Ext.plugin.Abstract', 'Ext.plugin.Manager', 'Ext.scroll.Scroller' ], // Note that Floating must be mixed in before Positionable. mixins: [ 'Ext.mixin.Inheritable', 'Ext.util.Floating', 'Ext.util.Positionable', 'Ext.util.Observable', 'Ext.mixin.ComponentDelegation', 'Ext.mixin.Bindable', 'Ext.util.Animate', 'Ext.util.ElementContainer', 'Ext.util.Renderable', 'Ext.state.Stateful', 'Ext.mixin.Focusable', 'Ext.mixin.Accessible', 'Ext.mixin.Keyboard' ], uses: [ 'Ext.overrides.*', 'Ext.Element', 'Ext.DomHelper', 'Ext.XTemplate', 'Ext.ComponentLoader', 'Ext.layout.Context', 'Ext.layout.Layout', 'Ext.layout.component.Auto', 'Ext.LoadMask', 'Ext.ZIndexManager', 'Ext.util.DelayedTask', 'Ext.resizer.Resizer', 'Ext.util.ComponentDragger' ], statics: { AUTO_ID: 1000, pendingLayouts: null, layoutSuspendCount: 0, // 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, /** * @property {String} ariaHighContrastModeCls CSS class to be applied * to the document body when High Contrast mode is detected in Windows. * @private */ ariaHighContrastModeCls: Ext.baseCSSPrefix + 'aria-highcontrast', /** * Cancels layout of a component. * @param {Ext.Component} comp * @param isDestroying */ cancelLayout: function(comp, isDestroying) { var context = this.runningLayoutContext || this.pendingLayouts; if (context) { context.cancelComponent(comp, false, isDestroying); } }, /** * Find the Widget or Component to which the given event/element belongs. * * @param {Ext.event.Event/Ext.dom.Element/HTMLElement} el The element or event * from which to start to find an owning Component. * @param {Ext.dom.Element/HTMLElement} [limit] The element at which to stop upward * searching for an owning Component, or the number of Components to traverse * before giving up. Defaults to the document's HTML element. * @param {String} [selector] An optional {@link Ext.ComponentQuery} selector to * filter the target. * @return {Ext.Component/null} Component, or null * * @since 6.5.0 */ from: function(el, limit, selector) { return Ext.ComponentManager.from(el, limit, selector); }, /** * Find the Widget or Component to which the given Element belongs. * * @param {Ext.dom.Element/HTMLElement} el The element from which to start to find an owning * Component. * @param {Ext.dom.Element/HTMLElement} [limit] The element at which to stop upward * searching for an owning Component, or the number of Components to traverse * before giving up. Defaults to the document's HTML element. * @param {String} [selector] An optional {@link Ext.ComponentQuery} selector to filter * the target. * @return {Ext.Component/null} Component, or null * * @deprecated 6.5.0 Use {@link Ext.Component#method!from} instead. * @since 6.0.1 */ fromElement: function(el, limit, selector) { return Ext.ComponentManager.from(el, limit, selector); }, /** * Performs all pending layouts that were scheduled while * {@link Ext.Component#suspendLayouts suspendLayouts} was in effect. * @static */ flushLayouts: function() { var me = this, context = me.pendingLayouts; if (context && context.invalidQueue.length) { me.pendingLayouts = null; me.runningLayoutContext = context; Ext.override(context, { runComplete: function() { var Scroller = Ext.scroll.Scroller, GlobalEvents = Ext.GlobalEvents, result; // we need to release the layout queue before running any of the // finishedLayout calls because they call afterComponentLayout // which can re-enter by calling updateLayout/doComponentLayout. me.runningLayoutContext = null; if (Scroller.viewport) { Scroller.viewport.restoreState(); } result = this.callParent(); // not "me" here! if (GlobalEvents.hasListeners.afterlayout) { GlobalEvents.fireEvent('afterlayout'); } return result; } }); context.run(); } }, /** * Resumes layout activity in the whole framework. * * {@link Ext#suspendLayouts} is alias of {@link Ext.Component#suspendLayouts}. * * @param {Boolean} [flush=false] `true` to perform all the pending layouts. This can also * be achieved by calling {@link Ext.Component#flushLayouts flushLayouts} directly. * @static */ resumeLayouts: function(flush) { if (this.layoutSuspendCount && ! --this.layoutSuspendCount) { if (flush) { this.flushLayouts(); } if (Ext.GlobalEvents.hasListeners.resumelayouts) { Ext.GlobalEvents.fireEvent('resumelayouts'); } } }, /** * Stops layouts from happening in the whole framework. * * It's useful to suspend the layout activity while updating multiple components and * containers: * * Ext.suspendLayouts(); * // batch of updates... * Ext.resumeLayouts(true); * * {@link Ext#suspendLayouts} is alias of {@link Ext.Component#suspendLayouts}. * * See also {@link Ext#batchLayouts} for more abstract way of doing this. * * @static */ suspendLayouts: function() { ++this.layoutSuspendCount; }, /** * Updates layout of a component. * * @param {Ext.Component} comp The component to update. * @param {Boolean} [defer=false] `true` to just queue the layout if this component. * @static */ updateLayout: function(comp, defer) { var me = this, running = me.runningLayoutContext, pending; if (running) { running.queueInvalidate(comp); } else { pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context()); pending.queueInvalidate(comp); if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) { me.flushLayouts(); } } } }, // <editor-fold desc="Config"> // *********************************************************************************** // Begin Config // *********************************************************************************** // We do not want "_hidden" style backing properties. $configPrefixed: false, // We also want non-config system properties to go to the instance. $configStrict: false, clearPropertiesOnDestroy: 'async', manageLayoutScroll: true, config: { /** * @cfg {Object} data * The initial set of data to apply to the `{@link #tpl}` to update the content * area of the Component. * * @accessor * @since 3.4.0 */ data: null, /** * @cfg {Boolean} modelValidation * This config enables binding to your `{@link Ext.data.Model#validators}`. This * is only processed by form fields (e.g., `Ext.form.field.Text`) at present, but * this setting is inherited and so can be set on a parent container. * * When set to `true` by a component or not set by a component but inherited from * an ancestor container, `Ext.data.Validation` records are used to automatically * bind validation results for any form field to which a `value` is bound. * * While this config can be set arbitrarily high in the component hierarchy, doing * so can create a lot overhead if most of your form fields do not actually rely on * `validators` in your data model. * * Using this setting for a form that is bound to an `Ext.data.Model` might look * like this: * * { * xtype: 'panel', * modelValidation: true, * items: [{ * xtype: 'textfield', * bind: '{theUser.firstName}' * },{ * xtype: 'textfield', * bind: '{theUser.lastName}' * },{ * xtype: 'textfield', * bind: '{theUser.phoneNumber}' * },{ * xtype: 'textfield', * bind: '{theUser.email}' * }] * } * * Notice that "validation" is a pseudo-association defined for all entities. See * `{@link Ext.data.Model#getValidation}` for further details. */ /** * @cfg {Number} maxHeight * The maximum value in pixels which this Component will set its height to. * * **Warning:** This will override any size management applied by layout managers. */ maxHeight: null, /** * @cfg {Number} maxWidth * The maximum value in pixels which this Component will set its width to. * * **Warning:** This will override any size management applied by layout managers. */ maxWidth: null, /** * @cfg {Number} minHeight * The minimum value in pixels which this Component will set its height to. * * **Warning:** This will override any size management applied by layout managers. */ minHeight: null, /** * @cfg {Number} minWidth * The minimum value in pixels which this Component will set its width to. * * **Warning:** This will override any size management applied by layout managers. */ minWidth: null, /** * @cfg {Boolean/String/Object} scrollable * Configuration options to make this Component scrollable. Acceptable values are: * * - `true` to enable auto scrolling. * - `false` (or `null`) to disable scrolling - this is the default. * - `x` or `horizontal` to enable horizontal scrolling only * - `y` or `vertical` to enable vertical scrolling only * * Also accepts a configuration object for a `{@link Ext.scroll.Scroller}` if * if advanced configuration is needed. * * The getter for this config returns the {@link Ext.scroll.Scroller Scroller} * instance. You can use the Scroller API to read or manipulate the scroll position: * * // scrolls the component to 5 on the x axis and 10 on the y axis * component.getScrollable().scrollTo(5, 10); */ scrollable: null }, /** * @cfg {Object} renderConfig * renderConfig wraps configs that do not get applied until after the component is * rendered. Unlike normal config system properties, renderConfigs use a special * setter method to store values on the instance instead of running the apply and * update methods if it is called before the component is rendered. Then, after the * component has been rendered, these values are processed by the normal apply and * update method for the config. * * This means that calling the get method for the config prior to render will return * whatever raw value has been set, while calling the getter after render will return * the value after processing by the config's apply method. If this distinction needs * to be made, it is the caller's responsibility to check for the rendered state and * handle such intermediate config values. */ renderConfig: { /** * @cfg {Object} touchAction * * Emulates the behavior of the CSS * [touch-action](https://www.w3.org/TR/pointerevents/#the-touch-action-css-property) * property in a cross-browser compatible manner. * * Keys in this object are touch action names, and values are `false` to disable * a touch action or `true` to enable it. Accepted keys are: * * - `panX` * - `panY` * - `pinchZoom` * - `doubleTapZoom` * * All touch actions are enabled (`true`) by default, so it is usually only necessary * to specify which touch actions to disable. For example, the following disables * only horizontal scrolling and pinch-to-zoom on the component's main element: * * touchAction: { * panX: false, * pinchZoom: false * } * * Touch actions can be specified on child elements using the child element name, * for example: * * // disables horizontal scrolling on the main element, and double-tap-zoom * // on the child element named "body" * touchAction: { * panY: false * body: { * doubleTapZoom: false * } * } * * The primary motivation for setting the touch-action of an element is to prevent * the browser's default handling of a gesture such as pinch-to-zoom, or * drag-to-scroll, so that the application can implement its own handling of that * gesture on the element. Suppose, for example, a component has a custom drag * handler on its element and wishes to prevent horizontal scrolling of its container * while it is being dragged: * * Ext.create('Ext.Component', { * touchAction: { * panX: false * }, * listeners: { * drag: function(e) { * // implement drag logic * } * } * }); */ touchAction: null }, /** * @property defaultBindProperty * @inheritdoc */ defaultBindProperty: 'html', /** * @cfg {String} alignTarget * A Component or Element by which to position this component according to the * {@link #defaultAlign}. Defaults to the owning Container. * * *Only applicable if this component is {@link #cfg-floating}* * * *Used upon first show*. */ alignTarget: null, /** * @cfg anchor * @inheritDoc Ext.layout.container.Anchor#cfg-anchor */ /** * @cfg {String/Object} autoEl * A tag name or {@link Ext.dom.Helper DomHelper} spec used to create the {@link #getEl Element} * which will encapsulate this Component. * * You do not normally need to specify this. For the base classes {@link Ext.Component} and * {@link Ext.container.Container}, this defaults to **'div'**. The more complex Sencha classes * use a more complex DOM structure specified by their own {@link #cfg-renderTpl}s. * * This is intended to allow the developer to create application-specific utility Components * encapsulated by different DOM elements. Example usage: * * { * xtype: 'component', * autoEl: { * tag: 'img', * src: 'http://www.example.com/example.jpg' * } * }, { * xtype: 'component', * autoEl: { * tag: 'blockquote', * html: 'autoEl is cool!' * } * }, { * xtype: 'container', * autoEl: 'ul', * cls: 'ux-unordered-list', * items: { * xtype: 'component', * autoEl: 'li', * html: 'First list item' * } * } * * @since 2.3.0 */ /** * @cfg {Boolean/String/HTMLElement/Ext.dom.Element} autoRender * This config is intended mainly for non-{@link #cfg-floating} Components which may or may not * be shown. Instead of using {@link #renderTo} in the configuration, and rendering upon * construction, this allows a Component to render itself upon first * _{@link Ext.Component#method-show show}_. If {@link #cfg-floating} is `true`, the value * of this config is omitted as if it is `true`. * * Specify as `true` to have this Component render to the document body upon first show. * * Specify as an element, or the ID of an element to have this Component render to a specific * element upon first show. */ autoRender: false, /** * @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}. * @deprecated 5.1.0 Use {@link #scrollable} instead */ /** * @cfg {Boolean} autoShow * `true` to automatically show the component upon creation. This config option may only be used * for {@link #cfg-floating} components or components that use {@link #autoRender}. * * @since 2.3.0 */ autoShow: false, /** * @cfg {String} baseCls * The base CSS class to apply to this component's element. This will also be prepended * to elements within this component like Panel's body will get a class `x-panel-body`. * This means that if you create a subclass of Panel, and you want it to get all the Panels * styling for the element and the body, you leave the `baseCls` `x-panel` and use * `componentCls` to add specific styling for this component. */ baseCls: Ext.baseCSSPrefix + 'component', /** * @cfg {Number/String/Boolean} border * Specifies the border size for this component. The border can be a single numeric value * to apply to all sides or it can be a CSS style specification for each style, * for example: '10 5 3 10' (top, right, bottom, left). * * For components that have no border by default, setting this won't make the border appear * by itself. You also need to specify border color and style: * * border: 5, * style: { * borderColor: 'red', * borderStyle: 'solid' * } * * To turn off the border, use `border: false`. */ /** * @cfg childEls * @inheritdoc Ext.util.ElementContainer#cfg-childEls */ /* eslint-disable key-spacing */ childEls: { frameTable: { frame: true }, frameTL: { frame: 'tl' }, frameTC: { frame: 'tc' }, frameTR: { frame: 'tr' }, frameML: { frame: 'ml' }, frameBody: { frame: 'mc' }, frameMR: { frame: 'mr' }, frameBL: { frame: 'bl' }, frameBC: { frame: 'bc' }, frameBR: { frame: 'br' } }, /* eslint-enable key-spacing */ /** * @cfg {String/String[]} [cls=''] * An optional extra CSS class that will be added to this component's Element. * The value can be a string, a list of strings separated by spaces, or an array of strings. * This can be useful for adding customized styles to the component or any of its children * using standard CSS rules. * * @since 1.1.0 */ /** * @cfg {Number} [columnWidth] * Defines the column width inside {@link Ext.layout.container.Column column layout}. * * The columnWidth property is always evaluated as a percentage and must be a decimal value * greater than 0 and less than 1 (e.g., .25). See the description at the top of * {@link Ext.layout.container.Column column layout} for additional usage details when combining * width and columnWidth configs within the layout. */ /** * @cfg {String} componentCls * CSS Class to be added to a components root level element to give distinction to it * via styling. */ // @cmd-auto-dependency { aliasPrefix : "layout." } /** * @cfg {String/Object} componentLayout * The sizing and positioning of a Component's internal Elements is the responsibility * of the Component's layout manager which sizes a Component's internal structure in response * to the Component being sized. * * Generally, developers will not use this configuration as all provided Components which need * their internal elements sizing (Such as {@link Ext.form.field.Base input fields}) come with * their own componentLayout managers. * * The {@link Ext.layout.container.Auto default layout manager} will be used on instances of the * base Ext.Component class which simply sizes the Component's encapsulating element to the * height and width specified in the {@link #setSize} method. * */ componentLayout: 'autocomponent', /** * @cfg {Object/String} constraintInsets * An object or a string (in TRBL order) specifying insets from the configured * {@link #constrainTo constrain region} within which this component must be constrained * when positioning or sizing. Example: * * constraintInsets: '10 10 10 10' // Constrain with 10px insets from parent */ /** * @cfg {Ext.util.Region/Ext.dom.Element} constrainTo * A {@link Ext.util.Region Region} (or an element from which a Region measurement will be read) * which is used to constrain the component. Only applies when the component is floating. */ /** * @cfg {String} contentEl * Specify an existing HTML element, or the `id` of an existing HTML element to use as the * content for this component. * * This config option is used to take an existing HTML element and place it in the layout * element of a new component (it simply moves the specified DOM element _after the Component * is rendered_ to use as the content. * * **Notes:** * * The specified HTML element is appended to the layout element of the component _after any * configured {@link #html HTML} has been inserted_, and so the document will not contain * this element at the time the {@link #event-render} event is fired. * * The specified HTML element used will not participate in any * **`{@link Ext.container.Container#layout layout}`** scheme that the Component may use. * It is just HTML. Layouts operate on child * **`{@link Ext.container.Container#cfg-items items}`**. * * Add either the `x-hidden` or the `x-hidden-display` CSS class to prevent a brief flicker * of the content before it is rendered to the panel. * * @since 3.4.0 */ /** * @cfg {String} defaultAlign * The default {@link Ext.util.Positionable#getAlignToXY Ext.dom.Element#getAlignToXY} anchor * position value for this component relative to its {@link #alignTarget} * (which defaults to its owning Container). * * _Only applicable if this component is {@link #cfg-floating}_ * * *Used upon first show*. */ defaultAlign: 'c-c', /** * @cfg {Boolean} disabled * `true` to disable the component. * @since 2.3.0 */ disabled: false, // http://www.w3.org/TR/html5/disabled-elements.html disabledRe: /^(?:button|input|select|textarea|optgroup|option|fieldset)$/i, nonMaskableRe: (function() { var re = ['input', 'select', 'textarea', 'optgroup', 'option', 'table']; // All IE browsers 9 and below except for IE 9 standards. if (Ext.isIE9m && !(Ext.isIE9 && !Ext.isIEQuirks)) { // <p>.insertAdjacentHTML('BeforeEnd', '<div>...</div>') yields // 'Invalid source HTML for this operation' in all IEs not IE 9 standards. re.push('p'); } return new RegExp('^(?:' + re.join('|') + ')$', 'i'); }()), /** * @cfg {String} disabledCls * CSS class to add when the Component is disabled. */ disabledCls: Ext.baseCSSPrefix + 'item-disabled', /** * @cfg {'top'/'bottom'/'left'/'right'} dock * The side of the {@link Ext.panel.Panel panel} where this component is to be * docked when specified in the panel's * {@link Ext.panel.Panel#dockedItems dockedItems} config. * * Possible values are: * * - top * - bottom * - left * - right */ /** * @cfg {Boolean/Object} draggable * Specify as true to make a {@link #cfg-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: '<h1 style="cursor:move">The title</h1><p>The content</p>', * draggable: { * delegate: 'h1' * } * }).show(); */ draggable: false, /** * @cfg {Number} flex * Flex may be applied to **child items** of a box layout * ({@link Ext.layout.container.VBox vbox} or {@link Ext.layout.container.HBox hbox}). * Each child item with a flex property will fill space (horizontally in `hbox`, vertically * in `vbox`) according to that item's **relative** flex value compared to the sum of all items * with a flex value specified. * * Any child items that have either a `flex` of `0` or `undefined` * will not be 'flexed' (the initial size will not be changed). */ /** * @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 programmatically {@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 sorts its stack according to an incrementing access counter and the * {@link Ext.util.Floating#alwaysOnTop alwaysOnTop} config 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} [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 {Boolean} frame * Specify as `true` to have the Component inject framing elements within the Component * at render time to provide a graphical rounded frame around the Component content. * * This is only necessary when running on outdated, or non standard-compliant browsers * such as Microsoft's Internet Explorer prior to version 9 which do not support rounded corners * natively. * * The extra space taken up by this framing is available from the read only property * {@link #frameSize}. */ /** * @cfg {Number|String} height * The height of this component. A numeric value will be interpreted as the number of * pixels; a string value will be treated as a CSS value with units. */ /** * @cfg {Boolean} hidden * `true` to hide the component. * @since 2.3.0 */ hidden: false, /** * @cfg {String} hideMode * A String which specifies how this Component's encapsulating DOM element will be hidden. * Values may be: * * - `'display'` : The Component will be hidden using the `display: none` style. * - `'visibility'` : The Component will be hidden using the `visibility: hidden` style. * - `'offsets'` : The Component will be hidden by absolutely positioning it out of the * visible area of the document. * This is useful when a hidden Component must maintain measurable dimensions. Hiding using * `display` results in a Component having zero dimensions. * * @since 1.1.0 */ hideMode: 'display', /** * @cfg {String/Object} [html=''] * An HTML fragment, or a {@link Ext.dom.Helper DomHelper} specification to use as the layout * element content. The HTML content is added after the component is rendered, so the document * will not contain this HTML at the time the {@link #event-render} event is fired. This content * is inserted into the body _before_ any configured {@link #contentEl} is appended. * * @since 3.4.0 */ /** * @cfg {String} id * The **unique** id of this component instance. * * Use of this config should be considered carefully as this value must be unique across * all existing components. Components created with an `id` may be accessed globally * using {@link Ext#getCmp Ext.getCmp}. * * Instead of using assigned ids, consider a {@link #reference} config and * a {@link #cfg-controller ViewController} to respond to events and perform processing * upon this Component. * * Alternatively, {@link #itemId} and {@link Ext.ComponentQuery ComponentQuery} can be * used to perform selector-based searching for Components analogous to DOM querying. * The {@link Ext.container.Container Container} class contains several helpful * {@link Ext.container.Container#down shortcut methods} to query its descendant * Components by selector. * * Note that this `id` will also be used as the element id for the containing HTML * element that is rendered to the page for this component. This allows you to write * id-based CSS rules to style the specific instance of this component uniquely, and * also to select sub-elements using this component's `id` as the parent. * * Defaults to an {@link #getId auto-assigned id}. * * **Note**: Valid identifiers start with a letter or underscore and are followed by * (optional) additional letters, underscores, digits or hyphens. * * @since 1.1.0 */ /** * @cfg {String} itemId * The **unique** id of this component instance within its container. See also the * {@link #reference} config. * * An `itemId` can be used as an alternative way to get a reference to a component * when no object reference is available. Instead of using an `{@link #id}` with * {@link Ext#getCmp getCmp}, use `itemId` with * {@link Ext.container.Container#getComponent getComponent} which will retrieve `itemId`'s * or {@link #id}'s. Since `itemId`'s are an index to the container's internal collection, * the `itemId` is scoped locally to the container -- avoiding potential conflicts with * {@link Ext.ComponentManager}, which requires a **unique** {@link #id} value. * * var c = new Ext.panel.Panel({ // * height: 300, * renderTo: document.body, * layout: 'auto', * items: [{ * itemId: 'p1', * title: 'Panel 1', * height: 150 * },{ * itemId: 'p2', * title: 'Panel 2', * height: 150 * }] * }); * * p1 = c.getComponent('p1'); // not the same as Ext.getCmp() * console.log(p1); * p2 = p1.ownerCt.getComponent('p2'); // reference via a sibling * console.log(p2); * * Also see {@link #id}, `{@link Ext.container.Container#query}`, * `{@link Ext.container.Container#down}` and `{@link Ext.container.Container#child}`. * * **Note**: Valid identifiers start with a letter or underscore and are followed by * (optional) additional letters, underscores, digits or hyphens. * * **Note**: to access the container of an item see {@link #ownerCt}. * * @since 3.4.0 */ /** * @cfg {Ext.ComponentLoader/Object} loader * A configuration object or an instance of a {@link Ext.ComponentLoader} to load remote content * for this Component. * * Ext.create('Ext.Component', { * loader: { * url: 'content.html', * autoLoad: true * }, * renderTo: Ext.getBody() * }); */ /** * @cfg {Number/String} margin * Specifies the margin for this component. The margin can be a single numeric value to apply * to all sides or it can be a CSS style specification for each style, * for example: '10 5 3 10' (top, right, bottom, left). */ /** * @cfg {String} maskElement * Related to the {@link #cfg-childEls} configuration which specifies named properties * which correspond to component sub-elements. * * The name of the element property in this component to mask when masked by a LoadMask. * * Defaults to `null` to indicate that Components cannot by default contain a LoadMask, * and that any LoadMask should be rendered into the document body. * * For example, Panels use `"el"` to indicate that the whole panel should be masked. * This could be configured to be `"body"` so that only the body is masked and toolbars * and the header are still mouse-accessible. */ maskElement: null, /** * @cfg {Object} [maskDefaults] Default LoadMask configuration for {@link #method-setLoading}. */ /** * @cfg {String} [overCls=''] * An optional extra CSS class that will be added to this component's Element when the mouse * moves over the Element, and removed when the mouse moves out. This can be useful for adding * customized 'active' or 'hover' styles to the component or any of its children using standard * CSS rules. * * @since 2.3.0 */ /** * @cfg {String} overflowX * Possible values are: * * - `'auto'` to enable automatic horizontal scrollbar (Style overflow-x: 'auto'). * - `'scroll'` to always enable horizontal scrollbar (Style overflow-x: 'scroll'). * * The default is overflow-x: 'hidden'. This should not be combined with {@link #autoScroll}. * @deprecated 5.1.0 Use {@link #scrollable} instead */ /** * @cfg {String} overflowY * Possible values are: * * - `'auto'` to enable automatic vertical scrollbar (Style overflow-y: 'auto'). * - `'scroll'` to always enable vertical scrollbar (Style overflow-y: 'scroll'). * * The default is overflow-y: 'hidden'. This should not be combined with {@link #autoScroll}. * @deprecated 5.1.0 Use {@link #scrollable} instead */ /** * @cfg {Number/String} padding * Specifies the padding for this component. The padding can be a single numeric value to apply * to all sides or it can be a CSS style specification for each style, * for example: '10 5 3 10' (top, right, bottom, left). */ /** * @cfg {Array/Ext.enums.Plugin/Object/Ext.plugin.Abstract} plugins * This config describes one or more plugin config objects used to create plugin * instances for this component. * * Plugins are a way to bundle and reuse custom functionality. Plugins should extend * `Ext.plugin.Abstract` but technically the only requirement for a valid plugin * is that it contain an `init` method that accepts a reference to its owner. Once * a plugin is created, the owner will call the `init` method, passing a reference * to itself. Each plugin can then call methods or respond to events on its owner * as needed to provide its functionality. * * This config's value can take several different forms. * * The value can be a single string with the plugin's {@link Ext.enums.Plugin alias}: * * plugins: 'cellediting', * * The preferred form for multiple plugins or to configure plugins is the keyed-object * form (new in version 6.5): * * plugins: { * gridviewdragdrop: true, * cellediting: { * clicksToEdit: 1 * } * }, * * The keys are `id`'s as well as the default type alias. * * The `plugins` config can also be an array of plugin aliases: * * plugins: [ 'cellediting', 'gridviewdragdrop' ], * * An array can also contain elements that are config objects with a `ptype` property * holding the type alias: * * plugins: ['gridviewdragdrop', { * ptype: 'cellediting', * clicksToEdit: 1 * }], * * @since 2.3.0 */ /** * @cfg {"north"/"south"/"east"/"west"/"center"} [region=undefined] * Defines the region inside {@link Ext.layout.container.Border border layout}. * * Possible values: * * - north - Positions component at top. * - south - Positions component at bottom. * - east - Positions component at right. * - west - Positions component at left. * - center - Positions component at the remaining space. * There **must** be a component with `region: "center"` in every border layout. */ /** * @cfg {Object} renderData * * The data used by {@link #renderTpl} in addition to the following property values * of the component: * * - id * - ui * - uiCls * - baseCls * - componentCls * - frame * * See {@link #renderSelectors} and {@link #cfg-childEls} for usage examples. */ /** * @cfg {Object} renderSelectors * An object containing properties specifying CSS selectors which identify child elements * created by the render process. * * After the Component's internal structure is rendered according to the {@link #renderTpl}, * this object is iterated through, and the found Elements are added as properties * to the Component using the `renderSelector` property name. * * For example, a Component which renders a title and description into its element: * * Ext.create('Ext.Component', { * renderTo: Ext.getBody(), * renderTpl: [ * '<h1 class="title">{title}</h1>', * '<p>{desc}</p>' * ], * renderData: { * title: "Error", * desc: "Something went wrong" * }, * renderSelectors: { * titleEl: 'h1.title', * descEl: 'p' * }, * listeners: { * afterrender: function(cmp){ * // After rendering the component will have a titleEl and descEl properties * cmp.titleEl.setStyle({color: "red"}); * } * } * }); * * The use of `renderSelectors` is deprecated (for performance reasons). The above * code should be refactored into something like this: * * Ext.create('Ext.Component', { * renderTo: Ext.getBody(), * renderTpl: [ * '<h1 class="title" id="{id}-titleEl" data-ref="titleEl">{title}</h1>', * '<p id="{id}-descEl" data-ref="descEl">{desc}</p>' * ], * renderData: { * title: "Error", * desc: "Something went wrong" * }, * childEls: [ * 'titleEl', * 'descEl' * ] * }); * * To use `childEls` yet retain the use of selectors (which remains as expensive as * `renderSelectors`): * * Ext.create('Ext.Component', { * renderTo: Ext.getBody(), * renderTpl: [ * '<h1 class="title">{title}</h1>', * '<p>{desc}</p>' * ], * renderData: { * title: "Error", * desc: "Something went wrong" * }, * childEls: { * titleEl: { selectNode: 'h1.title' }, * descEl: { selectNode: 'p' } * } * }); * * @deprecated 5.0 Use {@link #cfg-childEls} instead. */ /** * @cfg {String/HTMLElement/Ext.dom.Element} renderTo * Specify the `id` of the element, a DOM element or an existing Element that this component * will be rendered into. * * **Notes:** * * Do *not* use this option if the Component is to be a child item of a * {@link Ext.container.Container Container}. It is the responsibility of the * {@link Ext.container.Container Container}'s * {@link Ext.container.Container#layout layout manager} to render and manage its child items. * * When using this config, a call to `render()` is not required. * * See also: {@link #method-render}. * * @since 2.3.0 */ /** * @cfg {Ext.XTemplate/String/String[]} renderTpl * An {@link Ext.XTemplate XTemplate} used to create the internal structure inside * this Component's encapsulating {@link #getEl Element}. * * You do not normally need to specify this. For the base classes {@link Ext.Component} and * {@link Ext.container.Container}, this defaults to **`null`** which means that they will be * initially rendered with no internal structure; they render their {@link #getEl Element} * empty. The more specialized classes with complex DOM structures provide their own template * definitions. * * This is intended to allow the developer to create application-specific utility Components * with customized internal structure. * * Upon rendering, any created child elements may be automatically imported into object * properties using the {@link #renderSelectors} and {@link #cfg-childEls} options. * @protected */ renderTpl: '{%this.renderContent(out,values)%}', /** * @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/Number} shrinkWrap * * The possible values for shrinkWrap are: * * - 0 (or `false`): Neither width nor height depend on content. * - 1: Width depends on content (shrink wraps), but height does not. * - 2: Height depends on content (shrink wraps), but width does not. * - 3 (or `true`): Both width and height depend on content (shrink wrap). * * In CSS terms, shrink-wrap width is analogous to an inline-block element as opposed * to a block-level element. * * @localdoc ##Non-Panel Components * * The shrinkWrap config is a class-level config and should be used when defining a * subclass. * It is not intended to be set as a config on instances of a given component. * * For non-Panel components, shrinkWrap is a descriptive config only. It should be * set when defining your own custom class including the DOM elements used to * construct the component. The shrinkWrap property does not itself apply styling on * the component elements. Rather, it should describe the CSS styling you've applied * to your custom component (_refer to the numeric matrix above_). * * When a component is owned by a container the layout of that container will inspect * the component's shrinkWrap property during layout. The layout then uses the * content-wrapping policy described by shrinkWrap to correctly size and position the * container's child items. */ shrinkWrap: 2, /** * @cfg stateEvents * @inheritdoc Ext.state.Stateful#cfg-stateEvents * @localdoc By default the following stateEvents are added: * * - {@link #event-resize} */ /** * @cfg {String/Object} style * A custom style specification to be applied to this component's Element. * Should be a valid argument to {@link Ext.dom.Element#applyStyles}. * * new Ext.panel.Panel({ * title: 'Some Title', * renderTo: Ext.getBody(), * width: 400, height: 300, * layout: 'form', * items: [{ * xtype: 'textarea', * style: { * width: '95%', * marginBottom: '10px' * } * }, * new Ext.button.Button({ * text: 'Send', * minWidth: '100', * style: { * marginBottom: '10px' * } * }) * ] * }); * * @since 1.1.0 */ /** * @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, /** * @cfg {Ext.XTemplate/Ext.Template/String/String[]} tpl * An {@link Ext.Template}, {@link Ext.XTemplate} or an array of strings to form * an Ext.XTemplate. Used in conjunction with the `{@link #data}` and `{@link #tplWriteMode}` * configurations. * * @since 3.4.0 */ /** * @property {Boolean} synthetic * This property is `true` if the component was created internally by the framework * and is not explicitly user-defined. This is set for such things as `Splitter` * instances managed by `border` and `box` layouts. * @private */ synthetic: false, /** * @cfg {String} tplWriteMode * The Ext.(X)Template method to use when updating the content area of the Component. * See `{@link Ext.XTemplate#overwrite}` for information on default mode. * * @since 3.4.0 */ tplWriteMode: 'overwrite', /** * @cfg {String} ui * A UI style for a component. */ ui: 'default', /** * @cfg {String[]} uiCls * An array of of `classNames` which are currently applied to this component. * @private */ uiCls: [], /** * @cfg {String/String[]} userCls * One or more CSS classes to add to the component's primary element. This config * is intended solely for use by the component instantiator (the "user"), not by * derived classes. * * For example: * * items: [{ * xtype: 'button', * userCls: 'my-button' * ... * }] * @accessor */ userCls: null, /** * @cfg {Number} weight * A value to control how Components are laid out in a * {@link Ext.layout.container.Border Border} layout or as docked items. * * In a Border layout, this can control how the regions (not the center) region lay out * if the west or east take full height or if the north or south region take full width. * Also look at the {@link Ext.layout.container.Border#regionWeights} on the Border layout. * An example to show how you can take control of this is: * * Ext.create('Ext.container.Viewport', { * layout : 'border', * defaultType : 'panel', * items : [ * { * region : 'north', * title : 'North', * height : 100 * }, * { * region : 'south', * title : 'South', * height : 100, * weight : -25 * }, * { * region : 'west', * title : 'West', * width : 200, * weight : 15 * }, * { * region : 'east', * title : 'East', * width : 200 * }, * { * region : 'center', * title : 'center' * } * ] * }); * * If docked items, the weight will order how the items are laid out. Here is an example * to put a {@link Ext.toolbar.Toolbar} above a {@link Ext.panel.Panel}'s header: * * Ext.create('Ext.panel.Panel', { * renderTo : document.body, * width : 300, * height : 300, * title : 'Panel', * html : 'Panel Body', * dockedItems : [ * { * xtype : 'toolbar', * items : [ * { * text : 'Save' * } * ] * }, * { * xtype : 'toolbar', * weight : -10, * items : [ * { * text : 'Remove' * } * ] * } * ] * }); */ weight: null, /** * @cfg {Number|String} width * The width of this component. A numeric value will be interpreted as the number of * pixels; a string value will be treated as a CSS value with units. */ /** * @cfg {Ext.enums.Widget} xtype * **Note:** Only applies to {@link Ext.Component} derived classes when used as * a config in {@link Ext#define Ext.define}. * * This property provides a shorter alternative to creating objects than using a full * class name. Using `xtype` is the most common way to define component instances, * especially in a container. For example, the items in a form containing text fields * could be created explicitly like so: * * items: [ * Ext.create('Ext.form.field.Text', { * fieldLabel: 'Foo' * }), * Ext.create('Ext.form.field.Text', { * fieldLabel: 'Bar' * }), * Ext.create('Ext.form.field.Number', { * fieldLabel: 'Num' * }) * ] * * But by using `xtype`, the above becomes: * * items: [ * { * xtype: 'textfield', * fieldLabel: 'Foo' * }, * { * xtype: 'textfield', * fieldLabel: 'Bar' * }, * { * xtype: 'numberfield', * fieldLabel: 'Num' * } * ] * * When the `xtype` is common to many items, {@link Ext.container.Container#defaultType} * is another way to specify the `xtype` for all items that don't have an explicit `xtype`: * * defaultType: 'textfield', * items: [ * { fieldLabel: 'Foo' }, * { fieldLabel: 'Bar' }, * { fieldLabel: 'Num', xtype: 'numberfield' } * ] * * Each member of the `items` array is now just a "configuration object". These objects * are used to create and configure component instances. A configuration object can be * manually used to instantiate a component using {@link Ext#widget}: * * var text1 = Ext.create('Ext.form.field.Text', { * fieldLabel: 'Foo' * }); * * // or alternatively: * * var text1 = Ext.widget({ * xtype: 'textfield', * fieldLabel: 'Foo' * }); * * This conversion of configuration objects into instantiated components is done when * a container is created as part of its {Ext.container.AbstractContainer#initComponent} * process. As part of the same process, the `items` array is converted from its raw * array form into a {@link Ext.util.MixedCollection} instance. * * You can define your own `xtype` on a custom {@link Ext.Component component} by specifying * the `xtype` property in {@link Ext#define}. For example: * * Ext.define('MyApp.PressMeButton', { * extend: 'Ext.button.Button', * xtype: 'pressmebutton', * text: 'Press Me' * }); * * Care should be taken when naming an `xtype` in a custom component because there is * a single, shared scope for all xtypes. Third part components should consider using * a prefix to avoid collisions. * * Ext.define('Foo.form.CoolButton', { * extend: 'Ext.button.Button', * xtype: 'ux-coolbutton', * text: 'Cool!' * }); * * See {@link Ext.enums.Widget} for list of all available xtypes. * * @since 2.3.0 */ /** * @cfg {Number} tabIndex * DOM tabIndex attribute for this component's {@link #focusEl}. */ // *********************************************************************************** // End Config // *********************************************************************************** // </editor-fold> // <editor-fold desc="Properties"> // *********************************************************************************** // Begin Properties // *********************************************************************************** /** * @private */ allowDomMove: true, /** * @property {Boolean} autoGenId * `true` indicates an `id` was auto-generated rather than provided by configuration. * @private */ autoGenId: false, /** * @private */ borderBoxCls: Ext.baseCSSPrefix + 'border-box', /** * @property {Number} componentLayoutCounter * @private * The number of component layout calls made on this object. */ componentLayoutCounter: 0, /** * @property {String} contentPaddingProperty * The name of the padding property that is used by the layout to manage * padding. See {@link Ext.layout.container.Auto#managePadding managePadding} */ contentPaddingProperty: 'padding', /** * @private */ deferLayouts: false, /** * @property {Ext.Container} floatParent * **Only present for {@link #cfg-floating} Components which were inserted as child items * of Containers.** * * There are other similar relationships such as the {@link Ext.button.Button button} which * activates a {@link Ext.button.Button#cfg-menu menu}, or the * {@link Ext.menu.Item menu item} which activated a {@link Ext.menu.Item#cfg-menu submenu}, * or the {@link Ext.grid.column.Column column header} which activated the column menu. * * These differences are abstracted away by the {@link #up} method. * * Floating Components that are programmatically {@link Ext.Component#method-render rendered} * will not have a `floatParent` property. * * See {@link #cfg-floating} and {@link #zIndexManager} * @readonly */ /** * @property {Object} frameSize * @readonly * Indicates the width of any framing elements which were added within the encapsulating * element to provide graphical, rounded borders. See the {@link #frame} config. This * property is `null` if the component is not framed. * * This is an object containing the frame width in pixels for all four sides of the * Component containing the following properties: * * @property {Number} [frameSize.top=0] The width of the top framing element in pixels. * @property {Number} [frameSize.right=0] The width of the right framing element in pixels. * @property {Number} [frameSize.bottom=0] The width of the bottom framing element in pixels. * @property {Number} [frameSize.left=0] The width of the left framing element in pixels. * @property {Number} [frameSize.width=0] The total width of the left and right framing elements * in pixels. * @property {Number} [frameSize.height=0] The total height of the top and right bottom elements * in pixels. */ frameSize: null, /** * @private */ horizontalPosProp: 'left', /** * @property {Boolean} isComponent * `true` in this class to identify an object as an instantiated Component, or subclass thereof. */ isComponent: true, /** * @property {Boolean} _isLayoutRoot * Setting this property to `true` causes the {@link #isLayoutRoot} method to return * `true` and stop the search for the top-most component for a layout. * @protected */ _isLayoutRoot: false, /** * @private */ layoutSuspendCount: 0, /** * @cfg {Boolean} liquidLayout * Components that achieve their internal layout results using solely CSS with no JS * intervention must set this to true. This allows the component to opt out of the * layout run when used inside certain container layouts such as {@link * Ext.layout.container.Form Form} and {@link Ext.layout.container.Auto Auto} * resulting in a performance gain. The following components currently use liquid * layout (`liquidLayout: true`): * * - All Form Fields (subclasses of {@link Ext.form.field.Base}) * - {@link Ext.button.Button} * * It is important to keep in mind that components using liquidLayout do not fire * the following events: * * - {@link #event-resize} * - {@link #event-boxready} * * In addition, liquidLayout components do not call the following template methods: * * - {@link #method!afterComponentLayout} * - {@link #method!onBoxReady} * - {@link #method!onResize} * * Any component that needs to fire these events or to have these methods called during * its life cycle needs to set `liquidLayout` to `false`. The following example * demonstrates how to enable the resize event for a * {@link Ext.form.field.TextArea TextArea Field}: * * @example * var win = Ext.create({ * xtype: 'window', * title: 'Resize This Window!', * height: 100, * width: 200, * layout: 'anchor', * items: [{ * xtype: 'textarea', * anchor: '0 0', * liquidLayout: false // allows the textarea to fire "resize" * }] * }), * textfield = win.items.getAt(0); * * win.show(); * * textfield.on('resize', function(textfield, width, height) { * Ext.Msg.alert('Text Field Resized', 'width: ' + width + ', height: ' + height); * }); * * Use caution when setting `liquidLayout` to `false` as it carries a performance penalty * since it means the layout system must perform expensive DOM reads to determine the * Component's size. */ liquidLayout: false, /** * @property {Boolean} maskOnDisable * This is an internal flag that you use when creating custom components. By default this is set * to `true` which means that every component gets a mask when it's disabled. Components like * FieldContainer, FieldSet, Field, Button, Tab override this property to `false` * since they want to implement custom disable logic. */ maskOnDisable: true, /** * @property {Ext.Container} ownerCt * This Component's owner {@link Ext.container.Container Container} (is set automatically * when this Component is added to a Container). * * *Important.* This is not a universal upwards navigation pointer. It indicates the Container * which owns and manages this Component if any. There are other similar relationships such as * the {@link Ext.button.Button button} which activates a * {@link Ext.button.Button#cfg-menu menu}, or the {@link Ext.menu.Item menu item} which * activated a {@link Ext.menu.Item#cfg-menu submenu}, or the * {@link Ext.grid.column.Column column header} which activated the column menu. * * These differences are abstracted away by the {@link #up} method. * * **Note**: to access items within the Container see {@link #itemId}. * @readonly * @since 2.3.0 */ /** * @property {Boolean} rendered * Indicates whether or not the component has been rendered. * @readonly * @since 1.1.0 */ rendered: false, /** * @private */ rootCls: Ext.baseCSSPrefix + 'body', /** * @private */ scrollerCls: Ext.baseCSSPrefix + 'scroll-scroller', /** * @property {Object} scrollFlags * An object property which provides unified information as to which dimensions are * scrollable based upon the {@link #scrollable} settings (And for *views* of trees and * grids, the owning panel's {@link Ext.panel.Table#scroll scroll} setting). * * Note that if you set overflow styles using the {@link #style} config or * {@link Ext.panel.Panel#bodyStyle bodyStyle} config, this object does not include * that information. Use {@link #scrollable} if you need to access these flags. * * This object has the following properties: * @property {Boolean} scrollFlags.x `true` if this Component is scrollable * horizontally - style setting may be `'auto'` or `'scroll'`. * @property {Boolean} scrollFlags.y `true` if this Component is scrollable * vertically - style setting may be `'auto'` or `'scroll'`. * @property {Boolean} scrollFlags.both `true` if this Component is scrollable both * horizontally and vertically. * @property {String} scrollFlags.overflowX The `overflow-x` style setting, `'auto'` * or `'scroll'` or `''`. * @property {String} scrollFlags.overflowY The `overflow-y` style setting, `'auto'` * or `'scroll'` or `''`. * @readonly * @private */ _scrollFlags: { auto: { // x:auto, y:auto auto: { overflowX: 'auto', overflowY: 'auto', x: true, y: true, both: true }, // x:auto, y:false 'false': { overflowX: 'auto', overflowY: 'hidden', x: true, y: false, both: false }, // x:auto, y:scroll scroll: { overflowX: 'auto', overflowY: 'scroll', x: true, y: true, both: true } }, 'false': { // x:false, y:auto auto: { overflowX: 'hidden', overflowY: 'auto', x: false, y: true, both: false }, // x:false, y:false 'false': { overflowX: 'hidden', overflowY: 'hidden', x: false, y: false, both: false }, // x:false, y:scroll scroll: { overflowX: 'hidden', overflowY: 'scroll', x: false, y: true, both: false } }, scroll: { // x:scroll, y:auto auto: { overflowX: 'scroll', overflowY: 'auto', x: true, y: true, both: true }, // x:scroll, y:false 'false': { overflowX: 'scroll', overflowY: 'hidden', x: true, y: false, both: false }, // x:scroll, y:scroll scroll: { overflowX: 'scroll', overflowY: 'scroll', x: true, y: true, both: true } }, none: { overflowX: '', overflowY: '', x: false, y: false, both: false } }, _scrollableCfg: { x: { x: true, y: false }, y: { x: false, y: true }, horizontal: { x: true, y: false }, vertical: { x: false, y: true }, both: { x: true, y: true }, 'true': { x: true, y: true } }, validIdRe: Ext.validIdRe, // *********************************************************************************** // End Properties // *********************************************************************************** // </editor-fold> // <editor-fold desc="Events"> // *********************************************************************************** // Begin Events // *********************************************************************************** /** * @event afterlayoutanimation * This event first after a component's layout has been updated by a layout that * included animation (e.g., a {@link Ext.panel.Panel panel} in an * {@link Ext.layout.container.Accordion accordion} layout). * @param {Ext.Component} this * @since 6.0.0 */ /** * @event beforeactivate * Fires before a Component has been visually activated. Returning `false` from an event * listener can prevent the activate from occurring. * * **Note** This event is only fired if this Component is a child of a * {@link Ext.container.Container} that uses {@link Ext.layout.container.Card} as it's layout. * @param {Ext.Component} this */ /** * @event activate * Fires after a Component has been visually activated. * * **Note** This event is only fired if this Component is a child of a * {@link Ext.container.Container} that uses {@link Ext.layout.container.Card} as it's layout * or this Component is a floating Component. * @param {Ext.Component} this */ /** * @event beforedeactivate * Fires before a Component has been visually deactivated. Returning `false` from an event * listener can prevent the deactivate from occurring. * * **Note** This event is only fired if this Component is a child of a * {@link Ext.container.Container} that uses {@link Ext.layout.container.Card} as it's layout. * @param {Ext.Component} this */ /** * @event deactivate * Fires after a Component has been visually deactivated. * * **Note** This event is only fired if this Component is a child of a * {@link Ext.container.Container} that uses {@link Ext.layout.container.Card} as it's layout * or this Component is a floating Component. * @param {Ext.Component} this */ /** * @event added * Fires after a Component had been added to a Container. * @param {Ext.Component} this * @param {Ext.container.Container} container Parent Container * @param {Number} pos position of Component * @since 3.4.0 */ /** * @event disable * Fires after the component is disabled. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event enable * Fires after the component is enabled. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event beforeshow * Fires before the component is shown when calling the {@link Ext.Component#method-show show} * method. Return `false` from an event handler to stop the show. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event show * Fires after the component is shown when calling the {@link Ext.Component#method-show show} * method. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event beforehide * Fires before the component is hidden when calling the {@link Ext.Component#method-hide hide} * method. Return `false` from an event handler to stop the hide. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event hide * Fires after the component is hidden. Fires after the component is hidden when calling * the {@link Ext.Component#method-hide hide} method. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event removed * Fires when a component is removed from an Ext.container.Container * @param {Ext.Component} this * @param {Ext.container.Container} ownerCt Container which holds the component * @since 3.4.0 */ /** * @event beforerender * Fires before the component is {@link #rendered}. Return `false` from an event handler * to stop the {@link #method-render}. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event render * Fires after the component markup is {@link #rendered}. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event afterrender * Fires after the component rendering is finished. * * The `afterrender` event is fired after this Component has been {@link #rendered}, * been post-processed by any `afterRender` method defined for the Component. * @param {Ext.Component} this * @since 3.4.0 */ /** * @event boxready * Fires *one time* - after the component has been laid out for the first time at its initial * size. * * This event does not fire on components that use {@link #cfg-liquidLayout}, such as * {@link Ext.button.Button Buttons} and {@link Ext.form.field.Base Form Fields}. * @param {Ext.Component} this * @param {Number} width The initial width. * @param {Number} height The initial height. */ /** * @event beforedestroy * Fires before the component is destroyed. * * **Note:** This event should not be used to try to veto the destruction sequence by returning * `false`, even though this is often permitted in other "before" events. Doing so will have * unpredictable side-effects and can result in partially destroyed objects. Instead look to * other events like {@link Ext.panel.Panel#event!beforeclose beforeclose} that occur prior to * the call to the {@link Ext.Base#method!destroy destroy} method. * * @param {Ext.Component} this * @since 1.1.0 */ /** * @event destroy * Fires after the component is {@link #method-destroy}ed. * @param {Ext.Component} this * @since 1.1.0 */ /** * @event resize * Fires after the component is resized. Note that this does *not* fire when the component * is first laid out at its initial size. To hook that point in the life cycle, use the * {@link #boxready} event. * * This event does not fire on components that use {@link #cfg-liquidLayout}, such as * {@link Ext.button.Button Buttons} and {@link Ext.form.field.Base Form Fields}. * @param {Ext.Component} this * @param {Number} width The new width that was set. * @param {Number} height The new height that was set. * @param {Number} oldWidth The previous width. * @param {Number} oldHeight The previous height. */ /** * @event move * Fires after the component is moved. * @param {Ext.Component} this * @param {Number} x The new x position. * @param {Number} y The new y position. */ // *********************************************************************************** // End Events // *********************************************************************************** // </editor-fold> /** * Creates new Component. * @param {Ext.dom.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, i, len, xhooks, controller, autoScroll, overflowX, overflowY, scrollable; 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 }; } // Make initialConfig available early so that config getters may access it /** * @property {Object} initialConfig * @readonly * The config object passed to the constructor during Component creation. */ me.initialConfig = config; me.$iid = ++Ext.$nextIid; // We want to determine very early on whether or not we are a reference holder, // so peek at either the incoming config or the class config to see if we have // a controller defined. if ((config && config.controller) || me.config.controller) { me.referenceHolder = true; } // Ensure that we have an id early so that config getters may access it me.getId(); me.protoEl = new Ext.util.ProtoElement(); //<debug> me.$calledInitConfig = true; //</debug> me.initConfig(config); //<debug> delete me.$calledInitConfig; //</debug> if (me.scrollable == null) { autoScroll = me.autoScroll; if (autoScroll) { scrollable = !!autoScroll; } else { overflowX = me.overflowX; overflowY = me.overflowY; if (overflowX || overflowY) { scrollable = { x: (overflowX && overflowX !== 'hidden') ? overflowX : false, y: (overflowY && overflowY !== 'hidden') ? overflowY : false }; } } if (scrollable) { me.setScrollable(scrollable); } } xhooks = me.xhooks; if (xhooks) { delete me.xhooks; Ext.override(me, xhooks); } me.mixins.elementCt.constructor.call(me); //<debug> if (!me.validIdRe.test(me.id)) { Ext.raise('Invalid component "id": "' + me.id + '"'); } if (!me.validIdRe.test(me.itemId)) { Ext.raise('Invalid component "itemId": "' + me.itemId + '"'); } //</debug> me.setupProtoEl(); // initComponent, beforeRender, or event handlers may have set the style // or `cls` property since the `protoEl` was set up so we must apply styles // and classes here too. if (me.cls) { me.initialCls = me.cls; me.protoEl.addCls(me.cls); } if (me.style) { me.initialStyle = me.style; me.protoEl.setStyle(me.style); } me.renderData = me.renderData || {}; me.initComponent(); // initComponent gets a chance to change the id property before registering if (!me.preventRegister) { Ext.ComponentManager.register(me); } me.mixins.state.constructor.call(me); me.addStateEvents('resize'); controller = me.getController(); if (controller) { controller.init(me); } // Move this into Observable? if (me.plugins) { for (i = 0, len = me.plugins.length; i < len; i++) { me.plugins[i] = me.initPlugin(me.plugins[i]); } } me.loader = me.getLoader(); if (me.disabled) { me.disabled = false; me.disable(true); } if (me.renderTo) { me.render(me.renderTo); // EXTJSIV-1935 - should be a way to do afterShow or something, but that // won't work. Likewise, rendering hidden and then showing (w/autoShow) has // implications to afterRender so we cannot do that. } // Auto show only works unilaterally on *uncontained* Components. // If contained, then it is the Container's responsibility to do the showing // at next layout time. if (me.autoShow && !me.$initParent) { me.show(); } //<debug> if (Ext.isDefined(me.disabledClass)) { if (Ext.isDefined(Ext.global.console)) { Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. ' + 'Please use disabledCls.'); } me.disabledCls = me.disabledClass; delete me.disabledClass; } //</debug> // 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); } }, beforeInitConfig: function() { //<debug> if (!this.$calledInitConfig) { Ext.raise('initConfig should not be called by subclasses, it will be ' + 'called by Ext.Component'); } //</debug> this.mixins.observable.constructor.call(this); }, // <editor-fold desc="Component Methods"> // *********************************************************************************** // Begin Component Methods // *********************************************************************************** /** * Adds a CSS class to the top level element representing this component. * @param {String/String[]} cls The CSS class name to add. * @return {Ext.Component} Returns the Component to allow method chaining. */ addCls: function(cls) { var me = this, el = me.rendered ? me.el : me.protoEl; el.addCls.apply(el, arguments); return me; }, /** * Adds a `cls` to the `uiCls` array, which will also call {@link #addUIClsToElement} * and adds to all elements of this component. * @param {String/String[]} classes A string or an array of strings to add to the `uiCls`. * @param {Boolean} [skip] `true` to skip adding it to the class and do it later * (via the return). */ addClsWithUI: function(classes, skip) { var me = this, clsArray = [], i = 0, uiCls = me.uiCls = Ext.Array.clone(me.uiCls), activeUI = me.activeUI, length, cls; if (typeof classes === "string") { classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes); } length = classes.length; for (; i < length; i++) { cls = classes[i]; if (cls && !me.hasUICls(cls)) { uiCls.push(cls); // We can skip this bit if there isn't an activeUI because // we'll be called again from setUI if (activeUI) { clsArray = clsArray.concat(me.addUIClsToElement(cls)); } } } if (skip !== true && activeUI) { me.addCls(clsArray); } return clsArray; }, /** * Called by the layout system after the Component has been laid out. * * This method is not called on components that use {@link #cfg-liquidLayout}, such as * {@link Ext.button.Button Buttons} and {@link Ext.form.field.Base Form Fields}. * * @param {Number} width The width that was set * @param {Number} height The height that was set * @param {Number/undefined} oldWidth The old width, or `undefined` if this was the initial * layout. * @param {Number/undefined} oldHeight The old height, or `undefined` if this was the initial * layout. * * @template * @protected */ afterComponentLayout: function(width, height, oldWidth, oldHeight) { var me = this, scroller = me.scrollable; if (++me.componentLayoutCounter === 1) { me.afterFirstLayout(width, height); } else if (me.manageLayoutScroll && scroller) { scroller.restoreState(); } if (width !== oldWidth || height !== oldHeight) { me.onResize(width, height, oldWidth, oldHeight); } if (me.floating) { me.onAfterFloatLayout(); } }, /** * @protected * Adds a plugin. May be called at any time in the component's life cycle. */ addPlugin: function(plugin) { var me = this; plugin = me.constructPlugin(plugin); if (me.plugins) { me.plugins.push(plugin); } else { me.plugins = [ plugin ]; } if (me.pluginsInitialized) { me.initPlugin(plugin); } return plugin; }, /** * Save a property to the given state object if it is not its default or configured * value. * * @param {Object} state The state object. * @param {String} propName The name of the property on this object to save. * @param {String} [value] The value of the state property (defaults to `this[propName]`). * @return {Object} The state object or a new object if state was `null` and the property * was saved. * @protected */ addPropertyToState: function(state, propName, value) { var me = this, len = arguments.length; // If the property is inherited, it is a default and we don't want to save it to // the state, however if we explicitly specify a value, always save it if (len === 3 || me.hasOwnProperty(propName)) { if (len < 3) { value = me[propName]; } // If the property has the same value as was initially configured, again, we // don't want to save it. if (value !== me.initialConfig[propName]) { (state || (state = {}))[propName] = value; } } return state; }, /** * Method which adds a specified UI + `uiCls` to the components element. Can be overridden * to add the UI to more than just the component's element. * @param {String} uiCls The UI class to add to the element. * @protected */ addUIClsToElement: function(uiCls) { var me = this, baseClsUI = me.baseCls + '-' + me.ui + '-' + uiCls, result = [Ext.baseCSSPrefix + uiCls, me.baseCls + '-' + uiCls, baseClsUI], childEls, childElName, el, suffix; if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { // Loop through each frame element, and if they are defined add the ui: baseClsUI += '-'; childEls = me.getChildEls(); for (childElName in childEls) { suffix = childEls[childElName].frame; if (suffix && suffix !== true) { el = me[childElName]; if (el) { el.addCls(baseClsUI + suffix); } } } } return result; }, /** * Method which removes a specified UI + `uiCls` from the components element. The `cls` * which is added to the element will be: `this.baseCls + '-' + ui + uiCls`. * @param {String} uiCls The UI class to remove from the element. * @protected */ removeUIClsFromElement: function(uiCls) { var me = this, baseClsUI = me.baseCls + '-' + me.ui + '-' + uiCls, result = [ Ext.baseCSSPrefix + uiCls, me.baseCls + '-' + uiCls, baseClsUI ], childEls, childElName, el, suffix; if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { // Loop through each frame element, and if they are defined remove the ui: baseClsUI += '-'; childEls = me.getChildEls(); for (childElName in childEls) { suffix = childEls[childElName].frame; if (suffix && suffix !== true) { el = me[childElName]; if (el) { el.removeCls(baseClsUI + suffix); } } } } return result; }, /** * @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 }; }, /** * 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(callback, scope) { var me = this, container = me.ownerFocusableContainer; // Top level focusEnter is only valid when a floating component stack is visible. delete me.getInherited().topmostFocusEvent; me.hiddenByLayout = null; // Only lay out if there is an owning layout which might be affected by the hide if (me.ownerLayout) { me.updateLayout({ isRoot: false }); } if (container && !container.onFocusableChildHide.$nullFn) { container.onFocusableChildHide(me); } me.fireHierarchyEvent('hide'); me.fireEvent('hide', me); // Have to fire callback the last, because it may destroy the Component // and firing subsequent events will become impossible. Strictly speaking, // hide event handler above could have destroyed the Component too, but // in such case it is the responsibility of the callback to accommodate. Ext.callback(callback, scope || me); }, /** * Template method called after a Component has been positioned. * * @param {Number} x * @param {Number} y * * @template * @protected */ afterSetPosition: function(x, y) { var me = this; me.onPosition(x, y); if (me.hasListeners.move) { me.fireEvent('move', me, x, y); } }, /** * Invoked after the Component is shown (after #onShow is called). * * Gets passed the same parameters as #show. * * @param {String/Ext.dom.Element} [animateTarget] * @param {Function} [callback] * @param {Object} [scope] * @template * @protected */ afterShow: function(animateTarget, callback, scope) { var me = this, myEl = me.el, zim = me.zIndexManager, fromBox, toBox, ghostPanel; // Default to configured animate target if none passed animateTarget = me.getAnimateTarget(animateTarget); // We are going to churn the zIndex stack, so suspend its reactions. if (zim) { zim.suspendReflow(); } // 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) { toBox = { x: myEl.getX(), y: myEl.getY(), width: myEl.dom.offsetWidth, height: myEl.dom.offsetHeight }; fromBox = { x: animateTarget.getX(), y: animateTarget.getY(), width: animateTarget.dom.offsetWidth, height: animateTarget.dom.offsetHeight }; // Will move to front with underlying mask, and focus if necessary. me.fireHierarchyEvent('show'); // Ghost will brifly be topmost, but it is focusable: false // And ghosting does not disturb focus. ghostPanel = me.ghost(); ghostPanel.el.stopAnimation(); // Shunting it offscreen immediately, *before* the Animation class grabs it // ensure no flicker. ghostPanel.setX(-10000); me.ghostBox = toBox; ghostPanel.el.animate({ from: fromBox, to: toBox, listeners: { beforeanimate: function() { if (me.modal) { ghostPanel.toFront(); } }, afteranimate: function() { if (!me.destroying) { ghostPanel.componentLayout.lastComponentSize = null; me.unghost(); me.ghostBox = null; me.onShowComplete(callback, scope); } } } }); } else { me.onShowComplete(callback, scope); me.fireHierarchyEvent('show'); } // Take a run through the zIndex stack ensuring modal, focus and topmost is correct if (zim) { zim.resumeReflow(true); } }, animate: function(animObj) { var me = this, hasToWidth, hasToHeight, toHeight, toWidth, to, clearWidth, clearHeight, curWidth, w, curHeight, h, isExpanding, wasConstrained, wasConstrainedHeader, passedCallback, oldOverflow; animObj = animObj || {}; to = animObj.to || {}; if (Ext.fx.Manager.hasFxBlock(me.id)) { return me; } hasToWidth = Ext.isDefined(to.width); if (hasToWidth) { toWidth = Ext.Number.constrain(to.width, me.minWidth, me.maxWidth); } hasToHeight = Ext.isDefined(to.height); if (hasToHeight) { toHeight = Ext.Number.constrain(to.height, me.minHeight, me.maxHeight); } // Special processing for animating Component dimensions. if (!animObj.dynamic && (hasToWidth || hasToHeight)) { curWidth = (animObj.from ? animObj.from.width : undefined) || me.getWidth(); w = curWidth; curHeight = (animObj.from ? animObj.from.height : undefined) || me.getHeight(); h = curHeight; isExpanding = false; if (hasToHeight && toHeight > curHeight) { h = toHeight; isExpanding = true; } if (hasToWidth && toWidth > curWidth) { w = toWidth; isExpanding = true; } // During animated sizing, overflow has to be hidden to clip expanded content if (hasToHeight || hasToWidth) { oldOverflow = me.el.getStyle('overflow'); if (oldOverflow !== 'hidden') { me.el.setStyle('overflow', 'hidden'); } } // If any dimensions are being increased, we must resize the internal structure // of the Component, but then clip it by sizing its encapsulating element // back to original dimensions. // The animation will then progressively reveal the larger content. if (isExpanding) { clearWidth = !Ext.isNumber(me.width); clearHeight = !Ext.isNumber(me.height); // Lay out this component at the new, larger size to get the internals correctly // laid out. Then size the encapsulating **Element** back down to size. // We will then just animate the element to reveal the correctly laid out content. me.setSize(w, h); me.el.setSize(curWidth, curHeight); if (clearWidth) { delete me.width; } if (clearHeight) { delete me.height; } } if (hasToWidth) { to.width = toWidth; } if (hasToHeight) { to.height = toHeight; } } // No constraining during the animate - the "to" size has already been calculated // with respect to all settings. // Arrange to reinstate any constraining after the animation has completed wasConstrained = me.constrain; wasConstrainedHeader = me.constrainHeader; if (wasConstrained || wasConstrainedHeader) { me.constrain = me.constrainHeader = false; passedCallback = animObj.callback; animObj.callback = function() { me.constrain = wasConstrained; me.constrainHeader = wasConstrainedHeader; // Call the original callback if any if (passedCallback) { passedCallback.call(animObj.scope || me, arguments); } if (oldOverflow !== 'hidden') { me.el.setStyle('overflow', oldOverflow); } }; } return me.mixins.animate.animate.apply(me, arguments); }, applyScrollable: function(scrollable, oldScrollable) { var me = this, rendered = me.rendered, scrollableCfg; if (scrollable) { if (scrollable === true || typeof scrollable === 'string') { scrollableCfg = me._scrollableCfg[scrollable]; //<debug> if (!scrollableCfg) { Ext.raise("'" + scrollable + "' is not a valid value for 'scrollable'"); } //</debug> scrollable = scrollableCfg; } if (oldScrollable) { oldScrollable.setConfig(scrollable); scrollable = oldScrollable; } else { scrollable = Ext.Object.chain(scrollable); // don't mutate the user's config if (rendered) { // we create the scroller without an element by default (because the // element is not available at configuration time) and then assign // the element in onBoxReady. If we got here it means the scroller // is being configured after render, so we need to make sure the // element is in its config object scrollable.element = me.getOverflowEl(); } scrollable = Ext.scroll.Scroller.create(scrollable, me.scrollableType); scrollable.component = me; } } // We are disabling scrolling for this Component. else if (oldScrollable) { scrollable = oldScrollable; oldScrollable.setConfig({ x: false, y: false }); } if (me.rendered && !me.destroying && !me.destroyed) { if (scrollable) { me.getOverflowStyle(); // refresh the scrollFlags } else { me.scrollFlags = me._scrollFlags.none; } me.updateLayout(); } return scrollable; }, /** * This method is required by the Scroller to return the scrollable client region * @return {Ext.util.Region} The scrolling viewport region. * @private */ getScrollableClientRegion: function() { return this.getScrollable().getElement().getClientRegion(); }, applyTouchAction: function(touchAction, oldTouchAction) { if (oldTouchAction != null) { touchAction = Ext.merge({}, oldTouchAction, touchAction); } return touchAction; }, /** * Invoked before the Component is destroyed. * This method is deprecated, override {@link #onDestroy} instead. * * @method * @template * @protected * @deprecated 6.2.0 Please override {@link #onDestroy} instead */ beforeDestroy: Ext.emptyFn, /** * Occurs before componentLayout is run. In previous releases, this method could * return `false` to prevent its layout but that is not supported in Ext JS 4.1 or * higher. This method is simply a notification of the impending layout to give the * component a chance to adjust the DOM. Ideally, DOM reads should be avoided at this * time to reduce expensive document reflows. * * @template * @protected */ beforeLayout: function() { if (this.floating) { this.onBeforeFloatLayout(); } }, /** * @private * Template method called before a Component is positioned. * * Ensures that the position is adjusted so that the Component is constrained if so configured. */ beforeSetPosition: function(x, y, animate) { var me = this, pos = null, x0, hasX, hasY, adj; // Decode members of x if x is an array or an object. // If it is numeric (including zero), we need do nothing. if (x) { // Position in first argument as an array of [x, y] if (Ext.isNumber(x0 = x[0])) { animate = y; y = x[1]; x = x0; } // Position in first argument as object w/ x & y properties else if ((x0 = x.x) !== undefined) { animate = y; y = x.y; x = x0; } } if (me.constrain || me.constrainHeader) { pos = me.calculateConstrainedPosition(null, [x, y], true); if (pos) { x = pos[0]; y = pos[1]; } } hasX = (x !== undefined); hasY = (y !== undefined); if (hasX || hasY) { // The component's position is the position it was told to be at. // If it is contained, adjustPosition will add the floatParent's offsets. me.x = x; me.y = y; adj = me.adjustPosition(x, y); // Set up the return info and store the position in this object pos = { x: adj.x, y: adj.y, anim: animate, hasX: hasX, hasY: hasY }; } return pos; }, /** * Invoked before the Component is shown. * * @method * @template * @protected */ beforeShow: Ext.emptyFn, /** * Bubbles up the component/container hierarchy, 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; }, clearListeners: function() { var me = this; me.mixins.observable.clearListeners.call(me); me.mixins.componentDelegation.clearDelegatedListeners.call(me); }, /** * 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 || {}; // eslint-disable-next-line vars-on-top 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); }, /** * Destroys the Component. This method **must not** be overridden because Component * destruction sequence is conditional; if a `beforedestroy` handler returns `false` * we must abort destruction. * * To add extra functionality to destruction time in a subclass, override the * {@link #doDestroy} method. * * @since 1.1.0 */ destroy: function() { var me = this; if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) { // isDestroying added for compat reasons me.isDestroying = me.destroying = true; me.doDestroy(); // We need to defer clearing listeners until after doDestroy() completes, // to let the interested parties fire events until the very end. me.clearListeners(); // isDestroying added for compat reasons me.isDestroying = me.destroying = false; me.callParent(); // Ext.Base // ComponentDelegation mixin does not install "after" interceptor on the // base class destructor; Observable mixin does install the interceptor // but cannot destroy itself automatically because Components are // conditionally destructible. me.mixins.componentDelegation.destroyComponentDelegation.call(me); me.mixins.observable.destroyObservable.call(me, true); } }, /** * Perform the actual destruction sequence. * * As a rule of thumb, subclasses should destroy their child Components and/or other objects * before calling parent method. Any object references will be nulled after this method * has finished, to prevent the possibility of memory leaks. * * @private * @since 6.2.0 */ doDestroy: function() { var me = this, focusableContainer = me.ownerFocusableContainer, container = me.container, selectors = me.renderSelectors, selector, ownerCt, el; ownerCt = me.floatParent || me.ownerCt; if (me.floating) { delete me.floatParent; // 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.WindowManager instance. // It can also happen that Container's zIndexManager is destroyed before this. if (me.zIndexManager && !me.zIndexManager.destroyed) { me.zIndexManager.unregister(me); } // Some components may set floating as config object, which will be nulled // in the base destructor. We need this property in Containers, so set it // to Boolean instead. me.floating = true; } me.removeBindings(); if (!me.beforeDestroy.$emptyFn) { me.beforeDestroy(); } me.destroyBindable(); if (ownerCt && ownerCt.remove) { ownerCt.remove(me, { destroy: false }); } me.stopAnimation(); // Ensure that any ancillary components are destroyed. if (me.rendered) { Ext.destroy( me.loadMask, me.dd, me.resizer, me.proxy, me.proxyWrap, me.resizerComponent, me.scrollable, me.contentEl ); } if (focusableContainer && !focusableContainer.onFocusableChildDestroy.$nullFn) { focusableContainer.onFocusableChildDestroy(me); } if (me.focusable) { me.destroyFocusable(); } // Destroying the floatingItems ZIndexManager will also destroy descendant floating // Components Ext.destroy( me.componentLayout, me.loadMask, me.floatingDescendants ); if (!me.onDestroy.$emptyFn) { me.onDestroy(); } // Attempt to destroy all plugins Ext.destroy(me.plugins); if (me.rendered) { Ext.Component.cancelLayout(me, true); } me.componentLayout = null; if (me.hasListeners.destroy) { me.fireEvent('destroy', me); } if (!me.preventRegister) { Ext.ComponentManager.unregister(me); } me.mixins.state.destroy.call(me); if (me.floating) { me.onFloatDestroy(); } // make sure we clean up the element references after removing all events if (me.rendered) { if (me.showListenerIE) { me.showListenerIE.destroy(); me.showListenerIE = null; } if (!me.preserveElOnDestroy) { me.el.destroy(); } // We don't own the container element so can't just destroy it as that would // remove it from the DOM; we have to remove the Element instance from cache // though. if (me.collectContainerElement && container) { if (!container.destroyed) { container.collect(); } me.container = null; } me.el.component = null; me.mixins.elementCt.destroy.call(me); // removes childEls if (selectors) { for (selector in selectors) { if (selectors.hasOwnProperty(selector)) { el = me[selector]; if (el) { // in case any other code may have already removed it delete me[selector]; el.destroy(); } } } } // This is a very special boolean that warrants explicit clearing me.rendered = false; } }, /** * Disable the component. * @param {Boolean} [silent=false] Passing `true` will suppress the `disable` event * from being fired. * @param fromParent (private) * @since 1.1.0 */ disable: function(silent, fromParent) { var me = this, container = me.ownerFocusableContainer, inherited = me.getInherited(); if (!fromParent) { inherited.disabled = true; me.savedDisabled = true; } if (me.maskOnDisable) { inherited.disableMask = true; } if (!me.disabled) { if (container && !container.beforeFocusableChildDisable.$nullFn) { container.beforeFocusableChildDisable(me); } me.addCls(me.disabledCls); me.disabled = true; if (me.rendered) { me.onDisable(); } else { me.disableOnRender = true; } if (silent !== true) { me.fireEvent('disable', me); } if (container && !container.onFocusableChildDisable.$nullFn) { container.onFocusableChildDisable(me); } } return me; }, doFireEvent: function(eventName, args, bubbles) { var me = this, ret; ret = me.mixins.observable.doFireEvent.call(me, eventName, args, bubbles); // The Component instance can be destroyed in the handler, in which case // we can't fire delegated events on it anymore. if (ret !== false && !me.destroyed) { ret = me.mixins.componentDelegation.doFireDelegatedEvent.call(me, eventName, args); } return ret; }, /** * Enable the component * @param {Boolean} [silent=false] Passing `true` will suppress the `enable` event * from being fired. * @param fromParent (private) * @since 1.1.0 */ enable: function(silent, fromParent) { var me = this, container = me.ownerFocusableContainer, inherited = me.getInherited(); if (!fromParent) { delete me.getInherited().disabled; me.savedDisabled = false; } if (me.maskOnDisable) { delete inherited.disableMask; } if (me.disabled) { // A parent is asking us to enable, but if we were disabled directly, keep // our current state if (!(fromParent && inherited.hasOwnProperty('disabled'))) { if (container && !container.beforeFocusableChildEnable.$nullFn) { container.beforeFocusableChildEnable(me); } me.disableOnRender = false; me.removeCls(me.disabledCls); me.disabled = false; if (me.rendered) { me.onEnable(); } if (silent !== true) { me.fireEvent('enable', me); } if (container && !container.onFocusableChildEnable.$nullFn) { container.onFocusableChildEnable(me); } } } return me; }, /** * 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 owner chain until we don't have one, or we find an ancestor // which matches using the selector function. for (p = this.getRefOwner(); p && !fn(p, this); p = p.getRefOwner()) { // 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.self === xtype || p.constructor === xtype; }) : this.up(xtype); }, /** * Retrieves plugin from this component's collection by its `ptype`. * * var grid = Ext.create('Ext.grid.Panel', { * store: { * fields: ['name'], * data: [{ * name: 'Scott Pilgrim' * }] * }, * columns: [{ * header: 'Name', * dataIndex: 'name', * editor: 'textfield', * flex: 1 * }], * selType: 'cellmodel', * plugins: [{ * ptype: 'cellediting', * clicksToEdit: 1, * id: 'myplugin' * }], * height: 200, * width: 400, * renderTo: Ext.getBody() * }); * * grid.findPlugin('cellediting'); // the cellediting plugin * * **Note:** See also {@link #getPlugin} * * @param {String} ptype The Plugin's `ptype` as specified by the class's * {@link Ext.Class#cfg-alias alias} configuration. * @return {Ext.plugin.Abstract} plugin instance or `undefined` if not found */ findPlugin: function(ptype) { var i, plugins = this.plugins, ln = plugins && plugins.length; for (i = 0; i < ln; i++) { if (plugins[i].ptype === ptype) { return plugins[i]; } } }, getAnimateTarget: function(target) { target = target || this.animateTarget; if (target) { target = target.isComponent ? target.getEl() : Ext.get(target); } return target || null; }, /** * @protected * Implements an upward event bubbling policy. By default a Component bubbles events * up to its {@link #getRefOwner reference owner}. * * Component subclasses may implement a different bubbling strategy by overriding this method. */ getBubbleTarget: function() { return this.getRefOwner(); }, getComponentLayout: function() { var me = this; if (!me.componentLayout || !me.componentLayout.isLayout) { me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent')); } return me.componentLayout; }, /** * Retrieves the top level element representing this component. * @return {Ext.dom.Element} * @since 1.1.0 */ getEl: function() { return this.el; }, /** * Gets the current height of the component's underlying element. * @return {Number} */ getHeight: function() { return this.el.getHeight(); }, /** * Called by `getInherited` to initialize the inheritedState the first time it is * requested. * @protected */ initInheritedState: function(inheritedState) { var me = this, layout = me.componentLayout; if (me.hidden) { inheritedState.hidden = true; } if (me.collapseImmune) { inheritedState.collapseImmune = true; } if (me.modelValidation !== undefined) { inheritedState.modelValidation = me.modelValidation; } if (me.savedDisabled) { inheritedState.disabled = true; } me.mixins.bindable.initInheritedState.call(me, inheritedState); if (layout && layout.initInheritedState) { layout.initInheritedState(inheritedState); } }, /** * Retrieves the `id` of this component. Will auto-generate an `id` if one has not already * been set. * @return {String} */ getId: function() { var me = this, xtype; // If we have no id, attempt to gather it from our configuration. // Autogenerate it if none was configured. if (!(me.id || (me.id = me.initialConfig.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; }, /** * Returns the value of {@link #itemId} assigned to this component, or when that * is not set, returns the value of {@link #id}. * @return {String} */ getItemId: function() { return this.itemId || this.id; }, /** * Gets the {@link Ext.ComponentLoader} for this Component. * @return {Ext.ComponentLoader} The loader instance, null if it doesn't exist. */ getLoader: function() { var me = this, loader = me.loader; if (loader) { if (!loader.isLoader) { me.loader = new Ext.ComponentLoader(Ext.apply({ target: me }, loader)); } else { loader.setTarget(me); } return me.loader; } return null; }, /** * @protected * Returns the element which is masked by the {@link #mask} method, or into which the * {@link #setLoading LoadMask} is rendered into. * * The default implementation uses the {@link #maskElement} configuration to access * the Component's child element by name. By default, {@link #maskElement} is `null` which means * that `null` is returned from this method indicating that the mask needs to be rendered into * the document because component structure should not be contaminated by mask elements. * * Some subclasses may override this method if they have knowledge about external structures * where a mask could usefully be rendered. * * For example a {@link Ext.view.Table GridView} will request that its owning * {@link Ext.panel.Table GridPanel} be masked. The GridPanel will have its own implementation * of `getMaskTarget` which will return the element dictated by its own {@link #maskElement} * Panels use `"el"` as their {@link #maskElement} by default, but that could be overridden * to be `"body"` to leave toolbars and the header mouse-accessible. * */ getMaskTarget: function() { return this.maskElement ? this[this.maskElement] : null; }, /** * Retrieves a plugin from this component's collection by its `id`. * * var grid = Ext.create('Ext.grid.Panel', { * store: { * fields: ['name'], * data: [{ * name: 'Scott Pilgrim' * }] * }, * columns: [{ * header: 'Name', * dataIndex: 'name', * editor: 'textfield', * flex: 1 * }], * selType: 'cellmodel', * plugins: [{ * ptype: 'cellediting', * clicksToEdit: 1, * id: 'myplugin' * }], * height: 200, * width: 400, * renderTo: Ext.getBody() * }); * * grid.getPlugin('myplugin'); // the cellediting plugin * * **Note:** See also {@link #findPlugin}. Prior to 6.2.0 the plugin had to have a * `{@link Ext.plugin.Abstract#pluginId pluginId}` property but this can now be just * `{@link Ext.plugin.Abstract#id id}`. Both are supported (so plugins with a * matching `pluginId` are still found) but `id` is preferred. * * @param {String} id The `id` set on the plugin config object. * @return {Ext.plugin.Abstract} plugin instance or `null` if not found */ getPlugin: function(id) { var i, plugins = this.plugins, ln = plugins && plugins.length, plugin; for (i = 0; i < ln; i++) { plugin = plugins[i]; // pre-6.2 we only considered pluginId property... if (plugin.id === id || plugin.pluginId === id) { return plugin; } } return null; }, /** * 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, xy, isContainedFloater = me.isContainedFloater(), floatParentBox; // Local position for non-floaters means element's local position if ((local === true) && !isContainedFloater) { return [me.getLocalX(), me.getLocalY()]; } xy = me.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; }, /** * Returns the "x" scroll position for this component. Only applicable for * {@link #scrollable} components * @return {Number} */ getScrollX: function() { var scroller = this.getScrollable(); return scroller ? scroller.getPosition().x : 0; }, /** * Returns the "y" scroll position for this component. Only applicable for * {@link #scrollable} components * @return {Number} */ getScrollY: function() { var scroller = this.getScrollable(); return scroller ? scroller.getPosition().y : 0; }, /** * Gets the current size of the component's underlying element. * @param {Boolean} [contentSize] true to get the width/size minus borders and padding * @return {Object} An object containing the element's size: * @return {Number} return.width * @return {Number} return.height */ getSize: function(contentSize) { return this.el.getSize(contentSize); }, /** * Returns an object that describes how this component's width and height are managed. * All of these objects are shared and should not be modified. * * @return {Object} The size model for this component. * @return {Ext.layout.SizeModel} return.width The {@link Ext.layout.SizeModel size model} * for the width. * @return {Ext.layout.SizeModel} return.height The {@link Ext.layout.SizeModel size model} * for the height. * @protected */ getSizeModel: function(ownerCtSizeModel) { var me = this, models = Ext.layout.SizeModel, ownerContext = me.componentLayout.ownerContext, width = me.width, height = me.height, typeofWidth, typeofHeight, hasPixelWidth, hasPixelHeight, hasWidthStyle, hasHeightStyle, heightModel, ownerLayout, policy, shrinkWrap, topLevel, widthModel, // floating === a floating Component, floated === a border layout's slideout view // of a region. isFloating = me.floating || me.floated; if (ownerContext) { // If we are in the middle of a running layout, always report the current, // dynamic size model rather than recompute it. This is not (only) a time // saving thing, but a correctness thing since we cannot get the right answer // otherwise. widthModel = ownerContext.widthModel; heightModel = ownerContext.heightModel; } if (!widthModel || !heightModel) { hasPixelWidth = ((typeofWidth = typeof width) === 'number'); hasPixelHeight = ((typeofHeight = typeof height) === 'number'); topLevel = isFloating || !(ownerLayout = me.ownerLayout); // Floating or no owner layout, e.g. rendered using renderTo if (topLevel) { policy = Ext.layout.Layout.prototype.autoSizePolicy; shrinkWrap = isFloating ? 3 : me.shrinkWrap; if (hasPixelWidth) { widthModel = models.configured; } if (hasPixelHeight) { heightModel = models.configured; } } else { policy = ownerLayout.getItemSizePolicy(me, ownerCtSizeModel); shrinkWrap = ownerLayout.isItemShrinkWrap(me); } if (ownerContext) { ownerContext.ownerSizePolicy = policy; } shrinkWrap = (shrinkWrap === true) ? 3 : (shrinkWrap || 0); // false->0, true->3 // Now that we have shrinkWrap as a 0-3 value, we need to turn off shrinkWrap // bits for any dimension that has a configured size not in pixels. These must // be read from the DOM. // if (topLevel && shrinkWrap) { if (width && typeofWidth === 'string') { shrinkWrap &= 2; // percentage, "30em" or whatever - not width shrinkWrap hasWidthStyle = true; } if (height && typeofHeight === 'string') { shrinkWrap &= 1; // percentage, "30em" or whatever - not height shrinkWrap hasHeightStyle = true; } } if (shrinkWrap !== 3) { if (!ownerCtSizeModel) { ownerCtSizeModel = me.ownerCt && me.ownerCt.getSizeModel(); } if (ownerCtSizeModel) { shrinkWrap |= (ownerCtSizeModel.width.shrinkWrap ? 1 : 0) | (ownerCtSizeModel.height.shrinkWrap ? 2 : 0); } } if (!widthModel) { if (!policy.setsWidth && !(me.frame && hasWidthStyle)) { if (hasPixelWidth) { widthModel = models.configured; } else { widthModel = (shrinkWrap & 1) ? models.shrinkWrap : models.natural; } } else if (policy.readsWidth) { if (hasPixelWidth) { widthModel = models.calculatedFromConfigured; } else { widthModel = (shrinkWrap & 1) ? models.calculatedFromShrinkWrap : models.calculatedFromNatural; } } else { widthModel = models.calculated; } } if (!heightModel) { if (!policy.setsHeight && !(me.frame && hasHeightStyle)) { if (hasPixelHeight) { heightModel = models.configured; } else { heightModel = (shrinkWrap & 2) ? models.shrinkWrap : models.natural; } } else if (policy.readsHeight) { if (hasPixelHeight) { heightModel = models.calculatedFromConfigured; } else { heightModel = (shrinkWrap & 2) ? models.calculatedFromShrinkWrap : models.calculatedFromNatural; } } else { heightModel = models.calculated; } } } // We return one of the cached objects with the proper "width" and "height" as the // sizeModels we have determined. return widthModel.pairsByHeightOrdinal[heightModel.ordinal]; }, /** * The supplied default state gathering method for the Component class. * * This method returns dimension settings such as `flex`, `anchor`, `width` and `height` * along with `collapsed` state. * * Subclasses which implement more complex state should call the superclass's implementation, * and apply their state to the result if this basic state is to be saved. * * Note that Component state will only be saved if the Component has a {@link #stateId} * and there as a StateProvider configured for the document. * * @return {Object} */ getState: function() { var me = this, state = null, sizeModel = me.getSizeModel(); if (sizeModel.width.configured) { state = me.addPropertyToState(state, 'width'); } if (sizeModel.height.configured) { state = me.addPropertyToState(state, 'height'); } return state; }, getUserCls: function() { return this.userCls; }, setUserCls: function(cls) { var me = this, was = me.userCls; if (cls !== was) { me.userCls = cls; if (me.rendered) { me.el.replaceCls(was, cls); } } return was; }, /** * Gets the current width of the component's underlying element. * @return {Number} */ getWidth: function() { return this.el.getWidth(); }, /** * 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; }, /** * Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all * available xtypes, see the {@link Ext.Component} header. * * **If using your own subclasses, be aware that a Component must register its own xtype * to participate in determination of inherited xtypes.** * * Example usage: * * @example * var t = new Ext.form.field.Text(); * alert(t.getXTypes()); // alerts 'component/field/textfield' * * @return {String} The xtype hierarchy string * * @since 2.3.0 */ getXTypes: function() { var self = this.self, xtypes, parentPrototype, parentXtypes; if (!self.xtypes) { xtypes = []; parentPrototype = this; while (parentPrototype) { parentXtypes = parentPrototype.xtypes; if (parentXtypes !== undefined) { xtypes.unshift.apply(xtypes, parentXtypes); } parentPrototype = parentPrototype.superclass; } self.xtypeChain = xtypes; self.xtypes = xtypes.join('/'); } return self.xtypes; }, /** * Checks if the specified CSS class exists on this element's DOM node. * @param {String} className The CSS class to check for. * @return {Boolean} `true` if the class exists, else `false`. * @method */ hasCls: function(className) { var el = this.rendered ? this.el : this.protoEl; return el.hasCls.apply(el, arguments); }, /** * Checks if there is currently a specified `uiCls`. * @param {String} cls The `cls` to check. */ hasUICls: function(cls) { var me = this, uiCls = me.uiCls || []; return Ext.Array.contains(uiCls, cls); }, /** * Hides this Component, setting it to invisible using the configured {@link #cfg-hideMode}. * @param {String/Ext.dom.Element/Ext.Component} [animateTarget=null] **only valid for * {@link #cfg-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(animateTarget, callback, scope) { var me = this, container = me.ownerFocusableContainer; if (me.pendingShow) { // If this is a hierarchically hidden floating component with a pending show // hide() simply cancels the pending show. me.pendingShow = false; } if (!(me.rendered && !me.isVisible())) { if (!me.hasListeners.beforehide || me.fireEvent('beforehide', me) !== false || me.hierarchicallyHidden) { me.getInherited().hidden = me.hidden = true; if (container && !container.beforeFocusableChildHide.$nullFn) { container.beforeFocusableChildHide(me); } // Order of events is important here. Hierarchy event kicks off // ZIndexManager's collection sorting and floater activation; // The hidden flag must be set so that ZIndexManager takes it out of its stack. me.fireHierarchyEvent('beforehide'); if (me.rendered) { me.onHide.apply(me, arguments); } } } return 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#method!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 * @since 1.1.0 */ initComponent: function() { var me = this, width = me.width, height = me.height; // If plugins have been added by a subclass's initComponent before calling up to here // (or any components that don't have a table view), the processed flag will not have been // set, and we must process them again. // We could just call getPlugins here however most components don't have them so prevent // the extra function call. if (me.plugins && !me.plugins.processed) { me.constructPlugins(); } me.pluginsInitialized = true; // this will properly (ignore or) constrain the configured width/height to their // min/max values for consistency. if (width != null || height != null) { me.setSize(width, height); } if (me.listeners) { me.on(me.listeners); me.listeners = null; // change the value to remove any on prototype } if (me.focusable) { me.initFocusable(); } }, /** * Initialize any events on this component * @protected */ initEvents: function() { var me = this, afterRenderEvents = me.afterRenderEvents, afterRenderEvent, el, property, index, len; if (afterRenderEvents) { for (property in afterRenderEvents) { el = me[property]; if (el && el.on) { afterRenderEvent = afterRenderEvents[property]; for (index = 0, len = afterRenderEvent.length; index < len; ++index) { me.mon(el, afterRenderEvent[index]); } } } } if (me.focusable) { me.initFocusableEvents(); } // FocusableContainers are not themselves focusable, but they must process // their keyMap config me.initKeyMap(); }, /** * Tests whether this Component matches a {@link Ext.ComponentQuery ComponentQuery} * selector string. * @param {String} selector The selector string to test against. * @return {Boolean} `true` if this Component matches the selector. */ is: function(selector) { return Ext.ComponentQuery.is(this, selector); }, /** * Method to determine whether this Component is currently disabled. * @return {Boolean} the disabled state of this Component. */ isDisabled: function() { return this.disabled; }, /** * Method to determine whether this Component is draggable. * @return {Boolean} the draggable state of this component. */ isDraggable: function() { return !!this.draggable; }, /** * Method to determine whether this Component is droppable. * @return {Boolean} the droppable state of this component. */ isDroppable: function() { return !!this.droppable; }, /** * Method to determine whether this Component is floating. * @return {Boolean} the floating state of this component. */ isFloating: function() { return this.floating; }, /** * Method to determine whether this Component is currently set to hidden. * @return {Boolean} the hidden state of this Component. */ isHidden: function() { return this.hidden; }, getHidden: function() { return this.hidden; }, isHierarchicallyHidden: function() { var child = this, hidden = false, parent, parentInheritedState; // It is possible for some components to be immune to collapse meaning the immune // component remains visible when its direct parent is collapsed, e.g. panel header. // Because of this, we must walk up the component hierarchy to determine the true // visible state of the component. for (; (parent = child.ownerCt || child.floatParent); child = parent) { parentInheritedState = parent.getInherited(); if (parentInheritedState.hidden) { hidden = true; break; } if (child.getInherited().collapseImmune) { // The child or one of its ancestors is immune to collapse. if (parent.collapsed && !child.collapseImmune) { // If the child's direct parent is collapsed, and the child // itself does not have collapse immunity we know that // the child is not visible. hidden = true; break; } } else { // We have ascended the tree to a point where collapse immunity // is not in play. This means if any ancestor above this point // is collapsed, then the component is not visible. hidden = !!parentInheritedState.collapsed; break; } } return hidden; }, /** * Checks if this component will be contained by the passed component as part of its * layout run. If `true`, then the layout on `this` can be skipped because it will be * encompassed when the layout for `comp` runs. Typical cases where this may be be `false` * is when asking about floaters nested in containers. * @param {Ext.Component} ownerCandidate The potential owner. * @return {Boolean} `true` if this component is a layout child of `comp`. * * @private */ isLayoutChild: function(ownerCandidate) { return !this.floating && !!this.up(ownerCandidate); }, /** * Determines whether this Component is the root of a layout. This returns `true` if * this component can run its layout without assistance from or impact on its owner. * If this component cannot run its layout given these restrictions, `false` is returned * and its owner will be considered as the next candidate for the layout root. * * Setting the {@link #_isLayoutRoot} property to `true` causes this method to always * return `true`. This may be useful when updating a layout of a Container which shrink * wraps content, and you know that it will not change size, and so can safely be the * topmost participant in the layout run. * @protected */ isLayoutRoot: function() { var me = this, ownerLayout = me.ownerLayout; // Return true if we have been explicitly flagged as the layout root, or if we are floating. // Sometimes floating Components get an ownerCt ref injected into them which is *not* // a true ownerCt, merely an upward link for reference purposes. For example a grid column // menu is linked to the owning header via an ownerCt reference. if (!ownerLayout || me._isLayoutRoot || me.floating) { return true; } return ownerLayout.isItemLayoutRoot(me); }, /** * Returns `true` if layout is suspended for this component. This can come from direct * suspension of this component's layout activity ({@link Ext.Container#suspendLayout}) * or if one of this component's containers is suspended. * * @return {Boolean} `true` layout of this component is suspended. */ isLayoutSuspended: function() { var comp = this, ownerLayout; while (comp) { if (comp.layoutSuspendCount || comp.suspendLayout) { return true; } ownerLayout = comp.ownerLayout; if (!ownerLayout) { break; } // TODO - what about suspending a Layout instance? // this works better than ownerCt since ownerLayout means "is managed by" in // the proper sense... some floating components have ownerCt but won't have an // ownerLayout comp = ownerLayout.owner; } return false; }, /** * Returns `true` if this component is visible. * * @param {Boolean} [deep=false] Pass `true` to interrogate the visibility status of all * parent Containers to determine whether this Component is truly visible to the user. * * Generally, to determine whether a Component is hidden, the no argument form is needed. * For example when creating dynamically laid out UIs in a hidden Container before showing them. * * @return {Boolean} `true` if this component is visible, `false` otherwise. * * @since 1.1.0 */ isVisible: function(deep) { var me = this, hidden; if (me.hidden || !me.rendered || me.destroyed) { hidden = true; } else if (deep) { hidden = me.isHierarchicallyHidden(); } return !hidden; }, /** * Tests whether or not this Component is of a specific xtype. This can test whether this * Component is descended from the xtype (default) or whether it is directly of the xtype * specified (`shallow = true`). * * **If using your own subclasses, be aware that a Component must register its own xtype * to participate in determination of inherited xtypes.** * * For a list of all available xtypes, see the {@link Ext.Component} header. * * Example usage: * * @example * var t = new Ext.form.field.Text(); * var isText = t.isXType('textfield'); // true * var isBoxSubclass = t.isXType('field'); // true, descended from Ext.form.field.Base * var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.form.field.Base * // instance * * @param {String} xtype The xtype to check for this Component * @param {Boolean} [shallow=false] `true` to check whether this Component is directly * of the specified xtype, `false` to check whether this Component is descended from the xtype. * @return {Boolean} `true` if this component descends from the specified xtype, * `false` otherwise. * * @since 2.3.0 */ isXType: function(xtype, shallow) { return shallow ? (Ext.Array.indexOf(this.xtypes, xtype) !== -1) : !!this.xtypesMap[xtype]; }, /** * Returns masked state for this Component. * * @param {Boolean} [deep=false] True to look up this Component's parent masked state. * * @return {Boolean} True if masked, false otherwise. */ isMasked: function(deep) { var me = this; return !!(me.masked || (me.loadMask && me.loadMask.isVisible()) || (deep && me.getInherited().masked)); }, /** * Gets a named template instance for this class. See {@link Ext.XTemplate#getTpl}. * @param {String} name The name of the property that holds the template. * @return {Ext.XTemplate} The template, `null` if not found. * * @since 6.2.0 */ lookupTpl: function(name) { return Ext.XTemplate.getTpl(this, name); }, /** * Set masked state for this Component. * * @param {Boolean} isMasked True if masked, false otherwise. * @private */ setMasked: function(isMasked) { var me = this, container = me.ownerFocusableContainer; if (isMasked) { me.masked = true; me.getInherited().masked = isMasked; } else { me.masked = false; delete me.getInherited().masked; } if (container && !container.onFocusableChildMasked.$nullFn) { container.onFocusableChildMasked(me, isMasked); } return me; }, /** * Masks this component with a semi-opaque layer and makes the contents unavailable to clicks. * * See {@link #unmask}. * * @param {String} [msg] A message to show in the center of the mask layer. * @param {String} [msgCls] A CSS class name to use on the message element in the center * of the layer. * @param {Number} elHeight (private) The height of element to be masked */ mask: function(msg, msgCls, elHeight) { var box = this.lastBox, // getMaskTarget may be overridden in subclasses/ // null means that a LoadMask has to be rendered to document.body // Element masking falls back to masking the local el target = this.getMaskTarget() || this.el; // Pass it the height of our element if we know it. if (box) { elHeight = box.height; } target.mask(msg, msgCls, elHeight); this.setMasked(true); }, /** * Returns the next node in the Component tree in tree traversal order. * * Note that this is not limited to siblings, and if invoked upon a node with no matching * siblings, will walk the tree to attempt to find a match. Contrast with {@link #nextSibling}. * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter * the following nodes. * @param includeSelf (private) * @return {Ext.Component} The next node (or the next node which matches the selector). * Returns `null` if there is no matching node. */ nextNode: function(selector, includeSelf) { var node = this, ownerCt = node.ownerCt, result, it, len, i, sib; // If asked to include self, test me if (includeSelf && node.is(selector)) { return node; } if (ownerCt) { // eslint-disable-next-line max-len for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) + 1, len = it.length; i < len; i++) { sib = it[i]; if (sib.is(selector)) { return sib; } if (sib.down) { result = sib.down(selector); if (result) { return result; } } } return ownerCt.nextNode(selector); } return null; }, /** * Returns the next sibling of this Component. * * Optionally selects the next sibling which matches the passed * {@link Ext.ComponentQuery ComponentQuery} selector. * * May also be referred to as **`next()`** * * Note that this is limited to siblings, and if no siblings of the item match, * `null` is returned. Contrast with {@link #nextNode} * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter * the following items. * @return {Ext.Component} The next sibling (or the next sibling which matches the selector). * Returns `null` if there is no matching sibling. */ nextSibling: function(selector) { var o = this.ownerCt, it, last, idx, c; if (o) { it = o.items; idx = it.indexOf(this) + 1; if (idx) { if (selector) { for (last = it.getCount(); idx < last; idx++) { if ((c = it.getAt(idx)).is(selector)) { return c; } } } else { if (idx < it.getCount()) { return it.getAt(idx); } } } } return null; }, /** * Method to manage awareness of when components are added to their * respective Container, firing an #added event. References are * established at add time rather than at render time. * * Allows addition of behavior when a Component is added to a * Container. At this stage, the Component is in the parent * Container's collection of child items. After calling the * superclass's `onAdded`, the `ownerCt` reference will be present, * and if configured with a ref, the `refOwner` will be set. * * @param {Ext.container.Container} container Container which holds the component. * @param {Number} pos Position at which the component was added. * @param {Boolean} instanced `false` if this component was instanced by the parent * container. `true` if the instance already existed when it was passed to the container. * * @template * @protected * @since 3.4.0 */ onAdded: function(container, pos, instanced) { var me = this; me.ownerCt = container; me.onInheritedAdd(me, instanced); if (me.hasListeners && me.hasListeners.added) { me.fireEvent('added', me, container, pos); } if (Ext.GlobalEvents.hasListeners.added) { me.fireHierarchyEvent('added'); } }, /** * Method to manage awareness of when components are removed from their * respective Container, firing a #removed event. References are properly * cleaned up after removing a component from its owning container. * * Allows addition of behavior when a Component is removed from * its parent Container. At this stage, the Component has been * removed from its parent Container's collection of child items, * but has not been destroyed (It will be destroyed if the parent * Container's `autoDestroy` is `true`, or if the remove call was * passed a truthy second parameter). After calling the * superclass's `onRemoved`, the `ownerCt` and the `refOwner` will not * be present. * @param {Boolean} destroying Will be passed as `true` if the Container performing * the remove operation will delete this Component upon remove. * * @template * @protected * @since 3.4.0 */ onRemoved: function(destroying) { var me = this, focusTarget, floatParent = me.up('[floating]'); // Revert focus to closest sibling or ancestor unless we are being moved // In which case Ext.container.Container's move methods will handle // focus restoration. if (!me.isLayoutMoving && me.el && me.el.contains(Ext.Element.getActiveElement())) { focusTarget = me.findFocusTarget(); if (focusTarget) { focusTarget.focus(); } } if (Ext.GlobalEvents.hasListeners.removed) { me.fireHierarchyEvent('removed'); } if (me.hasListeners.removed) { me.fireEvent('removed', me, me.ownerCt); } me.onInheritedRemove(destroying); me.ownerCt = me.ownerLayout = null; // If the component being removed is in child heirarchy // of a floating component and it is not being destroyed, // we need to re-register its floating descendants with // the correct ZIndexManager. if (floatParent && !destroying) { floatParent.moveFloatingDescendants(me); } }, /** * Invoked when this component has first achieved size. This occurs after the * {@link #componentLayout} has completed its initial run. * * This method is not called on components that use {@link #cfg-liquidLayout}, such as * {@link Ext.button.Button Buttons} and {@link Ext.form.field.Base Form Fields}. * * **Note:** If the Component has a {@link Ext.Component#controller ViewController} * and the controller has a {@link Ext.app.ViewController#boxReady boxReady} method * it will be called passing the Component and its width and height. * * boxReady: function(view, width, height) { * // ... * } * * @param {Number} width The width of this component * @param {Number} height The height of this component * * @template * @protected */ onBoxReady: function(width, height) { var me = this, label; // We have to do this lookup onBoxReady instead of afterRender // to ensure that the components that could be referenced in // me.ariaLabelledBy or me.ariaDescribedBy are already rendered if (me.ariaLabelledBy || me.ariaDescribedBy) { if (me.ariaLabelledBy) { label = me.getAriaLabelEl(me.ariaLabelledBy); if (label) { me.ariaEl.dom.setAttribute('aria-labelledby', label); } } if (me.ariaDescribedBy) { label = me.getAriaLabelEl(me.ariaDescribedBy); if (label) { me.ariaEl.dom.setAttribute('aria-describedby', label); } } } if (me.resizable) { me.initResizable(me.resizable); } // Draggability must be initialized after resizability // Because if we have to be wrapped, the resizer wrapper must be dragged // as a pseudo-Component if (me.draggable) { me.initDraggable(); } if (me.hasListeners.boxready) { me.fireEvent('boxready', me, width, height); } }, /** * Allows addition of behavior to the destroy operation. * * @template * @method * @protected */ onDestroy: Ext.emptyFn, /** * Allows addition of behavior to the disable operation. * After calling the superclass's `onDisable`, the Component will be disabled. * * @template * @protected */ onDisable: function() { var me = this, dom, nodeName; if (me.focusable) { me.disableFocusable(); } if (!me.ariaStaticRoles[me.ariaRole]) { me.ariaEl.dom.setAttribute('aria-disabled', true); } // Only mask if we're set to & nobody above us will do so if (me.maskOnDisable && !me.getInheritedConfig('disableMask', true)) { dom = me.el.dom; nodeName = dom.nodeName; if (me.disabledRe.test(nodeName)) { dom.disabled = true; } if (!me.nonMaskableRe.test(nodeName)) { me.mask(); } } }, /** * Allows addition of behavior to the enable operation. * After calling the superclass's `onEnable`, the Component will be enabled. * * @template * @protected */ onEnable: function() { var me = this, dom, nodeName; if (me.focusable) { me.enableFocusable(); } if (!me.ariaStaticRoles[me.ariaRole]) { me.ariaEl.dom.setAttribute('aria-disabled', false); } if (me.maskOnDisable && me.getInherited().hasOwnProperty('masked')) { dom = me.el.dom; nodeName = dom.nodeName; if (me.disabledRe.test(nodeName)) { dom.disabled = false; } if (!me.nonMaskableRe.test(nodeName)) { me.unmask(); } } }, onGlobalShow: function(c) { if (this.up(c)) { this.getScrollable().restoreState(); } }, /** * 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.dom.Element/Ext.Component} [animateTarget] * @param {Function} [callback] Callback function to be called when finished * @param {Object} [scope] * * @template * @protected */ onHide: function(animateTarget, callback, scope) { var me = this, ghostPanel, fromSize, toBox; if (!me.ariaStaticRoles[me.ariaRole] && !me.destroying && !me.destroyed) { me.ariaEl.dom.setAttribute('aria-hidden', true); } // Part of the Focusable mixin API. // If we have focus now, move focus back to whatever had it before. me.revertFocus(); // Default to configured animate target if none passed animateTarget = me.getAnimateTarget(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) { toBox = { x: animateTarget.getX(), y: animateTarget.getY(), width: animateTarget.dom.offsetWidth, height: animateTarget.dom.offsetHeight }; ghostPanel = me.ghost(); ghostPanel.el.stopAnimation(); fromSize = me.getSize(); ghostPanel.el.animate({ to: toBox, listeners: { afteranimate: function() { if (!me.destroying) { ghostPanel.componentLayout.lastComponentSize = null; me.unghost(false); ghostPanel.el.setSize(fromSize); me.afterHide(callback, scope); } } } }); } else { me.el.hide(); } if (!animateTarget) { me.afterHide(callback, scope); } }, /** * @method * Called after the component is moved, this method is empty by default but can be implemented * by any subclass that needs to perform custom logic after a move occurs. * * @param {Number} x The new x position. * @param {Number} y The new y position. * * @template * @protected */ onPosition: Ext.emptyFn, /** * Called when the component is resized. * * This method is not called on components that use {@link #cfg-liquidLayout}, such as * {@link Ext.button.Button Buttons} and {@link Ext.form.field.Base Form Fields}. * * @param {Number} width The new width that was set * @param {Number} height The new height that was set * @param {Number} oldWidth The previous width * @param {Number} oldHeight The previous height * * @method * @template * @protected */ onResize: function(width, height, oldWidth, oldHeight) { var me = this; // constrain is a config on Floating if (me.floating && me.constrain) { me.doConstrain(); } if (me.hasListeners.resize) { me.fireEvent('resize', me, width, height, oldWidth, oldHeight); } }, /** * Invoked when a scroll is initiated on this component via its {@link #scrollable scroller}. * @method onScrollStart * @param {Number} x The current x position * @param {Number} y The current y position * @template * @protected */ /** * Invoked when this component is scrolled via its {@link #scrollable scroller}. * @method onScrollMove * @param {Number} x The current x position * @param {Number} y The current y position * @template * @protected */ /** * Invoked when a scroll operation is completed via this component's * {@link #scrollable scroller}. * @method onScrollEnd * @param {Number} x The current x position * @param {Number} y The current y position * @template * @protected */ /** * 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.dom.Element} [animateTarget] * @param {Function} [callback] * @param {Object} [scope] * * @template * @protected */ onShow: function() { var me = this; if (!me.ariaStaticRoles[me.ariaRole]) { me.ariaEl.dom.setAttribute('aria-hidden', false); } me.el.show(); me.updateLayout({ isRoot: false, context: me._showContext }); // 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(); } } }, _showContext: { show: true }, /** * 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(callback, scope) { var me = this, container = me.ownerFocusableContainer; if (me.floating) { me.onFloatShow(); } Ext.callback(callback, scope || me); me.fireEvent('show', me); if (container && !container.onFocusableChildShow.$nullFn) { container.onFocusableChildShow(me); } delete me.hiddenByLayout; }, onShowVeto: Ext.emptyFn, /** * Returns the previous node in the Component tree in tree traversal order. * * Note that this is not limited to siblings, and if invoked upon a node with no matching * siblings, will walk the tree in reverse order to attempt to find a match. Contrast with * {@link #previousSibling}. * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter * the preceding nodes. * @param includeSelf (private) * @return {Ext.Component} The previous node (or the previous node which matches the selector). * Returns `null` if there is no matching node. */ previousNode: function(selector, includeSelf) { var node = this, ownerCt = node.ownerCt, result, it, i, sib; // If asked to include self, test me if (includeSelf && node.is(selector)) { return node; } if (ownerCt) { for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) { sib = it[i]; if (sib.query) { result = sib.query(selector); result = result[result.length - 1]; if (result) { return result; } } if (sib.is(selector)) { return sib; } } return ownerCt.previousNode(selector, true); } return null; }, /** * Returns the previous sibling of this Component. * * Optionally selects the previous sibling which matches the passed * {@link Ext.ComponentQuery ComponentQuery} selector. * * May also be referred to as **`prev()`** * * Note that this is limited to siblings, and if no siblings of the item match, * `null` is returned. Contrast with {@link #previousNode} * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter * the preceding items. * @return {Ext.Component} The previous sibling (or the previous sibling which matches * the selector). * Returns `null` if there is no matching sibling. */ previousSibling: function(selector) { var o = this.ownerCt, it, idx, c; if (o) { it = o.items; idx = it.indexOf(this); if (idx !== -1) { if (selector) { for (--idx; idx >= 0; idx--) { if ((c = it.getAt(idx)).is(selector)) { return c; } } } else { if (idx) { return it.getAt(--idx); } } } } return null; }, /** * Called by Component#doAutoRender * * Register a Container configured `floating: true` with this Component's * {@link Ext.ZIndexManager ZIndexManager}. * * Components added in this way will not participate in any layout, but will be rendered * upon first show in the way that {@link Ext.window.Window Window}s are. */ registerFloatingItem: function(cmp) { var me = this; if (!me.floatingDescendants) { me.floatingDescendants = new Ext.ZIndexManager(me); } me.floatingDescendants.register(cmp); }, /** * Removes a CSS class from the top level element representing this component. * @param {String/String[]} cls The CSS class name to remove. * @return {Ext.Component} Returns the Component to allow method chaining. */ removeCls: function(cls) { var me = this, el = me.rendered ? me.el : me.protoEl; el.removeCls.apply(el, arguments); return me; }, /** * Removes a `cls` to the `uiCls` array, which will also call {@link #removeUIClsFromElement} * and removes it from all elements of this component. * @param {String/String[]} classes A string or an array of strings to remove to the `uiCls`. * @param {Boolean} skip True to remove the class */ removeClsWithUI: function(classes, skip) { var me = this, clsArray = [], i = 0, extArray = Ext.Array, remove = extArray.remove, uiCls = me.uiCls = extArray.clone(me.uiCls), activeUI = me.activeUI, length, cls; if (typeof classes === "string") { classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes); } length = classes.length; for (i = 0; i < length; i++) { cls = classes[i]; if (cls && me.hasUICls(cls)) { remove(uiCls, cls); // If there's no activeUI then there's nothing to remove if (activeUI) { clsArray = clsArray.concat(me.removeUIClsFromElement(cls)); } } } if (skip !== true && activeUI) { me.removeCls(clsArray); } return clsArray; }, resumeLayouts: function(flushOptions) { var me = this; if (!me.rendered) { return; } //<debug> if (!me.layoutSuspendCount) { Ext.log.warn('Mismatched call to resumeLayouts - layouts are currently not suspended.'); } //</debug> if (me.layoutSuspendCount && !--me.layoutSuspendCount) { me.suspendLayout = false; if (flushOptions && !me.isLayoutSuspended()) { me.updateLayout(flushOptions); } } }, /** * Scrolls this Component 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 scroller = this.getScrollable(); if (scroller) { scroller.scrollBy(deltaX, deltaY, animate); } }, /** * Scrolls this component to the specified `x` and `y` coordinates. Only applicable * for {@link #scrollable} components. * @param {Number} x * @param {Number} y * @param {Boolean/Object} [animate] true for the default animation or a standard Element * animation config object */ scrollTo: function(x, y, animate) { var scroller = this.getScrollable(); if (scroller) { scroller.scrollTo(x, y, animate); } }, /** * 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 * @deprecated 5.0.0 Use {@link #setScrollable} instead */ setAutoScroll: function(scroll) { this.setScrollable(!!scroll); return this; }, /** * @param {String/Number} border The border, see {@link #border}. If a falsey value is passed * @param targetEl (private) * the border will be removed. */ setBorder: function(border, targetEl) { var me = this, initial = !!targetEl; if (me.rendered || initial) { if (!initial) { targetEl = me.el; } if (!border) { border = 0; } else if (border === true) { border = '1px'; } else { border = this.unitizeBox(border); } targetEl.setStyle('border-width', border); if (!initial) { me.updateLayout(); } } me.border = border; }, /** * Sets the dock position of this component in its parent panel. Note that this only has effect * if this item is part of the `dockedItems` collection of a parent that has a DockLayout * (note that any Panel has a DockLayout by default) * @param {Object} dock The dock position. * @return {Ext.Component} this */ setDock: function(dock) { var me = this, ownerCt = me.ownerCt; if (dock !== me.dock) { if (ownerCt && ownerCt.moveDocked) { ownerCt.moveDocked(me, dock); } else { me.dock = dock; } } return me; }, /** * Enable or disable the component. * @param {Boolean} disabled `true` to disable. */ setDisabled: function(disabled) { return this[disabled ? 'disable' : 'enable'](); }, /** * Sets the flex property of this component. Only applicable when this component is * an item of a box layout * @private * @param {Number} flex */ setFlex: function(flex) { this.flex = flex; }, /** * Sets the height of the component. This method fires the {@link #resize} event. * * @param {Number} height The new height to set. This may be one of: * * - A Number specifying the new height in pixels. * - A String used to set the CSS height style. * - `undefined` to leave the height unchanged. * - `null` to clear the height. * * @return {Ext.Component} this */ setHeight: function(height) { return this.setSize(undefined, height); }, /** * This method allows you to show or hide a LoadMask on top of this component. * * The mask will be rendered into the element returned by {@link #getMaskTarget} which for most * Components is the Component's element. See {@link #getMaskTarget} and {@link #maskElement}. * * Most Components will return `null` indicating that their LoadMask cannot reside inside * their element, but must be rendered into the document body. * * {@link Ext.view.Table Grid Views} however will direct a LoadMask to be rendered into * the owning {@link Ext.panel.Table GridPanel}. * * @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 targetEl (private) This param is deprecated * @return {Ext.LoadMask} The LoadMask instance that has just been shown. */ setLoading: function(load, targetEl) { var me = this, config = { target: me }; if (me.rendered) { // Shows mask for anything but false. if (load !== false) { if (Ext.isString(load)) { config.msg = load; } else { Ext.apply(config, load, me.maskDefaults); } // We do not already have a LoadMask: create one if (!me.loadMask || !me.loadMask.isLoadMask) { // Deprecated second parameter. // maskElement config replaces this if (targetEl && config.useTargetEl == null) { config.useTargetEl = true; } me.loadMask = new Ext.LoadMask(config); } // Change any settings according to load config else { Ext.apply(me.loadMask, config); } // If already visible, just update display with passed configs. if (me.loadMask.isVisible()) { me.loadMask.syncMaskState(); } // Otherwise show with new configs else { me.loadMask.show(); } } // load == falsy: Hide the mask if it exists else { if (me.loadMask && me.loadMask.isLoadMask) { me.loadMask.hide(); } } } return me.loadMask; }, /** * Sets the margin on the target element. * @param {Number/String} margin The margin to set. See the {@link #margin} config. * @param preventLayout (private) */ setMargin: function(margin, preventLayout) { var me = this; if (me.rendered) { if (!margin && margin !== 0) { margin = ''; } else { if (margin === true) { margin = 5; } margin = this.unitizeBox(margin); } me.margin = margin; // See: EXTJS-13359 me.margin$ = null; me.getEl().setStyle('margin', margin); if (!preventLayout) { // Changing the margins can impact the position of this (and possibly) // other subsequent components in the layout. me.updateLayout(me._notAsLayoutRoot); } } else { me.margin = margin; } }, /** * 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 `undefined` will preserve the current value. * * @param {String} overflowX The overflow-x value. * @param {String} overflowY The overflow-y value. * @return {Ext.Component} this * @deprecated 5.0.0 Use {@link #setScrollable} instead */ setOverflowXY: function(overflowX, overflowY) { this.setScrollable({ x: (overflowX && overflowX !== 'hidden') ? overflowX : false, y: (overflowY && overflowY !== 'hidden') ? overflowY : false }); return this; }, /** * 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/Number[]} x The new x position or an array of `[x,y]`. * @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.translateXY(x, y); x = p.x; y = p.y; } me.setPosition(x, y, animate); } else { p = me.el.translateXY(x, y); me.setPosition(p.x, p.y, animate); } return me; }, /** * @member Ext.Component * Sets the left and top of the component. To set the page XY position instead, * use {@link Ext.Component#setPagePosition setPagePosition}. This method fires the * {@link #event-move} event. * @param {Number/Number[]/Object} x The new left, an array of `[x,y]`, or animation config * object containing `x` and `y` properties. * @param {Number} [y] The new top. * @param {Boolean/Object} [animate] If `true`, the Component is _animated_ into * its new position. You may also pass an animation configuration. * @return {Ext.Component} this */ setPosition: function(x, y, animate) { var me = this, pos = me.beforeSetPosition.apply(me, arguments); if (pos && me.rendered) { x = pos.x; y = pos.y; if (animate) { // Proceed only if the new position is different from the current // one. We only do these DOM reads in the animate case as we don't // want to incur the penalty of read/write on every call to setPosition if (x !== me.getLocalX() || y !== me.getLocalY()) { me.stopAnimation(); me.animate(Ext.apply({ duration: 1000, listeners: { afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y]) }, to: { // Use local coordinates for a component // We don't need to normalize this for RTL, the anim.target.Component // calls setPosition, which will normalize the x value to right when // it's necessary left: x, top: y } }, animate)); } } else { me.setLocalXY(x, y); me.afterSetPosition(x, y); } } return me; }, /** * Sets the "x" scroll position for this component. Only applicable for * {@link #scrollable} components * @param {Number} x * @param {Boolean/Object} [animate] true for the default animation or a standard Element * animation config object */ setScrollX: function(x, animate) { var scroller = this.getScrollable(); if (scroller) { scroller.scrollTo(x, null, animate); } }, /** * Sets the "y" scroll position for this component. Only applicable for * {@link #scrollable} components * @param {Number} y * @param {Boolean/Object} [animate] true for the default animation or a standard Element * animation config object */ setScrollY: function(y, animate) { var scroller = this.getScrollable(); if (scroller) { scroller.scrollTo(null, y, animate); } }, /** * Sets the width and height of this Component. This method fires the {@link #resize} event. * This method can accept either width and height as separate arguments, or you can pass * a size object like `{ width:10, height:20 }`. * * @param {Number/String/Object} width The new width to set. This may be one of: * * - A Number specifying the new width in pixels. * - A String used to set the CSS width style. * - A size object in the format `{width: widthValue, height: heightValue}`. * - `undefined` to leave the width unchanged. * * @param {Number/String} height The new height to set (not required if a size object is passed * as the first arg). This may be one of: * * - A Number specifying the new height in pixels. * - A String used to set the CSS height style. Animation may **not** be used. * - `undefined` to leave the height unchanged. * * @return {Ext.Component} this */ setSize: function(width, height) { var me = this, oldWidth = me.width, oldHeight = me.height, widthIsString, heightIsString; // support for standard size objects if (width && typeof width === 'object') { height = width.height; width = width.width; } // Constrain within configured maximum if (typeof width === 'number') { me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth); } else if (width === null) { delete me.width; } else if (typeof width === 'string') { // handle width specified as a string css style widthIsString = true; me.width = width; } if (typeof height === 'number') { me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight); } else if (height === null) { delete me.height; } else if (typeof height === 'string') { // handle width specified as a string css style heightIsString = true; me.height = height; } // If not rendered, all we need to is set the properties. // The initial layout will set the size if (me.rendered && me.isVisible()) { if (oldWidth !== me.width || oldHeight !== me.height) { if (me.liquidLayout || widthIsString || heightIsString) { // if we have a liquid layout we must setSize now, since the following // updateLayout call will not set our size in the dom if we successfully // opt out of the layout run me.el.setSize(me.width, me.height); } // If we are changing size, then we are not the root. me.updateLayout(me._notAsLayoutRoot); } } return me; }, /** * Sets the style for this Component's primary element. * * Styles should be a valid DOM element style property. * [Valid style property names](http://www.w3schools.com/jsref/dom_obj_style.asp) * (_along with the supported CSS version for each_) * * var name = Ext.create({ * xtype: 'component', * renderTo: Ext.getBody(), * html: 'Phineas Flynn' * }); * * // two-param syntax * name.setStyle('color', 'white'); * * // single-param syntax * name.setStyle({ * fontWeight: 'bold', * backgroundColor: 'gray', * padding: '10px' * }); * * @param {String/Object} property The style property to be set, or an object of * multiple styles. * @param {String} [value] The value to apply to the given property, or null if an * object was passed. * @return {Ext.Component} this */ setStyle: function(property, value) { var el = this.el || this.protoEl; el.setStyle(property, value); return this; }, /** * Sets the UI for the component. This will remove any existing UIs on the component. * It will also loop through any `uiCls` set on the component and rename them so they include * the new UI. * @param {String} ui The new UI for the component. */ setUI: function(ui) { var me = this, uiCls = me.uiCls, activeUI = me.activeUI, classes; if (ui === activeUI) { // The ui hasn't changed return; } // activeUI will only be set if setUI has been called before. // If it hasn't there's no need to remove anything if (activeUI) { classes = me.removeClsWithUI(uiCls, true); if (classes.length) { me.removeCls(classes); } // Remove the UI from the element me.removeUIFromElement(); } else { // We need uiCls to be empty otherwise our call to addClsWithUI won't do anything me.uiCls = []; } // Set the UI me.ui = ui; // After the first call to setUI the values ui and activeUI should track each other // but initially we need some way to tell whether the ui has really been set. me.activeUI = ui; // Add the new UI to the element me.addUIToElement(); classes = me.addClsWithUI(uiCls, true); if (classes.length) { me.addCls(classes); } // Changing the ui can lead to significant changes to a component's appearance, // so the layout needs to be updated. Internally most calls to setUI are pre-render. // Buttons are a notable exception as setScale changes the ui and often requires // the layout to be updated. if (me.rendered) { me.updateLayout(); } }, /** * Convenience function to hide or show this component by Boolean. * @param {Boolean} visible `true` to show, `false` to hide. * @return {Ext.Component} this * @since 1.1.0 */ setVisible: function(visible) { return this[visible ? 'show' : 'hide'](); }, /** * Sets the hidden state of this component. This is basically the same as * `{@link #setVisible}` but the boolean parameter has the opposite meaning. * @param {Boolean} hidden * @return {Ext.Component} */ setHidden: function(hidden) { return this.setVisible(!hidden); }, /** * Sets the width of the component. This method fires the {@link #resize} event. * * @param {Number} width The new width to set. This may be one of: * * - A Number specifying the new width in pixels. * - A String used to set the CSS width style. * - `undefined` to leave the width unchanged. * - `null` to clear the width. * * @return {Ext.Component} this */ setWidth: function(width) { return this.setSize(width); }, /** * Shows this Component, rendering it first if {@link #autoRender} or {@link #cfg-floating} * are `true`. * * After being shown, a {@link #cfg-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.dom.Element} [animateTarget=null] **only valid for {@link #cfg-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, callback, scope) { var me = this, rendered = me.rendered, container = me.ownerFocusableContainer; if (me.hierarchicallyHidden || (me.floating && !rendered && me.isHierarchicallyHidden())) { // If this is a hierarchically hidden floating component, we need to stash // the arguments to this call so that the call can be deferred until the next // time syncHidden() is called. if (!rendered) { // If the component has not yet been rendered it requires special treatment. // Normally, for rendered components we can just set the pendingShow property // and syncHidden() listens to events in the hierarchyEventSource and calls // show() when this component becomes hierarchically visible. However, // if the component has not yet been rendered the hierarchy event listeners // have not yet been attached (since Floating is initialized during the // render phase. This means we have to initialize the hierarchy event // listeners right now to ensure that the component will show itself when // it becomes hierarchically visible. me.initHierarchyEvents(); } // defer the show call until next syncHidden(), but ignore animateTarget. if (arguments.length > 1) { arguments[0] = null; me.pendingShow = arguments; } else { me.pendingShow = true; } } else if (rendered && me.isVisible()) { if (me.floating) { me.onFloatShow(); // onFloatShow does not toFront this Component // because that is done by our ZIndexManager in response to the global // show event fired from afterShow. // Since we will not execute afterShow if already visible, // maintain behaviour by explicitly toFronting here. if (me.toFrontOnShow) { me.toFront(); } } } else { if (me.fireEvent('beforeshow', me) !== false) { me.hidden = false; delete this.getInherited().hidden; // Render on first show if there is an autoRender config, or if this // is a floater (Window, Menu, BoundList etc). if (container && !container.beforeFocusableChildShow.$nullFn) { container.beforeFocusableChildShow(me); } // We suspend layouts here because floaters/autoRenders // will layout when onShow is called. If the render succeeded, // the layout will be trigger inside onShow, so we don't flush // in the first block. If, for some reason we couldn't render, then // we resume layouts and force a flush because we don't know if something // will force it. Ext.suspendLayouts(); if (!rendered && (me.autoRender || me.floating)) { me.doAutoRender(); rendered = me.rendered; } if (rendered) { me.beforeShow(); Ext.resumeLayouts(); me.onShow.apply(me, arguments); me.afterShow.apply(me, arguments); } else { Ext.resumeLayouts(true); } } else { me.onShowVeto(); } } return me; }, /** * 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/Number[]} x The new x position or array of `[x,y]`. * @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 */ showAt: function(x, y, animate) { var me = this; // Not rendered, then animating to a position is meaningless, // just set the x,y position and allow show's processing to work. if (!me.rendered && (me.autoRender || me.floating)) { me.x = x; me.y = y; return me.show(); } if (me.floating) { me.setPosition(x, y, animate); } else { me.setPagePosition(x, y, animate); } return me.show(); }, /** * Shows this component by the specified {@link Ext.Component Component} or * {@link Ext.dom.Element Element}. Used when this component is {@link #cfg-floating}. * @param {Ext.Component/Ext.dom.Element} component The {@link Ext.Component} or * {@link Ext.dom.Element} to show the component by. * @param {String} [position] Alignment position as used by * {@link Ext.util.Positionable#getAlignToXY}. Defaults to `{@link #defaultAlign}`. * See {@link #alignTo} for possible values. * @param {Number[]} [offset] Alignment offsets as used by * {@link Ext.util.Positionable#getAlignToXY}. See {@link #alignTo} for possible values. * @return {Ext.Component} this */ showBy: function(component, position, offset) { var me = this; //<debug> if (!me.floating) { Ext.log.warn('Using showBy on a non-floating component'); } //</debug> if (me.floating && component) { me._lastAlignTarget = component; me._lastAlignToPos = position || me.defaultAlign; if (!me.alignOffset || me.alignOffset !== offset) { me.alignOffset = offset; } me._lastAlignToOffsets = offset || me.alignOffset; me.show(); } return me; }, suspendLayouts: function() { var me = this; if (!me.rendered) { return; } if (++me.layoutSuspendCount === 1) { me.suspendLayout = true; } }, /** * Toggles the specified CSS class on this component (removes it if it already exists, * otherwise adds it). * @param {String} className The CSS class to toggle. * @param {Boolean} [state] If specified as `true`, causes the class to be added. If * specified as `false`, causes the class to be removed. * @return {Ext.Component} Returns the Component to allow method chaining. * @chainable */ toggleCls: function(className, state) { if (state === undefined) { state = !this.hasCls(className); } return this[state ? 'addCls' : 'removeCls'](className); }, unitizeBox: function(box) { return Ext.Element.unitizeBox(box); }, /** * Removes the mask applied by {@link #mask} */ unmask: function() { (this.getMaskTarget() || this.el).unmask(); this.setMasked(false); }, unregisterFloatingItem: function(cmp) { var me = this; if (me.floatingDescendants) { me.floatingDescendants.unregister(cmp); } }, /** * Navigates up the ownership hierarchy searching for an ancestor Container which matches * any passed selector or component. * * *Important.* There is not a universal upwards navigation pointer. There are several upwards * relationships such as the {@link Ext.button.Button button} which activates * a {@link Ext.button.Button#cfg-menu menu}, or the {@link Ext.menu.Item menu item} * which activated a {@link Ext.menu.Item#cfg-menu submenu}, or the * {@link Ext.grid.column.Column column header} which activated the column menu. * * These differences are abstracted away by this method. * * Example: * * var owningTabPanel = grid.up('tabpanel'); * * @param {String/Ext.Component} [selector] The selector component or actual component to test. * If not passed the immediate owner/activator is returned. * @param {String/Number/Ext.Component} [limit] This may be a selector upon which to stop * the upward scan, or a limit of the number of steps, or Component reference to stop on. * @return {Ext.container.Container} The matching ancestor Container (or `undefined` if no match * was found). */ up: function(selector, limit) { var result = this.getRefOwner(), limitSelector = typeof limit === 'string', limitCount = typeof limit === 'number', limitComponent = limit && limit.isComponent, steps = 0; if (selector) { for (; result; result = result.getRefOwner()) { if (result.destroyed) { return null; } steps++; if (selector.isComponent) { if (result === selector) { return result; } } else { if (Ext.ComponentQuery.is(result, selector)) { return result; } } // Stop when we hit the limit selector if (limitSelector && result.is(limit)) { return; } if (limitCount && steps === limit) { return; } if (limitComponent && result === limit) { return; } } } return result; }, /** * Update the content area of a component. * @param {String/Object} htmlOrData If this component has been configured with a * template via the tpl config then it will use this argument as data to populate the * template. If this component was not configured with a template, the components * content area will be updated via Ext.Element update. * @param {Boolean} [loadScripts=false] Only legitimate when using the `html` * configuration. Causes embedded script tags to be executed. Inline source will be executed * with this Component as the scope (`this` reference). * @param {Function} [callback] Only legitimate when using the `html` configuration. * Callback to execute when scripts have finished loading. * @param {Object} [scriptScope=`this`] The scope (`this` reference) in which to * execute *inline* script elements content. Scripts with a `src` attribute cannot * be executed with this scope. * * @since 3.4.0 */ update: function(htmlOrData, loadScripts, callback, scriptScope) { var me = this, isData = (me.tpl && !Ext.isString(htmlOrData)), container = me.ownerFocusableContainer, sizeModel, doLayout, el; if (isData) { me.data = (htmlOrData && htmlOrData.isEntity) ? htmlOrData.getData(true) : htmlOrData; } else { me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData; } if (me.rendered) { sizeModel = me.getSizeModel(); doLayout = sizeModel.width.shrinkWrap || sizeModel.height.shrinkWrap; if (me.isContainer) { el = me.layout.getRenderTarget(); // If we are a non-empty container being updated with raw content we have to lay out doLayout = doLayout || me.items.items.length > 0; } else { el = me.getTargetEl(); } if (isData) { me.tpl[me.tplWriteMode](el, me.data || {}); } else { el.setHtml(me.html, loadScripts, callback, scriptScope || me); } if (doLayout) { me.updateLayout(); } if (container && !container.onFocusableChildUpdate.$nullFn) { container.onFocusableChildUpdate(me); } } }, /** * Update the content area of a component. * @param {String/Object} html If this component has been configured with a * template via the tpl config then it will use this argument as data to populate the * template. If this component was not configured with a template, the components * content area will be updated via Ext.Element update. * @param {Boolean} [loadScripts=false] Only legitimate when using the `html` * configuration. Causes embedded script tags to be executed. Inline source will be executed * with this Component as the scope (`this` reference). * @param {Function} [callback] Only legitimate when using the `html` configuration. * Callback to execute when scripts have finished loading. * @param {Object} [scriptScope='this'] The scope (`this` reference) in which to * execute *inline* script elements content. Scripts with a `src` attribute cannot * be executed with this scope. * * @since 3.4.0 */ setHtml: function(html, loadScripts, callback, scriptScope) { this.update(html, loadScripts, null, scriptScope); }, applyData: function(data) { // Don't return data here, update will set this.data this.update(data); }, /** * 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; }, _asLayoutRoot: { isRoot: true }, _notAsLayoutRoot: { isRoot: false }, /** * Updates this component's layout. If this update affects this components {@link #ownerCt}, * that component's `updateLayout` method will be called to perform the layout instead. * Otherwise, just this component (and its child items) will layout. * * @param {Object} [options] An object with layout options. * @param {Boolean} options.defer `true` if this layout should be deferred. * @param {Boolean} options.isRoot `true` if this layout should be the root of the layout. */ updateLayout: function(options) { var me = this, defer, lastBox = me.lastBox, isRoot = options && options.isRoot, context = options && options.context; if (lastBox) { // remember that this component's last layout result is invalid and must be // recalculated lastBox.invalid = true; } if (!me.rendered || me.isDetached || me.layoutSuspendCount || me.suspendLayout) { return; } if (me.hidden) { Ext.Component.cancelLayout(me); } else if (typeof isRoot !== 'boolean') { isRoot = me.isLayoutRoot(); } // if we aren't the root, see if our ownerLayout will handle it... if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me, context)) { // either we are the root or our ownerLayout doesn't care if (!me.isLayoutSuspended()) { // we aren't suspended (knew that), but neither is any of our ownerCt's... defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts; Ext.Component.updateLayout(me, defer); } } }, updateMaxHeight: function(maxHeight, oldMaxHeight) { this.changeConstraint(maxHeight, oldMaxHeight, 'min', 'max-height', 'height'); }, updateMaxWidth: function(maxWidth, oldMaxWidth) { this.changeConstraint(maxWidth, oldMaxWidth, 'min', 'max-width', 'width'); }, updateMinHeight: function(minHeight, oldMinHeight) { this.changeConstraint(minHeight, oldMinHeight, 'max', 'min-height', 'height'); }, updateMinWidth: function(minWidth, oldMinWidth) { this.changeConstraint(minWidth, oldMinWidth, 'max', 'min-width', 'width'); }, updateTouchAction: function(touchAction) { var name, childEl, value, hasRootActions; for (name in touchAction) { childEl = this[name]; value = touchAction[name]; if (childEl && childEl.isElement) { childEl.setTouchAction(value); } else { hasRootActions = true; } } if (hasRootActions) { this.el.setTouchAction(touchAction); } }, // *********************************************************************************** // End Component methods // *********************************************************************************** // </editor-fold> // <editor-fold desc="Positionable Methods"> // *********************************************************************************** // Begin Positionable methods // *********************************************************************************** getAnchorToXY: function(el, anchor, local, mySize) { return el.getAnchorXY(anchor, local, mySize); }, getBorderPadding: function() { return this.el.getBorderPadding(); }, getLocalX: function() { return this.el.getLocalX(); }, getLocalXY: function() { return this.el.getLocalXY(); }, getLocalY: function() { return this.el.getLocalY(); }, getX: function() { return this.el.getX(); }, getXY: function() { return this.el.getXY(); }, getY: function() { return this.el.getY(); }, setLocalX: function(x) { this.el.setLocalX(x); }, setLocalXY: function(x, y) { this.el.setLocalXY(x, y); }, setLocalY: function(y) { this.el.setLocalY(y); }, setX: function(x, animate) { this.el.setX(x, animate); }, setXY: function(xy, animate) { this.el.setXY(xy, animate); }, setY: function(y, animate) { this.el.setY(y, animate); }, // *********************************************************************************** // End Positionable methods // *********************************************************************************** // </editor-fold> privates: { addOverCls: function() { var me = this; if (!me.disabled) { me.el.addCls(me.overCls); } }, /** * Method which adds a specified UI to the components element. * @private */ addUIToElement: function() { var me = this, baseClsUI = me.baseCls + '-' + me.ui, childEls, childElName, el, suffix; me.addCls(baseClsUI); if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { // Loop through each frame element, and if they are defined add the ui: baseClsUI += '-'; childEls = me.getChildEls(); for (childElName in childEls) { suffix = childEls[childElName].frame; if (suffix && suffix !== true) { el = me[childElName]; if (el) { el.addCls(baseClsUI + suffix); } } } } }, /** * @private */ changeConstraint: function(newValue, oldValue, constrainMethod, styleName, sizeName) { var me = this, size = me[sizeName]; if (newValue != null && typeof size === 'number') { me[sizeName] = Math[constrainMethod](size, newValue); } if (me.liquidLayout) { // components that use liquidLayout need their size constraints set in the dom if (newValue != null) { me.setStyle(styleName, newValue + 'px'); } else if (oldValue) { me.setStyle(styleName, ''); } } if (me.rendered) { me.updateLayout(); } }, /** * @param {String/Object} plugin string or config object containing a ptype property. * * Constructs a plugin according to the passed config object/ptype string. * * Ensures that the constructed plugin always has a `cmp` reference back to this component. * The setting up of this is done in PluginManager. The PluginManager ensures that * a reference to this component is passed to the constructor. It also ensures that * the plugin's `setCmp` method (if any) is called. * @private */ constructPlugin: function(plugin) { var me = this, PM = Ext.plugin.Manager; // ptype only, pass as the defultType if (typeof plugin === 'string') { plugin = PM.create({}, plugin, me); } // Object (either config with ptype or an instantiated plugin) else { plugin = PM.create(plugin, null, me); } return plugin; }, /** * Returns an array of fully constructed plugin instances. This converts any configs * into their appropriate instances. * * It does not mutate the plugins array. It creates a new array. * @private */ constructPlugins: function() { var me = this, plugins = me.plugins, result, i, len; if (plugins) { me.plugins = result = Ext.plugin.Abstract.decode(plugins, 'ptype'); // The processed flag indicates that the plugins have been constructed. // This is usually done at construction time, so if at initComponent time, // there is a non-zero array of plugins which does NOT have the processed flag, // it needs to be processed again. result.processed = true; for (i = 0, len = result.length; i < len; i++) { // this just returns already-constructed plugin instances... result[i] = me.constructPlugin(result[i]); } } me.pluginsInitialized = true; return result; }, detachFromBody: function() { // Also see reattachToBody var me = this; if (!me.isDetached) { Ext.getDetachedBody().appendChild(me.el, true); Ext.Component.cancelLayout(me); me.isDetached = true; } }, doAddListener: function(ename, fn, scope, options, order, caller, manager) { var me = this, listeners, option, eventOptions, elementName, element, delegate; if (Ext.isObject(fn) || (options && options.element)) { if (options.element) { elementName = options.element; listeners = {}; listeners[ename] = fn; if (scope) { listeners.scope = scope; } eventOptions = me.$elementEventOptions; for (option in options) { if (eventOptions[option]) { listeners[option] = options[option]; } } } else { listeners = fn; elementName = ename; } element = me[elementName]; // can be any kind of observable, not just element if (element && element.isObservable) { me.mon(element, listeners); } else { me.afterRenderEvents = me.afterRenderEvents || {}; if (!me.afterRenderEvents[elementName]) { me.afterRenderEvents[elementName] = []; } me.afterRenderEvents[elementName].push(listeners); } return; } if (options) { delegate = options.delegate; if (delegate) { me.mixins.componentDelegation.addDelegatedListener.call( me, ename, fn, scope, options, order, caller, manager ); return; } } me.mixins.observable.doAddListener.call( me, ename, fn, scope, options, order, caller, manager ); }, doRemoveListener: function(eventName, fn, scope) { var me = this; me.mixins.observable.doRemoveListener.call(me, eventName, fn, scope); me.mixins.componentDelegation.removeDelegatedListener.call(me, eventName, fn, scope); }, /** * This method fires an event on `Ext.GlobalEvents` allowing interested parties to know * of certain critical events for this component. This is done globally because the * (few) listeners can immediately receive the event rather than bubbling the event * only to reach the top and have no listeners. * * The main usage for these events is to do with floating components. For example, the * load mask is a floating component. The component it is masking may be inside several * containers. As such, they need to know when component is hidden, either directly, or * via a parent container being hidden. To do this they subscribe to these events and * filter out the appropriate container. * * This functionality is contained in Component (as opposed to Container) because a * Component can be the ownerCt for a floating component (loadmask), and the loadmask * needs to know when its owner is shown/hidden so that its hidden state can be * synchronized. * * @param {String} eventName The event name. * @since 4.2.0 * @private */ fireHierarchyEvent: function(eventName) { var globalEvents = Ext.GlobalEvents; if (globalEvents.hasListeners[eventName]) { globalEvents.fireEvent(eventName, this); } }, getActionEl: function() { return this.el; }, /** * @private */ getAutoId: function() { this.autoGenId = true; return ++Ext.Component.AUTO_ID; }, /** * @private */ getContentTarget: function() { return this.el; }, getDragEl: function() { return this.el; }, /** * Get an el for overflowing, defaults to the target el * @private */ getOverflowEl: function() { return this.getTargetEl(); }, /** * @private * Returns the CSS style object which will set the Component's scroll styles. * This must be applied to the {@link #getTargetEl target element}. */ getOverflowStyle: function() { var me = this, scroller = me.getScrollable(), flags = me._scrollFlags, x, y, scrollFlags; if (scroller) { x = scroller.getX(); if (x === true) { x = 'auto'; } y = scroller.getY(); if (y === true) { y = 'auto'; } scrollFlags = flags[x][y]; } else { scrollFlags = flags.none; } me.scrollFlags = scrollFlags; return { overflowX: scrollFlags.overflowX, overflowY: scrollFlags.overflowY }; }, /** * Returns an array of current fully constructed plugin instances. * @private */ getPlugins: function() { var plugins = this.plugins; plugins = (plugins && plugins.processed) ? plugins : this.constructPlugins(); return plugins || null; }, getProxy: function() { var me = this, target; if (!me.proxy) { target = Ext.getBody(); me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true); } return me.proxy; }, /** * This is used to determine where to insert the 'html', 'contentEl' and 'items' in this * component. * @private */ getTargetEl: function() { return this.frameBody || this.el; }, /** * @private * Needed for when widget is rendered into a grid cell. The class to add to the cell * element. */ getTdCls: function() { return Ext.baseCSSPrefix + this.getTdType() + '-' + this.ui + '-cell'; }, /** * @private * Partner method to {@link #getTdCls}. * * Returns the base type for the component. Defaults to return `this.xtype`, but * All derived classes of {@link Ext.form.field.Text TextField} can return the type * 'textfield', and all derived classes of {@link Ext.button.Button Button} can return * the type 'button' */ getTdType: function() { return this.xtype; }, /** * @private */ getTpl: function(name) { //<debug> Ext.log.warn('getTpl is deprecated, use lookupTpl.'); //</debug> return this.lookupTpl(name); }, initCls: function() { var me = this, cls = [me.baseCls], targetCls = me.getComponentLayout().targetCls; if (targetCls) { cls.push(targetCls); } //<deprecated since=0.99> if (Ext.isDefined(me.cmpCls)) { if (Ext.isDefined(Ext.global.console)) { Ext.global.console.warn( 'Ext.Component: cmpCls has been deprecated. Please use componentCls.' ); } me.componentCls = me.cmpCls; delete me.cmpCls; } //</deprecated> if (me.componentCls) { cls.push(me.componentCls); } else { me.componentCls = me.baseCls; } return cls; }, initDraggable: function() { var me = this, dragTarget, ddConfig; // If we are resizable, and the resizer had to wrap this Component's el (e.g. 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(), // eslint-disable-next-line max-len constrainTo: (me.constrain || me.draggable.constrain) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : 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); }, /** * Initializes padding by applying it to the target element, or if the layout manages * padding ensures that the padding on the target element is "0". * @private */ initPadding: function(targetEl) { var me = this, padding = me.padding; if (padding != null) { // eslint-disable-next-line max-len if (me.layout && me.layout.managePadding && me.contentPaddingProperty === 'padding') { // If the container layout manages padding, or if a touch scroller is in // use, the padding will be applied to an inner layout element, or the // touch scroller element. This is done as a workaround for the browser bug // where right and/or bottom padding is lost when the element has overflow. // The assumed intent is for the configured padding to override any padding // that is applied to the target element via style sheet rules. It is // therefore necessary to set the target element's padding to "0". targetEl.setStyle('padding', 0); } else { // Convert the padding, margin and border properties // from a space separated string into a proper style string targetEl.setStyle('padding', this.unitizeBox((padding === true) ? 5 : padding)); } } }, /** * @private */ initPlugin: function(plugin) { plugin.init(this); return plugin; }, initResizable: function(resizable) { var me = this; resizable = Ext.apply({ target: me, dynamic: false, // eslint-disable-next-line max-len constrainTo: (me.constrain || (resizable && resizable.constrain)) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : undefined, handles: me.resizeHandles }, resizable); resizable.target = me; me.resizer = new Ext.resizer.Resizer(resizable); }, /** * Applies padding, margin, border, top, left, height, and width configs to the * appropriate elements. * @private */ initStyles: function(targetEl) { var me = this, margin = me.margin, border = me.border, cls = me.cls, style = me.style, x = me.x, y = me.y, liquidLayout = me.liquidLayout, width, height; me.initPadding(targetEl); if (margin != null) { targetEl.setStyle('margin', this.unitizeBox((margin === true) ? 5 : margin)); } if (border != null) { me.setBorder(border, targetEl); } // initComponent, beforeRender, or event handlers may have set the style // or cls property since the protoEl was set up so we must apply styles // and classes here too. if (cls && cls !== me.initialCls) { targetEl.addCls(cls); me.cls = me.initialCls = null; } if (style && style !== me.initialStyle) { targetEl.setStyle(style); me.style = me.initialStyle = null; } if (x != null) { targetEl.setStyle(me.horizontalPosProp, (typeof x === 'number') ? (x + 'px') : x); } if (y != null) { targetEl.setStyle('top', (typeof y === 'number') ? (y + 'px') : y); } if (!me.ownerCt || me.floating) { if (Ext.scopeCss) { targetEl.addCls(me.rootCls); } targetEl.addCls(me.borderBoxCls); } // Framed components need their width/height to apply to the frame, which is // best handled in layout at present. The exception to this rule is component // that use liquidLayout and so cannot rely on the layout to set their size. if (liquidLayout || !me.getFrameInfo()) { width = me.width; height = me.height; // If we're using the content box model, we also cannot assign // numeric initial sizes since we do not know the border widths to subtract if (width != null) { if (typeof width === 'number') { targetEl.setStyle('width', width + 'px'); } else { targetEl.setStyle('width', width); } } if (height != null) { if (typeof height === 'number') { targetEl.setStyle('height', height + 'px'); } else { targetEl.setStyle('height', height); } } } }, // 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); }, isDescendant: function(ancestor) { var c; if (ancestor.isContainer) { for (c = this.ownerCt; c; c = c.ownerCt) { if (c === ancestor) { return true; } } } return false; }, /** * @private * Returns `true` if the passed element is within the container tree of this component. * * For example if a menu's submenu contains an {@link Ext.form.field.Date}, that top level * menu owns the elements of the date picker. Using this method, you can tell if an event * took place within a certain component tree. */ owns: function(element) { var result = false, cmp; if (element.isEvent) { element = element.target; } else if (element.isElement) { element = element.dom; } cmp = Ext.Component.fromElement(element); if (cmp) { result = (cmp === this) || (!!cmp.up(this)); } return result; }, parseBox: function(box) { return Ext.Element.parseBox(box); }, reattachToBody: function() { // Also see detachFromBody this.isDetached = false; }, removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope) { var me = this, element = managedListener.options ? managedListener.options.element : null; if (element) { element = me[element]; if (element && element.un) { // eslint-disable-next-line max-len if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) { element.un( managedListener.ename, managedListener.fn, managedListener.scope ); if (!isClear) { Ext.Array.remove(me.managedListeners, managedListener); } } } } else { return me.mixins.observable.removeManagedListenerItem.apply(me, arguments); } }, removeOverCls: function() { this.el.removeCls(this.overCls); }, removePlugin: function(plugin) { Ext.Array.remove(this.plugins, plugin); plugin.destroy(); }, /** * Method which removes a specified UI from the components element. * @private */ removeUIFromElement: function() { var me = this, baseClsUI = me.baseCls + '-' + me.ui, childEls, childElName, el, suffix; me.removeCls(baseClsUI); if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { // Loop through each frame element, and if they are defined remove the ui: baseClsUI += '-'; childEls = me.getChildEls(); for (childElName in childEls) { suffix = childEls[childElName].frame; if (suffix && suffix !== true) { el = me[childElName]; if (el) { el.removeCls(baseClsUI + suffix); } } } } }, /** * @private */ setComponentLayout: function(layout) { var currentLayout = this.componentLayout; if (currentLayout && currentLayout.isLayout && currentLayout !== layout) { currentLayout.setOwner(null); } this.componentLayout = layout; layout.setOwner(this); }, setHiddenState: function(hidden) { var me = this, inheritedState = me.getInherited(), zIndexManager = me.zIndexManager; me.hidden = hidden; if (hidden) { inheritedState.hidden = true; } else { delete inheritedState.hidden; } // Ensure any ZIndexManager knowns about visibility state change // to keep its filtering correct if (zIndexManager) { zIndexManager.onComponentShowHide(me); } }, setupProtoEl: function() { var cls = this.initCls(); this.protoEl.addCls(cls); }, wrapPrimaryEl: function(dom) { var me = this, el = me.el; if (!el || !el.isElement) { // allow subclass override to instantiate the element me.el = Ext.get(dom); } if (me.floating) { this.mixins.floating.constructor.call(this); } } }, // private deprecated: { 5: { methods: { /** * @method addClass * @inheritdoc Ext.Component#addCls * @deprecated 4.1 Use {@link #addCls} instead. * @since 2.3.0 */ addClass: 'addCls', /** * @method doComponentLayout * This method needs to be called whenever you change something on this * component that equires the Component's layout to be recalculated. * @return {Ext.Component} this * @deprecated 4.1 Use {@link Ext.Component#method-updateLayout} instead. */ doComponentLayout: function() { this.updateLayout(); return this; }, /** * @method removeClass * @inheritdoc Ext.Component#removeCls * @deprecated 4.1 Use {@link #addCls} instead. * @since 2.3.0 */ removeClass: 'removeCls', /** * @method forceComponentLayout * @inheritdoc Ext.Component#updateLayout * @deprecated 4.1 Use {@link Ext.Component#method-updateLayout} instead. */ forceComponentLayout: 'updateLayout', /** * @method setDocked * @inheritdoc Ext.Component#setDock * @deprecated 5.0 Use {@link #setDock} instead. */ setDocked: 'setDock' } } }}, function(Component) { var prototype = Component.prototype; // event options for listeners that use the "element" event options must also include // event options from Ext.Element (prototype.$elementEventOptions = Ext.Object.chain(Ext.Element.prototype.$eventOptions)).element = 1; (prototype.$eventOptions = Ext.Object.chain(prototype.$eventOptions)).delegate = 1; Component.createAlias({ on: 'addListener', prev: 'previousSibling', next: 'nextSibling', setUi: 'setUI' }); /** * @method resumeLayouts * @inheritdoc Ext.Component#static-method-resumeLayouts * @member Ext */ Ext.resumeLayouts = function(flush) { Component.resumeLayouts(flush); }; /** * @method suspendLayouts * @inheritdoc Ext.Component#static-method-suspendLayouts * @member Ext */ Ext.suspendLayouts = function() { Component.suspendLayouts(); }; /** * @method batchLayouts * Utility wrapper that suspends layouts of all components for the duration of a given * function. * @param {Function} fn The function to execute. * @param {Object} [scope] The scope (`this` reference) in which the specified function * is executed. * @member Ext */ Ext.batchLayouts = function(fn, scope) { Component.suspendLayouts(); // Invoke the function // note: try/finally works in IE8 standards mode try { fn.call(scope); } finally { Component.resumeLayouts(true); } }; /** * @method setGlyphFontFamily * Sets the default font-family to use for components that support a `glyph` config. * @param {String} fontFamily The name of the font-family * @member Ext */ Ext.setGlyphFontFamily = function(fontFamily) { Ext._glyphFontFamily = fontFamily; }; // These are legacy names which are now subsumed by Ext.GlobalEvents Component.hierarchyEventSource = prototype.hierarchyEventSource = Ext.GlobalEvents; // High Contrast mode in Windows disables background images and usually // causes much pain by this. We feature detect it early and add this class // to allow potential workarounds. Ext.onReady(function() { if (Ext.supports.HighContrastMode) { Ext.getBody().addCls(Component.ariaHighContrastModeCls); } });}); /* TODO * * ## Child Sessions * * By default, sessions created in child components are called "child sessions". * All data and edits are copied from the parent session in to the child session. * These changes can be written back to the parent session much like a session * can save its data to a server. * * Ext.create({ * xtype: 'window', * modal: true, * * // Creates a new session for this Window but that session is linked * // to a parent session (probably from the application's Viewport). * // * session: true, * * items: [{ * ... * },{ * xtype: 'button', * text: 'OK', * listeners: { * click: function() { * // Get the Session this component has inherited (from * // the Window) and save it back to the parent session. * // * this.lookupSession().save(); * } * } * }] * }); * * ### Isolated Sessions * * This connection of a child session to its parent session is determined by the * `{@link Ext.data.Session#isolated isolated}` config. If `isolated` is * set to `true` then this session will not copy anything from a higher level * session and will instead do its own communication with the server. * * Ext.create({ * xtype: 'window', * modal: true, * * // Creates a new session for this Window that is isolated from any * // other sessions. All data for this session will be fetched from * // the server and ultimately saved back to the server. * // * session: { * isolated: true * }, * * items: [{ * ... * },{ * xtype: 'button', * text: 'OK', * listeners: { * click: function() { * // Get the Session this component has inherited (from * // the Window) and save it back to the server. * // * this.lookupSession().save(); * } * } * }] * }); */