/** * Ext.Widget is a light-weight Component that consists of nothing more than a template * Element that can be cloned to quickly and efficiently replicate many instances. * Ext.Widget is typically not instantiated directly, because the default template is * just a single element with no listeners. Instead Ext.Widget should be extended to * create Widgets that have a useful markup structure and event listeners. * * For example: * * Ext.define('MyWidget', { * extend: 'Ext.Widget', * * // The element template passed to Ext.Element.create() * element: { * reference: 'element', * listeners: { * click: 'onClick' * }, * children: [{ * reference: 'innerElement', * listeners: { * click: 'onInnerClick' * } * }] * }, * * constructor: function(config) { * // It is important to remember to call the Widget superclass constructor * // when overriding the constructor in a derived class. This ensures that * // the element is initialized from the template, and that initConfig() is * // is called. * this.callParent([config]); * * // After calling the superclass constructor, the Element is available and * // can safely be manipulated. Reference Elements are instances of * // Ext.Element, and are cached on each Widget instance by reference name. * Ext.getBody().appendChild(this.element); * }, * * onClick: function() { * // listeners use this Widget instance as their scope * console.log('element clicked', this); * }, * * onInnerClick: function() { * // access the innerElement reference by name * console.log('inner element clicked', this.innerElement); * } * }); * * @since 5.0.0 * @disable {DuplicateAlternateClassName} */Ext.define('Ext.Widget', { extend: 'Ext.Evented', xtype: 'widget', alternateClassName: 'Ext.Gadget', requires: [ 'Ext.dom.Element' ], mixins: [ 'Ext.mixin.Inheritable', 'Ext.mixin.Bindable', 'Ext.mixin.ComponentDelegation', 'Ext.mixin.Pluggable', 'Ext.mixin.Keyboard', 'Ext.mixin.Factoryable', 'Ext.mixin.Focusable', 'Ext.mixin.Accessible' ], isWidget: true, factoryConfig: { creator: null, defaultProperty: 'xtype', defaultType: 'component', typeProperty: 'xtype' }, /* eslint-disable max-len */ /** * @property {Object} element * A configuration object for Ext.Element.create() that is used to create the Element * template. Subclasses should avoid overriding this property and instead add elements * using {@link #template}. * * Supports all the standard options of a Ext.Element.create() config and adds 3 * additional options: * * 1. `reference` - this option specifies a name for Element references. These * references names become properties of the Widget instance and refer to Ext.Element * instances that were created using the template: * * element: { * reference: 'element', * children: [{ * reference: 'innerElement' * }] * } * * After construction of a widget the reference elements are accessible as follows: * * var foo = new FooWidget(), * innerEl = foo.innerElement; // an Ext.Element that wraps the innerElement * * The reference attribute is optional, but all Widgets must have a `'element'` * reference on some element within the template (usually the outermost one). * * 2. `listeners` - a standard listeners object as specified by {@link Ext.mixin.Observable}. * * element: { * reference: 'element', * listeners: { * click: 'onClick' * }, * children: [{ * reference: 'innerElement', * listeners: { * click: 'onInnerClick' * } * }] * } * * Since listeners cannot be attached without an Ext.Element reference the `reference` * property MUST be specified in order to use `listeners`. * * The Widget instance is used as the scope for all listeners specified in this way, * so it is invalid to use the `scope` option in the `listeners` config since it will * always be overwritten using `this`. * * 3. `uiCls` - a suffix to be appended to the ui-specific CSS class for each `{@link #ui}` * for this widget. These ui classes are constructed by appending the `ui` to each * `{@link #classCls}` or `{@link #baseCls}` for the widget. As such, `uiCls` should * never be used on the main `element` reference, as its `uiCls` is computed automatically. * * For example, assume a widget is defined with a `ui` of `'alt action'` and a * `uiCls` of `'inner-el'` on its `innerElement` reference element: * * Ext.define('Doodad', { * extend: 'Ext.Widget', * xtype: 'doodad', * * classCls: 'x-doodad', * * ui: 'alt action', * * element: { * reference: 'element', * * children: [{ * reference: 'innerElement', * cls: 'x-inner-el', * uiCls: 'inner-el' * }] * } * }); * * This would result in the following markup when rendered: * * <div class="x-doodad x-doodad-alt x-doodad-action"> * <div class="x-inner-el x-doodad-inner-el x-doodad-alt-inner-el x-doodad-action-inner-el"></div> * </div> * * These additional classes can be used to style the reference element for a particular * ui; however, use of `uiCls` is not typically necessary or recommended. Reference * elements should usually be styled using simple descendant selectors: * * .x-doodad-alt .x-inner-el { * color: red; * } * * When there is a possibility that widgets can be nested it is best to use direct * child selectors to avoid the possibility of selecting all descendants instead * of just the reference element for the intended widget: * * .x-doodad-alt > .x-inner-el { * color: red; * } * * Only use `uiCls` when there is a possibility of nesting, AND there may be a variable * number of elements between the main `element` and the reference element in question. * For example, Ext.Container with docked items has a different number of elements * in between its `element` and its `bodyElement` than a Container without docked items * because of the wrapping elements that are dynamically added to support docking. * To ensure it does not style all descendants it must use a `uiCls` to style its * `bodyElement`: * * .x-container-alt-body-el { * background: #fff; * } * * Note that when `uiCls` is specified it also adds a class name that does not contain * the `ui` using just the `classCls` and/or `baseCls` as the prefix. This class name * can be used for base-level styling that does not relate to any particular UI: * * .x-container-body-el { * position: relative; * } * * @protected */ element: { reference: 'element' }, /* eslint-enable */ observableType: 'component', cachedConfig: { /** * @cfg {String/String[]} cls The CSS class to add to this widget's element, in * addition to the {@link #baseCls}. In many cases, this property will be specified * by the derived widget class. See {@link #userCls} for adding additional CSS * classes to widget instances (such as items in a {@link Ext.Container}). * @accessor */ cls: null, /** * @cfg {Number/String} margin * The margin to use on this Component. Can be specified as a number (in which * case all edges get the same margin) or a CSS string like '5 10 10 10' */ margin: null, /** * @cfg {String/Object} style * Additional CSS styles that will be rendered into an inline style attribute when * the widget is rendered. * * You can pass either a string syntax: * * style: 'background:red' * * Or by using an object: * * style: { * background: 'red' * } * * When using the object syntax, you can define CSS Properties by using a string: * * style: { * 'border-left': '1px solid red' * } * * Although the object syntax is much easier to read, we suggest you to use the * string syntax for better performance. * @accessor set */ style: null, /** * @cfg {Boolean} border Enables or disables bordering on this component. * The following values are accepted: * * - `null` or `true (default): Do nothing and allow the border to be specified * by the theme. * - `false`: suppress the default border provided by the theme. * * Please note that enabling bordering via this config will not add a `border-color` * or `border-style` CSS property to the component; you provide the `border-color` * and `border-style` via CSS rule or {@link #style} configuration * (if not already provide by the theme). * * ## Using {@link #style}: * * Ext.Viewport.add({ * centered: true, * width: 100, * height: 100, * * style: 'border: 1px solid blue;' * // ... * }); * * ## Using CSS: * * Ext.Viewport.add({ * centered: true, * width: 100, * height: 100, * * cls: 'my-component' * // ... * }); * * And your CSS file: * * .my-component { * border: 1px solid red; * } * * @accessor */ border: null, /** * @cfg {Object} * * 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 reference elements using the reference 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.Widget', { * touchAction: { * panX: false * }, * listeners: { * drag: function(e) { * // implement drag logic * } * } * }); */ touchAction: null, /** * @cfg {Object} eventHandlers A map of event type to the corresponding handler method * name. This is used internally by native event handling mechanism. * @private * @deprecated 6.6.0 Inline event handlers are deprecated */ eventHandlers: { focus: 'handleFocusEvent', blur: 'handleBlurEvent' } }, /** * @cfg {String} name Name for the widget to be used with {@link Ext.Container#lookupName} * et al. */ name: null, config: { /** * @cfg {Ext.Element} [renderTo] Optional element to render this Component to. * Not required if this component is an {@link Ext.Container#items item} of a Container * of a Container. */ renderTo: null, /** * @cfg {String/String[]} ui The ui or uis to be used on this Component * * When a ui is configured, CSS class names are added to the {@link #element}, created * by appending the ui name(s) to each {@link #classCls} and/or {@link #baseCls}. */ ui: null, /** * @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' * ... * }] */ userCls: null, /** * @cfg {Boolean/Object/String} ripple * Set to truthy, Color or Object value for the ripple. * @cfg {String} ripple.color The background color of the ripple. * @cfg {Array} ripple.position Position for the ripple to start at [x,y]. * Determines if a Ripple effect should happen whenever this element is pressed. * * For example: * { * ripple: true * } * * Or: * * { * ripple: { * color: 'red' * } * } * * For complex components, individual elements can suppress ripples by adding the * `x-no-ripple` class to disable rippling for a tree of elements. * * @since 6.5.0 */ ripple: null, /** * @cfg {'clip'/'display'/'offsets'/'opacity'/'visibility'} [hideMode='display'] * A String which specifies how this component's DOM element will be hidden. The * accepted values are any of these: * * - `'clip'` : Hide using {@link Ext.dom.Element#CLIP clip}. * - `'display'` : Hide using {@link Ext.dom.Element#DISPLAY display}. * - `'offsets'` : Hide using positioning {@link Ext.dom.Element#OFFSETS offsets}. * - `'opacity'` : Hide using {@link Ext.dom.Element#OPACITY opacity}. * - `'visibility'` : Hide using {@link Ext.dom.Element#VISIBILITY visibility}. * * Hiding using ``display`` results in having no dimensions as well as resetting * scroll positions to 0. * * The other modes overcome this but may have different trade-offs in certain * circumstances. * * @since 6.5.0 */ hideMode: null, /** * @cfg {String/String[]} instanceCls * * An extra CSS class or classes to augment the {@link #classCls} on an individual instance * * @private * @since 6.5.0 */ instanceCls: null }, eventedConfig: { /** * @cfg {Number/String} width * The width of this Component; must be a valid CSS length value, e.g: `300`, `100px`, * `30%`, etc. By default, if this is not explicitly set, this Component's element will * simply have its own natural size. If set to `auto`, it will set the width to `null` * meaning it will have its own natural size. * @accessor * @evented */ width: null, /** * @cfg {Number/String} height * The height of this Component; must be a valid CSS length value, e.g: `300`, `100px`, * `30%`, etc. By default, if this is not explicitly set, this Component's element will * simply have its own natural size. If set to `auto`, it will set the width to `null` * meaning it will have its own natural size. * @accessor * @evented */ height: null, /** * @cfg {Boolean} [hidden] * Whether or not this Component is hidden (its CSS `display` property is set to `none`). * * Defaults to `true` for {@link #floated} Components. * @accessor * @evented */ hidden: null, /** * @cfg {Boolean} [disabled] * Whether or not this component is disabled * @accessor * @evented */ disabled: null }, /** * @property {Array} template * An array of child elements to use as the children of the main element in the {@link * #element} template. Only used if "children" are not specified explicitly in the * {@link #element} template. * @protected */ template: [], /** * The base CSS class to apply to this widget's element. * Used as the prefix for {@link #ui}-specific class names. * Defaults to the value of {@link #classCls} or (`x-` + the {@link #xtype}) of the widget * if {@link #classCls} is `null` * @protected * @property */ baseCls: null, /** * A CSS class to apply to the main element that will be inherited down the class * hierarchy. Subclasses may override this property on their prototype to add their * own CSS class in addition to the CSS classes inherited from ancestor classes via * the prototype chain. For example * * Ext.define('Foo', { * extend: 'Ext.Widget', * classCls: 'foo' * }); * * Ext.define('Bar', { * extend: 'Foo', * classCls: 'bar' * }); * * var bar = new Bar(); * * console.log(bar.element.className); // outputs 'foo bar' * * @protected * @property */ classCls: null, /** * When set to `true` during widget class definition, that class will be the "root" for * {@link #classCls} inheritance. Derived classes may set this to `true` to avoid * inheriting a {@link #classCls} from their superclass. * @property * @protected */ classClsRoot: true, // default empty classClsList since Ext.Widget has no classCls of its own classClsList: [], clearPropertiesOnDestroy: 'async', focusEl: 'element', ariaEl: 'element', spaceRe: /\s+/, /** * @property {String} [noBorderCls] The CSS class to add to this component should not have * a border. * @private * @readonly */ noBorderCls: Ext.baseCSSPrefix + 'noborder-trbl', borderedCls: Ext.baseCSSPrefix + 'bordered', disabledCls: Ext.baseCSSPrefix + 'disabled', heightedCls: Ext.baseCSSPrefix + 'heighted', widthedCls: Ext.baseCSSPrefix + 'widthed', constructor: function(config) { var me = this, baseCls = me.baseCls, renderTo = config && config.renderTo, controller; me.$iid = ++Ext.$nextIid; if (baseCls == null || baseCls === true) { me.baseCls = me.classCls || Ext.baseCSSPrefix + me.xtype; } //<debug> if (config && ('baseCls' in config)) { Ext.raise('baseCls cannot be used as an instance config. It must be specified ' + 'at class definition time.'); } //</debug> // 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; } me.initId(config); me.initElement(); if (renderTo) { config = Ext.apply({}, config); delete config.renderTo; } me.mixins.observable.constructor.call(me, config); // Wait until configs have run to do this if (me.focusable) { me.initFocusableEvents(true); } me.syncUiCls(); Ext.ComponentManager.register(me); controller = me.getController(); if (controller) { controller.init(me); } if (renderTo) { me.setRenderTo(renderTo); } }, afterCachedConfig: function() { // This method runs once for the first instance of this Widget type that is // created. It runs after the element config has been processed for the first // instance, and after all the cachedConfigs (whose appliers/updaters may modify // the element) have been initialized. At this point we are ready to take the // DOM that was generated for the first Element instance, clone it, and cache it // on the prototype, so that it can be cloned by future instance to create their // elements (see initElement). var me = this, prototype = me.self.prototype, referenceList = me.referenceList, renderElement = me.renderElement, renderTemplate, element, i, ln, reference, elements; // This is where we take the first instance's DOM and clone it as the template // for future instances prototype.renderTemplate = renderTemplate = document.createDocumentFragment(); renderTemplate.appendChild(renderElement.clone(true, true)); elements = renderTemplate.querySelectorAll('[id]'); for (i = 0, ln = elements.length; i < ln; i++) { element = elements[i]; element.removeAttribute('id'); } // initElement skips removal of reference attributes for the first instance so that // the reference attributes will be present in the cached element when it is cloned. // Now that we're done cloning and caching the template element, it is safe to // remove the reference attributes from this instance's elements for (i = 0, ln = referenceList.length; i < ln; i++) { reference = referenceList[i]; me[reference].dom.removeAttribute('reference'); } }, applyHidden: function(hidden) { return !!hidden; }, applyDisabled: function(disabled) { return !!disabled; }, updateDisabled: function(disabled) { var me = this, container = me.ownerFocusableContainer; if (container) { if (disabled) { if (!container.beforeFocusableChildDisable.$nullFn) { container.beforeFocusableChildDisable(me); } } else { if (!container.beforeFocusableChildEnable.$nullFn) { container.beforeFocusableChildEnable(me); } } } me.element.toggleCls(me.disabledCls, disabled); if (me.focusable) { if (disabled) { me.disableFocusable(); } else { me.enableFocusable(); } } if (container) { if (disabled) { if (!container.onFocusableChildDisable.$nullFn) { container.onFocusableChildDisable(me); } } else { if (!container.onFocusableChildEnable.$nullFn) { container.onFocusableChildEnable(me); } } } }, /** * Disables this Component */ disable: function() { this.setDisabled(true); }, /** * Enables this Component */ enable: function() { this.setDisabled(false); }, /** * Returns `true` if this Component is currently disabled. * @return {Boolean} `true` if currently disabled. */ isDisabled: function() { return this.getDisabled(); }, /** * Returns `true` if this Component is not currently disabled. * @return {Boolean} `true` if not currently disabled. */ isEnabled: function() { return !this.getDisabled(); }, applyTouchAction: function(touchAction, oldTouchAction) { if (oldTouchAction != null) { touchAction = Ext.merge({}, oldTouchAction, touchAction); } return touchAction; }, applyWidth: function(width) { return this.filterLengthValue(width); }, applyHeight: function(height) { return this.filterLengthValue(height); }, updateBorder: function(border) { var me = this; // If the border is null it means we should not suppress the border border = border || border === null; me.toggleCls(me.noBorderCls, !border); me.toggleCls(me.borderedCls, !!border); }, clearListeners: function() { var me = this; me.mixins.observable.clearListeners.call(me); me.mixins.componentDelegation.clearDelegatedListeners.call(me); }, /** * Destroys the Widget. This method should not be overridden in custom Widgets, * because it sets the flags and does final cleanup that must go last. Instead, * override {@link #doDestroy} method to add functionality at destruction time. */ destroy: function() { var me = this; // isDestroying added for compat reasons me.isDestroying = me.destroying = true; me.destroy = Ext.emptyFn; 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(); // This just makes it hard to ask "was destroy() called?": // me.isDestroying = me.destroying = false; // removed in 7.0 // ComponentDelegation mixin does not install "after" interceptor on the // base class destructor so we need to call it explicitly. me.mixins.componentDelegation.destroyComponentDelegation.call(me); me.callParent(); }, /** * Perform the actual destruction sequence. This is the method to override in your * subclasses to add steps specific to the destruction of custom Component or Widget. * * As a rule of thumb, subclasses should destroy their child Components, Elements, * 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. * * @since 6.2.0 */ doDestroy: function() { var me = this, referenceList = me.referenceList, container = me.ownerFocusableContainer, i, ln, reference; // Many non-contained Widgets use the ownerCmp link to find their logical owner. me.ownerCmp = null; if (container && !container.onFocusableChildDestroy.$nullFn) { container.onFocusableChildDestroy(me); } // Destroy all element references for (i = 0, ln = referenceList.length; i < ln; i++) { reference = referenceList[i]; if (me.hasOwnProperty(reference)) { me[reference].destroy(); me[reference] = null; } } me.destroyBindable(); Ext.ComponentManager.unregister(me); }, doFireEvent: function(eventName, args, bubbles) { var me = this, ev, ret; ret = me.mixins.observable.doFireEvent.call(me, eventName, args, bubbles); // Could have been destroyed in event handler. if (ret !== false && !me.destroyed) { ev = me.events[eventName]; // Also, a suspendEvent() call on the target could be in effect: if (!ev || !ev.suspended) { ret = me.mixins.componentDelegation.doFireDelegatedEvent.call(me, eventName, args); } } return ret; }, getBubbleTarget: function() { return this.getRefOwner(); }, /** * A template method for modifying the {@link #element} config before it is processed. * By default adds the result of `this.getTemplate()` as the `children` array of * {@link #element} if `children` were not specified in the original * {@link #element} config. Typically this method should not need to be implemented * in subclasses. Instead the {@link #element} property should be use to configure * the element template for a given Widget subclass. * * This method is called once when the first instance of each Widget subclass is * created. The element config object that is returned is cached and used as the template * for all successive instances. The scope object for this method is the class prototype, * not the instance. * * @return {Object} the element config object * @protected */ getElementConfig: function() { var me = this, el = me.element; if (!('children' in el)) { el = Ext.apply({ children: me.getTemplate() }, el); } return el; }, /** * Returns the height and width of the Component. * @return {Object} The current `height` and `width` of the Component. * @return {Number} return.width * @return {Number} return.height */ getSize: function() { return this.el.getSize(); }, getTemplate: function() { return Ext.clone(this.template); }, /** * @private */ getClassCls: function() { var proto = this.self.prototype, prototype = proto, classes, classCls, i, ln; while (prototype) { classCls = prototype.hasOwnProperty('classCls') ? prototype.classCls : null; if (classCls) { if (classCls instanceof Array) { for (i = 0, ln = classCls.length; i < ln; i++) { (classes || (classes = [])).push(classCls[i]); } } else { (classes || (classes = [])).push(classCls); } } if (prototype.classClsRoot && prototype.hasOwnProperty('classClsRoot')) { break; } prototype = prototype.superclass; } if (classes) { proto.classClsList = classes; } return classes; }, hide: function() { this.setHidden(true); }, /** * Initializes the Element for this Widget instance. If this is the first time a * Widget of this type has been instantiated the {@link #element} config will be * processed to create an Element. This Element is then cached on the prototype (see * afterCachedConfig) so that future instances can obtain their element by simply * cloning the Element that was cached by the first instance. * @protected */ initElement: function() { var me = this, prototype = me.self.prototype, id = me.getId(), // The double assignment is intentional to workaround a JIT issue that prevents // me.referenceList from being assigned in random scenarios. The issue occurs // on 4th gen iPads and lower, possibly other older iOS devices. See EXTJS-16494. referenceList = me.referenceList = me.referenceList = [], isFirstInstance = !prototype.hasOwnProperty('renderTemplate'), /** * @property {Object} uiReferences * @private * A map that tracks all reference elements configured with a `uiCls`. * Contains the `element` reference by default since the `element` always gets * non-suffixed ui-specific CSS class names added to it (see {@link #syncUiCls}) */ uiReferences = prototype.hasOwnProperty('uiReferences') ? prototype.uiReferences : (prototype.uiReferences = { element: '' }), renderTemplate, renderElement, renderConfig, element, referenceNodes, i, ln, referenceNode, reference, classCls, uiCls, baseCls, /* eslint-disable-next-line no-unused-vars */ referenceElement; if (isFirstInstance) { // this is the first instantiation of this widget type. Process the element // config from scratch to create our Element. renderTemplate = document.createDocumentFragment(); renderConfig = me.processElementConfig.call(prototype); renderElement = Ext.Element.create(renderConfig, true); renderTemplate.appendChild(renderElement); // Collect all nodes that were configured with a uiCls and stash the reference // and uiCls names in a map so they can be used to update the ui class names // at a later time (see syncUiCls). referenceNodes = renderTemplate.querySelectorAll('[uiCls]'); for (i = 0, ln = referenceNodes.length; i < ln; i++) { referenceNode = referenceNodes[i]; reference = referenceNode.getAttribute('reference'); uiCls = referenceNode.getAttribute('uiCls'); //<debug> if (!reference) { Ext.raise('Cannot render element with uiCls="' + uiCls + '". uiCls is only allowed on elements that have a reference name.'); } //</debug> uiReferences[reference] = uiCls; // uiCls attribute has served its purpose - we have cached it in the // uiReferences map so it does not need to remain in the template referenceNode.removeAttribute('uiCls'); } } else { // we have already created an instance of this Widget type, so the element // config has already been processed, and the resulting DOM has been cached on // the prototype (see afterCachedConfig). This means we can obtain our element // by simply cloning the cached element. renderTemplate = me.renderTemplate.cloneNode(true); renderElement = renderTemplate.firstChild; } referenceNodes = renderTemplate.querySelectorAll('[reference]'); for (i = 0, ln = referenceNodes.length; i < ln; i++) { referenceNode = referenceNodes[i]; reference = referenceNode.getAttribute('reference'); if (!isFirstInstance) { // on first instantiation we do not clean the reference attributes here. // This is because this instance's element will be used as the template // for future instances, and we need the reference attributes to be // present in the template so that future instances can resolve their // references. afterCachedConfig is responsible for removing the // reference attributes from the DOM for the first instance after the // Element has been cloned and cached as the template. referenceNode.removeAttribute('reference'); } if (reference === 'element') { //<debug> if (element) { // already resolved a reference named element - can't have two Ext.raise("Duplicate 'element' reference detected in '" + me.$className + "' template."); } //</debug> referenceNode.id = id; // element reference needs to be established ASAP, so add the reference // immediately, not "on-demand" element = me.el = me.addElementReference(reference, referenceNode); // Poke our id in our magic attribute to enable Component#from element.dom.setAttribute('data-componentid', id); if (isFirstInstance) { classCls = me.getClassCls(); if (classCls) { element.addCls(classCls); } baseCls = me.baseCls; if (baseCls && (baseCls !== me.classCls)) { element.addCls(baseCls); } } } else { uiCls = uiReferences[reference]; if (uiCls && isFirstInstance) { // Elements configured with a uiCls must always have non-ui-specific // class names for base styling. For example, if the classCls is x-foo // a reference element with a uiCls of 'bar' must always have a class // name of x-foo-bar, regardless of whether or not the widget was // configured with a ui. // On the first instance these element must be immediately instantiated // so that the CSS class names can be added in the render template, // but they can be lazily instantiated on successive instances. referenceElement = me.addElementReference(reference, referenceNode); me.initUiReference(reference, uiCls, false); } else { me.addElementReferenceOnDemand(reference, referenceNode); } } // At this point focusEl and ariaEl are still reference names if (reference === me.focusEl) { me.addElementReference('focusEl', referenceNode); } if (reference === me.ariaEl) { me.addElementReferenceOnDemand('ariaEl', referenceNode); } referenceList.push(reference); } //<debug> if (!element) { Ext.raise("No 'element' reference found in '" + me.$className + "' template."); } //</debug> if (renderElement === element.dom) { me.renderElement = element; } else { me.addElementReferenceOnDemand('renderElement', renderElement); } renderElement.setAttribute(me.dataXid, me.$iid); }, dataXid: 'data-' + Ext.baseCSSPrefix.substr(0, Ext.baseCSSPrefix.length - 1) + 'id', /** * Tests whether this Widget matches a {@link Ext.ComponentQuery ComponentQuery} * selector string. * @param {String} selector The selector string to test against. * @return {Boolean} `true` if this Widget matches the selector. */ is: function(selector) { return Ext.ComponentQuery.is(this, selector); }, /** * Returns `true` if this Component is currently hidden. * @param {Boolean/Ext.Widget} [deep=false] `true` to check if this component * is hidden because a parent container is hidden. Alternatively, a reference to the * top-most parent at which to stop climbing. * @return {Boolean} `true` if currently hidden. */ isHidden: function(deep) { var hidden = !!this.getHidden(), owner; if (!hidden && deep) { owner = this.getRefOwner(); while (owner && owner !== deep && !hidden) { hidden = !!owner.getHidden(); owner = owner.getRefOwner(); } } return hidden; }, /** * Returns `true` if this Component is currently visible. * * A Widget is visible if its element is not hidden, *and* has been * {@link #property!rendered} *and* has not been destroyed. * * @param {Boolean} [deep=false] `true` to check if this component * is visible and all parents are also visible. * * Contrast this with the {@link #isHidden} method which just checks the * hidden state of the component. * @return {Boolean} `true` if currently visible. */ isVisible: function(deep) { return this.rendered && !this.destroyed && !this.isHidden(deep); }, /** * 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: * * var t = new Ext.field.Text(); * var isText = t.isXType('textfield'); // true * var isBoxSubclass = t.isXType('field'); // true, descended from Ext.field.Field * var isBoxInstance = t.isXType('field', true); // false, not a direct * // Ext.field.Field instance * * @param {String} xtype The xtype to check for this Component. * @param {Boolean} shallow (optional) `false` to check whether this Component is descended * from the xtype (this is the default), or `true` to check whether this Component is directly * of the specified xtype. * @return {Boolean} `true` if this component descends from the specified xtype, `false` * otherwise. */ isXType: function(xtype, shallow) { return shallow ? (Ext.Array.indexOf(this.xtypes, xtype) !== -1) : !!this.xtypesMap[xtype]; }, /** * 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); }, owns: function(element) { var result = false, cmp; if (element.isEvent) { element = element.target; } else if (element.isElement) { element = element.dom; } cmp = Ext.Component.from(element); if (cmp) { result = (cmp === this) || (!!cmp.up(this)); } return result; }, render: function(container, insertBeforeElement) { if (container && container.isWidget) { container = container.el; } /* eslint-disable-next-line vars-on-top */ var dom = this.renderElement.dom, containerDom = Ext.getDom(container), insertBeforeChildDom; if (Ext.isNumber(insertBeforeChildDom)) { insertBeforeElement = containerDom.childNodes[insertBeforeElement]; } insertBeforeChildDom = Ext.getDom(insertBeforeElement); if (containerDom) { if (insertBeforeChildDom) { containerDom.insertBefore(dom, insertBeforeChildDom); } else { containerDom.appendChild(dom); } // A component is rendered if it is in the document. // A display:none component will have no offsetParent // so offsetParent is not a valid test for renderedness. this.setRendered(Ext.getBody().contains(dom), true); } }, /** * Toggles the specified CSS class on this element (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. * @chainable */ toggleCls: function(className, state) { this.element.toggleCls(className, state); return this; }, resolveListenerScope: function(defaultScope, skipThis) { // break the tie between Observable and Inheritable resolveListenerScope return this.mixins.inheritable.resolveListenerScope.call(this, defaultScope, skipThis); }, /** * Sets the size of the Component. * @param {Number} width The new width for the Component. * @param {Number} height The new height for the Component. */ setSize: function(width, height) { // Allow setSize to be called with a result from getSize. if (width && typeof width === 'object') { return this.setSize(width.width, width.height); } if (width !== undefined) { this.setWidth(width); } if (height !== undefined) { this.setHeight(height); } }, show: function() { this.setHidden(false); }, /** * Adds a CSS class (or classes) to this Component's rendered element. * @param {String/String[]} cls The CSS class(es) to add. * @param {String} [prefix=""] Optional prefix to add to each class. * @param {String} [suffix=""] Optional suffix to add to each class. */ addCls: function(cls, prefix, suffix) { if (!this.destroyed) { this.el.replaceCls(null, cls, prefix, suffix); } }, applyCls: function(cls) { return cls && Ext.dom.Element.splitCls(cls); }, applyUi: function(ui) { return this.parseUi(ui, true); }, /** * Removes the given CSS class(es) from this widget's primary element. * @param {String/String[]} cls The class(es) to remove. * @param {String} [prefix=""] Optional prefix to prepend before each class. * @param {String} [suffix=""] Optional suffix to append to each class. */ removeCls: function(cls, prefix, suffix) { if (!this.destroyed) { this.el.replaceCls(cls, null, prefix, suffix); } }, /** * Replaces specified classes with the newly specified classes. * It uses the {@link #addCls} and {@link #removeCls} methods, so if the class(es) you * are removing don't exist, it will still add the new classes. * @param {String/String[]} oldCls The class(es) to remove. * @param {String/String[]} newCls The class(es) to add. * @param {String} [prefix=""] Optional prefix to prepend before each class. * @param {String} [suffix=""] Optional suffix to append to each class. */ replaceCls: function(oldCls, newCls, prefix, suffix) { if (!this.destroyed) { this.el.replaceCls(oldCls, newCls, prefix, suffix); } }, /** * 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) { return this.el.hasCls(className); }, /** * @private * All cls methods directly report to the {@link #cls} configuration, so anytime it changes, * {@link #updateCls} will be called */ updateCls: function(newCls, oldCls) { this.element.replaceCls(oldCls, newCls); }, updateHidden: function(hidden) { var me = this, element = me.renderElement, container = me.ownerFocusableContainer; // If we are owned by a FocusableContainer, allow that to redirect // focus if we are being hidden. if (container) { if (hidden) { if (!container.beforeFocusableChildHide.$nullFn) { container.beforeFocusableChildHide(me); } } else { if (!container.beforeFocusableChildShow.$nullFn) { container.beforeFocusableChildShow(me); } } } // Not owned by a FocusableContainer - revert focus to previous focus holder. else if (hidden) { // Part of the Focusable mixin API. // If we have focus now, move focus back to whatever had it before. me.revertFocus(); } if (element && !element.destroyed) { if (hidden) { element.hide(); } else { element.show(); } } if (me.focusableContainer && me.activateFocusableContainer) { me.activateFocusableContainer(!hidden); } if (container) { if (hidden) { if (!container.onFocusableChildHide.$nullFn) { container.onFocusableChildHide(me); } } else { if (!container.onFocusableChildShow.$nullFn) { container.onFocusableChildShow(me); } } } }, updateMargin: function(margin) { this.element.setMargin(margin); }, updateRipple: function(ripple) { var me = this, el = me.el; if (el) { el.un('touchstart', 'onRippleStart', me); el.un('touchend', 'onRippleStart', me); el.destroyAllRipples(); if (ripple.release) { el.on('touchend', 'onRippleStart', me); } else { el.on('touchstart', 'onRippleStart', me); } } }, shouldRipple: function(e) { var me = this, disabled = me.getDisabled && me.getDisabled(), el = me.el, ripple = !disabled && me.getRipple(), target; if (ripple && e) { target = e.getTarget(me.noRippleSelector); if (target) { if ((el.dom === target) || el.contains(target)) { ripple = null; } } } return ripple; }, onRippleStart: function(e) { var ripple = this.shouldRipple(e); if (e.button === 0 && ripple) { this.el.ripple(e, ripple); } }, /** * @protected */ applyStyle: function(style, oldStyle) { // If we're doing something with data binding, say: // style: { // backgroundColor: 'rgba({r}, {g}, {b}, 1)' // } // The inner values will change, but the object won't, so force // a copy to be created here if necessary if (oldStyle && style === oldStyle && Ext.isObject(oldStyle)) { style = Ext.apply({}, style); } this.element.applyStyles(style); return null; }, //<debug> getStyle: function() { Ext.Error.raise("'style' is a write-only config. To query element styles use " + "the Ext.dom.Element API."); }, //</debug> updateRenderTo: function(newContainer) { this.render(newContainer); }, 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); } }, updateUi: function() { if (!this.isConfiguring) { this.syncUiCls(); } }, /** * @param width * @protected */ updateWidth: function(width) { var el = this.el; el.setWidth(width); el.toggleCls(this.widthedCls, width != null && width !== 'auto'); }, /** * @param height * @protected */ updateHeight: function(height) { var el = this.el; el.setHeight(height); el.toggleCls(this.heightedCls, height != null && height !== 'auto'); }, /** * @private */ isWidthed: function() { var width = this.getWidth(); return width != null && width !== 'auto'; }, /** * @private */ isHeighted: function() { var height = this.getHeight(); return height != null && height !== 'auto'; }, /** * Walks up the ownership hierarchy looking for an ancestor Component which matches * the passed simple selector. * * Example: * * var owningTabPanel = grid.up('tabpanel'); * * @param {String} selector (optional) The simple selector to test. * @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} 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 || selector.isWidget) { 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; }, updateLayout: Ext.emptyFn, // empty fn for modern/classic compat updateInstanceCls: function(instanceCls, oldInstanceCls) { var me = this, el = me.el, classClsList = me.classClsList, Array = Ext.Array, uiReferences = me.uiReferences, referenceName, referenceElement, i, ln, cls, uiCls; if (oldInstanceCls) { el.removeCls(oldInstanceCls); oldInstanceCls = Array.from(oldInstanceCls); for (i = 0, ln = oldInstanceCls.length; i < ln; i++) { cls = oldInstanceCls[i]; Array.remove(classClsList, cls); for (referenceName in uiReferences) { referenceElement = me[referenceName]; uiCls = uiReferences[referenceName]; referenceElement.removeCls(cls + '-' + uiCls); } } } if (instanceCls) { el.addCls(instanceCls); instanceCls = Array.from(instanceCls); // clone the classClsList so that the instanceCls is not shared on the prototype me.classClsList = classClsList.concat(instanceCls); for (i = 0, ln = instanceCls.length; i < ln; i++) { cls = instanceCls[i]; for (referenceName in uiReferences) { referenceElement = me[referenceName]; uiCls = uiReferences[referenceName]; referenceElement.addCls(cls + '-' + uiCls); } } } if (!me.isConfiguring) { me.syncUiCls(); } }, // getter for backward compatibility with < 6.5 where baseCls was a config getBaseCls: function() { return this.baseCls; }, //<debug> setBaseCls: function() { Ext.raise('baseCls cannot be reconfigured. It must be specified at class definition time.'); }, onClassExtended: function(Class, members) { if (members.config && members.config.baseCls) { Ext.raise('baseCls must be declared directly on the class body. Please move it ' + 'outside of the config block.'); } }, //</debug> //------------------------------------------------------------------------- privates: { _hideModes: { clip: 'CLIP', display: 'DISPLAY', offsets: 'OFFSETS', opacity: 'OPACITY', visibility: 'VISIBILITY' }, noRippleSelector: '.' + Ext.baseCSSPrefix + 'no-ripple', /** * Reduces instantiation time for a Widget by lazily instantiating Ext.Element * references the first time they are used. This optimization only works for elements * with no listeners specified. * * @param {String} name The name of the reference * @param {HTMLElement} domNode * @private */ addElementReferenceOnDemand: function(name, domNode) { if (this._elementListeners[name]) { // if the element was configured with listeners then we cannot add the // reference on demand because we need to make sure the element responds // immediately to any events, even if its reference is never accessed this.addElementReference(name, domNode); } else { // no listeners - element reference can be resolved on demand. // TODO: measure if this has any significant performance impact. Ext.Object.defineProperty(this, name, { get: function() { if (this.destroyed) { return null; } // remove the property that was defined using defineProperty because // addElementReference will set the property on the instance, - the // getter is not needed after the first access. delete this[name]; return this.addElementReference(name, domNode); }, configurable: true }); } }, /** * Adds an element reference to this Widget instance. * @param {String} name The name of the reference * @param {HTMLElement} domNode * @return {Ext.dom.Element} * @private */ addElementReference: function(name, domNode) { var me = this, referenceEl = me[name] = Ext.get(domNode), listeners = me._elementListeners[name], eventName, listener; referenceEl.skipGarbageCollection = true; referenceEl.component = me; if (listeners) { // TODO: These references will be needed when we use delegation to listen // for element events, but for now, we'll just attach the listeners directly // referenceEl.reference = name; // referenceEl.component = me; // referenceEl.listeners = listeners; // At this point "listeners" exists on the class prototype. We need to clone // it before poking the scope reference onto it, because it is used as the // options object by Observable and so can't be safely shared. // listeners = Ext.clone(listeners); // If the listener is specified as an object it needs to have the scope // option added to that object, for example: // // { // click: { // fn: 'onClick', // scope: this // } // } // for (eventName in listeners) { listener = listeners[eventName]; if (typeof listener === 'object') { listener.scope = me; } } // The outermost listeners object always needs the scope option. This covers // a listeners object with the following shape: // // { // click: 'onClick' // scope: this // } // listeners.scope = me; // do this *after* the above loop over listeners // Hopefully in the future we can stop calling on() here, and just use // event delegation to dispatch events to Widgets that have declared their // listeners in their template. // referenceEl.on(listeners); } return referenceEl; }, detachFromBody: function() { // See reattachToBody Ext.getDetachedBody().appendChild(this.element, true); this.isDetached = true; }, reattachToBody: function() { var detachedBody; if (this.isDetached) { detachedBody = Ext.getDetachedBody(); if (detachedBody.contains(this.element)) { Ext.getBody().appendChild(this.element, true); } } // See detachFromBody this.isDetached = false; }, /** * @private */ doAddListener: function(name, fn, scope, options, order, caller, manager) { var me = this, elementName = options && options.element, delegate = options && options.delegate, listeners, eventOptions, option; if (elementName) { //<debug> if (Ext.Array.indexOf(me.referenceList, elementName) === -1) { Ext.Logger.error( "Adding event listener with an invalid element reference of '" + elementName + "' for this component. Available values are: '" + me.referenceList.join("', '") + "'", me ); } //</debug> listeners = {}; listeners[name] = fn; if (scope) { listeners.scope = scope; } eventOptions = Ext.Element.prototype.$eventOptions; for (option in options) { if (eventOptions[option]) { listeners[option] = options[option]; } } me.mon(me[elementName], listeners); return; } else if (delegate) { me.mixins.componentDelegation.addDelegatedListener.call(me, name, fn, scope, options, order, caller, manager); return; } me.callParent([name, 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); }, filterLengthValue: function(value) { if (!value && value !== 0) { return null; } return value; }, hasHiddenContent: function() { return this.getHidden(); }, /** * Called for the first instance of this Widget to create an object that contains the * listener configs for all of the element references keyed by reference name. The * object is cached on the prototype and has the following shape: * * _elementListeners: { * element: { * click: 'onClick', * scope: this * }, * fooReference: { * tap: { * fn: someFunction, * delay: 100 * } * } * } * * The returned object is prototype chained to the _elementListeners object of its * superclass, and each key in the object is prototype chained to object with the * corresponding key in the superclass _elementListeners. This allows element * listeners to be inherited and overridden when subclassing widgets. * * This method is invoked with the prototype object as the scope * * @private */ initElementListeners: function(elementConfig) { var prototype = this, superPrototype = prototype.self.superclass, superElementListeners = superPrototype._elementListeners, reference = elementConfig.reference, children = elementConfig.children, elementListeners, listeners, superListeners, ln, i; if (prototype.hasOwnProperty('_elementListeners')) { elementListeners = prototype._elementListeners; } else { elementListeners = prototype._elementListeners = (superElementListeners ? Ext.Object.chain(superElementListeners) : {}); } if (reference) { listeners = elementConfig.listeners; if (listeners) { if (superElementListeners) { superListeners = superElementListeners[reference]; if (superListeners) { listeners = Ext.Object.chain(superListeners); Ext.apply(listeners, elementConfig.listeners); } } elementListeners[reference] = listeners; // null out the listeners on the elementConfig, since we are going to pass // it to Element.create(), and don't want "listeners" to be treated as an // attribute elementConfig.listeners = null; } } if (children) { for (i = 0, ln = children.length; i < ln; i++) { prototype.initElementListeners(children[i]); } } }, initId: function(config) { var me = this, defaultConfig = me.config, id = (config && config.id) || (defaultConfig && defaultConfig.id); if (id) { // setId() will normally be inherited from Identifiable, unless "id" is a // proper config, in which case it will be generated by the config system. me.setId(id); me.id = id; } else { // if no id configured, generate one (Identifiable) me.getId(); } }, measure: function(dimension) { return this.element.measure(dimension); }, /** * Recursively processes the element templates for this class and its superclasses, * ascending the hierarchy until it reaches a superclass whose element template * has already been processed. This method is invoked using the prototype as the scope. * * @private * @return {Object} */ processElementConfig: function() { var prototype = this, superPrototype = prototype.self.superclass, elementConfig; if (prototype.hasOwnProperty('_elementConfig')) { elementConfig = prototype._elementConfig; } else { // cache the elementConfig on the prototype, since we may end up here multiple // times if there are multiple subclasses elementConfig = prototype._elementConfig = prototype.getElementConfig(); if (superPrototype.isWidget) { // Before initializing element listeners we must process the element template // for our superclass so that we can chain our listeners to the superclass // listeners prototype.processElementConfig.call(superPrototype); } // initElementListeners needs to be called BEFORE passing the element config // along to Ext.Element.create(). This ensures that the listener meta data is // saved, and then the listeners objects are removed from the element config // so that they do not get added as attributes by create() prototype.initElementListeners(elementConfig); } return elementConfig; }, parseUi: function(ui, asString) { ui = Ext.String.splitWords(ui); if (asString) { ui = ui.join(' '); } return ui; }, addUi: function(ui) { this.setUi(this.doAddUi(ui, this.getUi())); }, doAddUi: function(ui, oldUi) { var me = this, newUi = null, i, u, len; if (ui) { ui = me.parseUi(ui); len = ui.length; oldUi = me.parseUi(oldUi); for (i = 0; i < len; i++) { u = ui[i]; if (Ext.Array.indexOf(oldUi, u) === -1) { oldUi.push(u); } } newUi = oldUi.join(' '); } return newUi; }, removeUi: function(ui) { this.setUi(this.doRemoveUi(ui, this.getUi())); }, doRemoveUi: function(ui, oldUi) { var me = this, newUi = null, i, u, index, len; if (ui) { ui = me.parseUi(ui); len = ui.length; oldUi = me.parseUi(oldUi); for (i = 0; i < len; i++) { u = ui[i]; index = Ext.Array.indexOf(oldUi, u); if (index !== -1) { oldUi.splice(index, 1); } } newUi = oldUi.join(' '); } return newUi; }, /** * Initializes a "uiReference". Ui rerefences are reference elements that have * classCls and ui info in their CSS class names. They can be used by setting * uiCls in the template, or by invoking this method to setup the ui reference * after element/template initialization (Toolable uses this for its dock wrapper) * @param {String} referenceName * @param {String} uiCls * @param {Boolean} [isInstance=false] pass `false` if this is not an instance-level * reference * @private */ initUiReference: function(referenceName, uiCls, isInstance) { var me = this, referenceElement = me[referenceName], baseCls = me.baseCls, classClsList = me.classClsList, cls = [], i, n; isInstance = (isInstance !== false); if (isInstance) { // clone so we don't modify the prototype uiReferences if (!me.hasOwnProperty('uiReferences')) { me.uiReferences = Ext.clone(me.uiReferences); } me.uiReferences[referenceName] = uiCls; } uiCls = '-' + uiCls; if (baseCls && (baseCls !== me.classCls)) { cls.push(baseCls + uiCls); } if (classClsList) { for (i = 0, n = classClsList.length; i < n; i++) { cls.push(classClsList[i] + uiCls); } } referenceElement.addCls(cls); if (isInstance && !me.isConfiguring) { me.syncUiCls(); } }, syncUiCls: function(refs) { var me = this, ui = me.getUi(), currentUiCls = me.currentUiCls || (me.currentUiCls = {}), baseCls = me.baseCls, uiReferences = refs || me.uiReferences, classClsList = me.classClsList, classClsListLen = classClsList ? classClsList.length : 0, uiCls, uiLen, refName, refEl, cls, suffix, uiSuffix, i, j; if (ui) { ui = me.parseUi(ui); uiLen = ui.length; } for (refName in uiReferences) { refEl = me[refName]; uiCls = []; if (refEl) { cls = currentUiCls[refName]; if (cls) { refEl.removeCls(cls); } if (ui) { suffix = uiReferences[refName]; suffix = suffix ? ('-' + suffix) : ''; for (i = 0; i < uiLen; i++) { uiSuffix = '-' + ui[i] + suffix; if (baseCls && (baseCls !== me.classCls)) { uiCls.push(baseCls + uiSuffix); } if (classClsList) { for (j = 0; j < classClsListLen; j++) { uiCls.push(classClsList[j] + uiSuffix); } } } refEl.addCls(uiCls); currentUiCls[refName] = uiCls; } } } }, applyHideMode: function(mode) { return mode || 'display'; }, updateHideMode: function(mode) { var me = this, el = me.el, shouldToggle = me.getHidden(); //<debug> if (!me._hideModes[mode]) { Ext.raise('Invalid hideMode: "' + mode + '" (must be one of: "' + Object.keys(me._hideModes).join('", "') + '")'); } //</debug> if (shouldToggle) { el.show(); } me.renderElement.setVisibilityMode(Ext.Element[me._hideModes[mode]]); if (shouldToggle) { el.hide(); } }, updateUserCls: function(newCls, oldCls) { this.element.replaceCls(oldCls, newCls); } }}, function(Widget) { var prototype = Widget.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; /** * @member Ext * @method updateWidget * @inheritdoc Ext.Factory#update * @since 6.5.1 */ Ext.updateWidget = Ext.Factory.widget.update;});