/** * A Container has all of the abilities of {@link Ext.Component Component}, but lets you nest other Components inside * it. Applications are made up of lots of components, usually nested inside one another. Containers allow you to * render and arrange child Components inside them. Most apps have a single top-level Container called a Viewport, * which takes up the entire screen. Inside of this are child components, for example in a mail app the Viewport * Container's two children might be a message List and an email preview pane. * * Containers give the following extra functionality: * * - Adding child Components at instantiation and run time * - Removing child Components * - Specifying a [Layout](#!/guide/layouts) * * Layouts determine how the child Components should be laid out on the screen. In our mail app example we'd use an * HBox layout so that we can pin the email list to the left hand edge of the screen and allow the preview pane to * occupy the rest. There are several layouts in Sencha Touch 2, each of which help you achieve your desired * application structure, further explained in the [Layout guide](#!/guide/layouts). * * ## Adding Components to Containers * * As we mentioned above, Containers are special Components that can have child Components arranged by a Layout. One of * the code samples above showed how to create a Panel with 2 child Panels already defined inside it but it's easy to * do this at run time too: * * @example miniphone * //this is the Panel we'll be adding below * var aboutPanel = Ext.create('Ext.Panel', { * html: 'About this app' * }); * * //this is the Panel we'll be adding to * var mainPanel = Ext.create('Ext.Panel', { * fullscreen: true, * * layout: 'hbox', * defaults: { * flex: 1 * }, * * items: { * html: 'First Panel', * style: 'background-color: #5E99CC;' * } * }); * * //now we add the first panel inside the second * mainPanel.add(aboutPanel); * * Here we created three Panels in total. First we made the aboutPanel, which we might use to tell the user a little * about the app. Then we create one called mainPanel, which already contains a third Panel in its * {@link Ext.Container#cfg-items items} configuration, with some dummy text ("First Panel"). Finally, we add the first * panel to the second by calling the {@link Ext.Container#method-add add} method on `mainPanel`. * * In this case we gave our mainPanel another hbox layout, but we also introduced some * {@link Ext.Container#defaults defaults}. These are applied to every item in the Panel, so in this case every child * inside `mainPanel` will be given a `flex: 1` configuration. The effect of this is that when we first render the screen * only a single child is present inside `mainPanel`, so that child takes up the full width available to it. Once the * `mainPanel.add` line is called though, the `aboutPanel` is rendered inside of it and also given a `flex` of 1, which will * cause it and the first panel to both receive half the full width of the `mainPanel`. * * Likewise, it's easy to remove items from a Container: * * mainPanel.remove(aboutPanel); * * After this line is run everything is back to how it was, with the first child panel once again taking up the full * width inside `mainPanel`. * * ## Further Reading * * See the [Component & Container Guide](#!/guide/components) for more information, and check out the * {@link Ext.Container} class docs also. * * @aside guide components * @aside guide layouts */ Ext.define('Ext.Container', { extend: 'Ext.Component', alternateClassName: 'Ext.lib.Container', requires: [ 'Ext.layout.*', 'Ext.ItemCollection', 'Ext.behavior.Scrollable', 'Ext.Mask' ], xtype: 'container', /** * @event add * Fires whenever item added to the Container. * @param {Ext.Container} this The Container instance. * @param {Object} item The item added to the Container. * @param {Number} index The index of the item within the Container. */ /** * @event remove * Fires whenever item removed from the Container. * @param {Ext.Container} this The Container instance. * @param {Object} item The item removed from the Container. * @param {Number} index The index of the item that was removed. */ /** * @event move * Fires whenever item moved within the Container. * @param {Ext.Container} this The Container instance. * @param {Object} item The item moved within the Container. * @param {Number} toIndex The new index of the item. * @param {Number} fromIndex The old index of the item. */ /** * @private * @event renderedchange * Fires whenever an item is rendered into a container or derendered * from a Container. * @param {Ext.Container} this The Container instance. * @param {Object} item The item in the Container. * @param {Boolean} rendered The current rendered status of the item. */ /** * @event activate * Fires whenever item within the Container is activated. * @param {Object} newActiveItem The new active item within the container. * @param {Ext.Container} this The Container instance. * @param {Object} oldActiveItem The old active item within the container. */ /** * @event deactivate * Fires whenever item within the Container is deactivated. * @param {Object} oldActiveItem The old active item within the container. * @param {Ext.Container} this The Container instance. * @param {Object} newActiveItem The new active item within the container. */ eventedConfig: { /** * @cfg {Object/String/Number} activeItem The item from the {@link #cfg-items} collection that will be active first. This is * usually only meaningful in a {@link Ext.layout.Card card layout}, where only one item can be active at a * time. If passes a string, it will be assumed to be a {@link Ext.ComponentQuery} selector. * @accessor * @evented */ activeItem: 0, /** * @cfg {Boolean/String/Object} scrollable * Configuration options to make this Container scrollable. Acceptable values are: * * - `'horizontal'`, `'vertical'`, `'both'` to enabling scrolling for that direction. * - `true`/`false` to explicitly enable/disable scrolling. * * Alternatively, you can give it an object which is then passed to the scroller instance: * * scrollable: { * direction: 'vertical', * directionLock: true * } * * Please look at the {@link Ext.scroll.Scroller} documentation for more example on how to use this. * @accessor * @evented */ scrollable: null }, config: { /** * @cfg {String/Object/Boolean} cardSwitchAnimation * Animation to be used during transitions of cards. * @removed 2.0.0 Please use {@link Ext.layout.Card#animation} instead */ /** * @cfg {Object/String} layout Configuration for this Container's layout. Example: * * Ext.create('Ext.Container', { * layout: { * type: 'hbox', * align: 'middle' * }, * items: [ * { * xtype: 'panel', * flex: 1, * style: 'background-color: red;' * }, * { * xtype: 'panel', * flex: 2, * style: 'background-color: green' * } * ] * }); * * See the [Layouts Guide](#!/guide/layouts) for more information. * * @accessor */ layout: null, /** * @cfg {Object} control Enables you to easily control Components inside this Container by listening to their * events and taking some action. For example, if we had a container with a nested Disable button, and we * wanted to hide the Container when the Disable button is tapped, we could do this: * * Ext.create('Ext.Container', { * control: { * 'button[text=Disable]': { * tap: 'hideMe' * } * }, * * hideMe: function () { * this.hide(); * } * }); * * We used a {@link Ext.ComponentQuery} selector to listen to the {@link Ext.Button#tap tap} event on any * {@link Ext.Button button} anywhere inside the Container that has the {@link Ext.Button#text text} 'Disable'. * Whenever a Component matching that selector fires the `tap` event our `hideMe` function is called. `hideMe` is * called with scope: `this` (e.g. `this` is the Container instance). * */ control: {}, /** * @cfg {Object} defaults A set of default configurations to apply to all child Components in this Container. * It's often useful to specify defaults when creating more than one items with similar configurations. For * example here we can specify that each child is a panel and avoid repeating the xtype declaration for each * one: * * Ext.create('Ext.Container', { * defaults: { * xtype: 'panel' * }, * items: [ * { * html: 'Panel 1' * }, * { * html: 'Panel 2' * } * ] * }); * * @accessor */ defaults: null, /** * @cfg {Array/Object} items The child items to add to this Container. This is usually an array of Component * configurations or instances, for example: * * Ext.create('Ext.Container', { * items: [ * { * xtype: 'panel', * html: 'This is an item' * } * ] * }); * @accessor */ items: null, /** * @cfg {Boolean} autoDestroy If `true`, child items will be destroyed as soon as they are {@link #method-remove removed} * from this container. * @accessor */ autoDestroy: true, /** @cfg {String} defaultType * The default {@link Ext.Component xtype} of child Components to create in this Container when a child item * is specified as a raw configuration object, rather than as an instantiated Component. * @accessor */ defaultType: null, //@private useBodyElement: null, /** * @cfg {Boolean/Object/Ext.Mask/Ext.LoadMask} masked * A configuration to allow you to mask this container. * You can optionally pass an object block with and xtype of `loadmask`, and an optional `message` value to * display a loading mask. Please refer to the {@link Ext.LoadMask} component to see other configurations. * * masked: { * xtype: 'loadmask', * message: 'My message' * } * * Alternatively, you can just call the setter at any time with `true`/`false` to show/hide the mask: * * setMasked(true); //show the mask * setMasked(false); //hides the mask * * There are also two convenient methods, {@link #method-mask} and {@link #unmask}, to allow you to mask and unmask * this container at any time. * * Remember, the {@link Ext.Viewport} is always a container, so if you want to mask your whole application at anytime, * can call: * * Ext.Viewport.setMasked({ * xtype: 'loadmask', * message: 'Hello' * }); * * @accessor */ masked: null, /** * @cfg {Boolean} modal `true` to make this Container modal. This will create a mask underneath the Container * that covers its parent and does not allow the user to interact with any other Components until this * Container is dismissed. * @accessor */ modal: null, /** * @cfg {Boolean} hideOnMaskTap When using a {@link #modal} Component, setting this to `true` will hide the modal * mask and the Container when the mask is tapped on. * @accessor */ hideOnMaskTap: null }, isContainer: true, constructor: function(config) { var me = this; me._items = me.items = new Ext.ItemCollection(); me.innerItems = []; me.onItemAdd = me.onFirstItemAdd; me.callParent(arguments); }, getElementConfig: function() { return { reference: 'element', classList: ['x-container', 'x-unsized'], children: [{ reference: 'innerElement', className: 'x-inner' }] }; }, /** * Changes the {@link #masked} configuration when its setter is called, which will convert the value * into a proper object/instance of {@link Ext.Mask}/{@link Ext.LoadMask}. If a mask already exists, * it will use that instead. * @param {Boolean/Object/Ext.Mask/Ext.LoadMask} masked * @return {Object} */ applyMasked: function(masked) { var isVisible = true, currentMask; if (masked === false) { masked = true; isVisible = false; } currentMask = Ext.factory(masked, Ext.Mask, this.getMasked()); if (currentMask) { this.add(currentMask); currentMask.setHidden(!isVisible); } return currentMask; }, /** * Convenience method which calls {@link #setMasked} with a value of `true` (to show the mask). For additional * functionality, call the {@link #setMasked} function direction (See the {@link #masked} configuration documentation * for more information). */ mask: function(mask) { this.setMasked(mask || true); }, /** * Convenience method which calls {@link #setMasked} with a value of false (to hide the mask). For additional * functionality, call the {@link #setMasked} function direction (See the {@link #masked} configuration documentation * for more information). */ unmask: function() { this.setMasked(false); }, setParent: function(container) { this.callSuper(arguments); if (container) { var modal = this.getModal(); if (modal) { container.insertBefore(modal, this); modal.setZIndex(this.getZIndex() - 1); } } }, applyModal: function(modal, currentModal) { var isVisible = true; if (modal === false) { modal = true; isVisible = false; } currentModal = Ext.factory(modal, Ext.Mask, currentModal); if (currentModal) { currentModal.setVisibility(isVisible); } return currentModal; }, updateModal: function(modal) { var container = this.getParent(); if (container) { if (modal) { container.insertBefore(modal, this); modal.setZIndex(this.getZIndex() - 1); } else { container.remove(modal); } } }, updateHideOnMaskTap : function(hide) { var mask = this.getModal(); if (mask) { mask[hide ? 'on' : 'un'].call(mask, 'tap', 'hide', this); } }, updateZIndex: function(zIndex) { var modal = this.getModal(); this.callParent(arguments); if (modal) { modal.setZIndex(zIndex - 1); } }, updateBaseCls: function(newBaseCls, oldBaseCls) { var me = this, ui = me.getUi(); if (newBaseCls) { this.element.addCls(newBaseCls); this.innerElement.addCls(newBaseCls, null, 'inner'); if (ui) { this.element.addCls(newBaseCls, null, ui); } } if (oldBaseCls) { this.element.removeCls(oldBaseCls); this.innerElement.removeCls(newBaseCls, null, 'inner'); if (ui) { this.element.removeCls(oldBaseCls, null, ui); } } }, updateUseBodyElement: function(useBodyElement) { if (useBodyElement) { this.link('bodyElement', this.innerElement.wrap({ cls: 'x-body' })); } }, applyItems: function(items, collection) { if (items) { var me = this; me.getDefaultType(); me.getDefaults(); if (me.initialized && collection.length > 0) { me.removeAll(); } me.add(items); //Don't need to call setActiveItem when Container is first initialized if (me.initialized) { var activeItem = me.initialConfig.activeItem || me.config.activeItem || 0; me.setActiveItem(activeItem); } } }, /** * @private */ applyControl: function(selectors) { var selector, key, listener, listeners; for (selector in selectors) { listeners = selectors[selector]; for (key in listeners) { listener = listeners[key]; if (Ext.isObject(listener)) { listener.delegate = selector; } } listeners.delegate = selector; this.addListener(listeners); } return selectors; }, /** * Initialize layout and event listeners the very first time an item is added * @private */ onFirstItemAdd: function() { delete this.onItemAdd; if (this.innerHtmlElement && !this.getHtml()) { this.innerHtmlElement.destroy(); delete this.innerHtmlElement; } this.on('innerstatechange', 'onItemInnerStateChange', this, { delegate: '> component' }); return this.onItemAdd.apply(this, arguments); }, //<debug error> updateLayout: function(newLayout, oldLayout) { if (oldLayout && oldLayout.isLayout) { Ext.Logger.error('Replacing a layout after one has already been initialized is not currently supported.'); } }, //</debug> getLayout: function() { var layout = this.layout; if (!layout) { layout = this.link('_layout', this.link('layout', Ext.factory(this._layout || 'default', Ext.layout.Default, null, 'layout'))); layout.setContainer(this); } return layout; }, updateDefaultType: function(defaultType) { // Cache the direct reference to the default item class here for performance this.defaultItemClass = Ext.ClassManager.getByAlias('widget.' + defaultType); //<debug error> if (!this.defaultItemClass) { Ext.Logger.error("Invalid defaultType of: '" + defaultType + "', must be a valid component xtype"); } //</debug> }, applyDefaults: function(defaults) { if (defaults) { this.factoryItem = this.factoryItemWithDefaults; return defaults; } }, factoryItem: function(item) { //<debug error> if (!item) { Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " + "or an existing component instance"); } //</debug> return Ext.factory(item, this.defaultItemClass); }, factoryItemWithDefaults: function(item) { //<debug error> if (!item) { Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " + "or an existing component instance"); } //</debug> var me = this, defaults = me.getDefaults(), instance; if (!defaults) { return Ext.factory(item, me.defaultItemClass); } // Existing instance if (item.isComponent) { instance = item; // Apply defaults only if this is not already an item of this container if (defaults && item.isInnerItem() && !me.has(instance)) { instance.setConfig(defaults, true); } } // Config object else { if (defaults && !item.ignoreDefaults) { // Note: // - defaults is only applied to inner items // - we merge the given config together with defaults into a new object so that the original object stays intact if (!( item.hasOwnProperty('left') && item.hasOwnProperty('right') && item.hasOwnProperty('top') && item.hasOwnProperty('bottom') && item.hasOwnProperty('docked') && item.hasOwnProperty('centered') )) { item = Ext.mergeIf({}, item, defaults); } } instance = Ext.factory(item, me.defaultItemClass); } return instance; }, /** * Adds one or more Components to this Container. Example: * * var myPanel = Ext.create('Ext.Panel', { * html: 'This will be added to a Container' * }); * * myContainer.add([myPanel]); * * @param {Object/Object[]/Ext.Component/Ext.Component[]} newItems The new items to add to the Container. * @return {Ext.Component} The last item added to the Container from the `newItems` array. */ add: function(newItems) { var me = this, i, ln, item, newActiveItem; if (Ext.isArray(newItems)) { for (i = 0, ln = newItems.length; i < ln; i++) { item = me.factoryItem(newItems[i]); this.doAdd(item); if (!newActiveItem && !this.getActiveItem() && this.innerItems.length > 0 && item.isInnerItem()) { newActiveItem = item; } } } else { item = me.factoryItem(newItems); this.doAdd(item); if (!newActiveItem && !this.getActiveItem() && this.innerItems.length > 0 && item.isInnerItem()) { newActiveItem = item; } } if (newActiveItem) { this.setActiveItem(newActiveItem); } return item; }, /** * @private * @param item */ doAdd: function(item) { var me = this, items = me.getItems(), index; if (!items.has(item)) { index = items.length; items.add(item); if (item.isInnerItem()) { me.insertInner(item); } item.setParent(me); me.onItemAdd(item, index); } }, /** * Removes an item from this Container, optionally destroying it. * @param {Object} item The item to remove. * @param {Boolean} [destroy] Calls the Component's {@link Ext.Component#method-destroy destroy} * method if `true`. * @return {Ext.Component} this */ remove: function(item, destroy) { var me = this, index = me.indexOf(item), innerItems = me.getInnerItems(); if (destroy === undefined) { destroy = me.getAutoDestroy(); } if (index !== -1) { if (!me.removingAll && innerItems.length > 1 && item === me.getActiveItem()) { me.on({ activeitemchange: 'doRemove', scope: me, single: true, order: 'after', args: [item, index, destroy] }); me.doResetActiveItem(innerItems.indexOf(item)); } else { me.doRemove(item, index, destroy); if (innerItems.length === 0) { me.setActiveItem(null); } } } return me; }, doResetActiveItem: function(innerIndex) { if (innerIndex === 0) { this.setActiveItem(1); } else { this.setActiveItem(0); } }, doRemove: function(item, index, destroy) { var me = this; me.items.remove(item); if (item.isInnerItem()) { me.removeInner(item); } me.onItemRemove(item, index, destroy); item.setParent(null); if (destroy) { item.destroy(); } }, /** * Removes all items currently in the Container, optionally destroying them all. * @param {Boolean} destroy If `true`, {@link Ext.Component#method-destroy destroys} * each removed Component. * @param {Boolean} everything If `true`, completely remove all items including * docked / centered and floating items. * @return {Ext.Component} this */ removeAll: function(destroy, everything) { var items = this.items, ln = items.length, i = 0, item; if (typeof destroy != 'boolean') { destroy = this.getAutoDestroy(); } everything = Boolean(everything); // removingAll flag is used so we don't unnecessarily change activeItem while removing all items. this.removingAll = true; for (; i < ln; i++) { item = items.getAt(i); if (item && (everything || item.isInnerItem())) { this.doRemove(item, i, destroy); i--; ln--; } } this.setActiveItem(null); this.removingAll = false; return this; }, /** * Returns the Component for a given index in the Container's {@link #property-items}. * @param {Number} index The index of the Component to return. * @return {Ext.Component} The item at the specified `index`, if found. */ getAt: function(index) { return this.items.getAt(index); }, getInnerAt: function(index) { return this.innerItems[index]; }, /** * Removes the Component at the specified index: * * myContainer.removeAt(0); // removes the first item * * @param {Number} index The index of the Component to remove. */ removeAt: function(index) { var item = this.getAt(index); if (item) { this.remove(item); } return this; }, /** * Removes an inner Component at the specified index: * * myContainer.removeInnerAt(0); // removes the first item of the innerItems property * * @param {Number} index The index of the Component to remove. */ removeInnerAt: function(index) { var item = this.getInnerItems()[index]; if (item) { this.remove(item); } return this; }, /** * @private */ has: function(item) { return this.getItems().indexOf(item) != -1; }, /** * @private */ hasInnerItem: function(item) { return this.innerItems.indexOf(item) != -1; }, /** * @private */ indexOf: function(item) { return this.getItems().indexOf(item); }, innerIndexOf: function(item) { return this.innerItems.indexOf(item); }, /** * @private * @param item * @param index */ insertInner: function(item, index) { var items = this.getItems().items, innerItems = this.innerItems, currentInnerIndex = innerItems.indexOf(item), newInnerIndex = -1, nextSibling; if (currentInnerIndex !== -1) { innerItems.splice(currentInnerIndex, 1); } if (typeof index == 'number') { do { nextSibling = items[++index]; } while (nextSibling && !nextSibling.isInnerItem()); if (nextSibling) { newInnerIndex = innerItems.indexOf(nextSibling); innerItems.splice(newInnerIndex, 0, item); } } if (newInnerIndex === -1) { innerItems.push(item); newInnerIndex = innerItems.length - 1; } if (currentInnerIndex !== -1) { this.onInnerItemMove(item, newInnerIndex, currentInnerIndex); } return this; }, onInnerItemMove: Ext.emptyFn, /** * @private * @param item */ removeInner: function(item) { Ext.Array.remove(this.innerItems, item); return this; }, /** * Adds a child Component at the given index. For example, here's how we can add a new item, making it the first * child Component of this Container: * * myContainer.insert(0, {xtype: 'panel', html: 'new item'}); * * @param {Number} index The index to insert the Component at. * @param {Object} item The Component to insert. */ insert: function(index, item) { var me = this, i; //<debug error> if (typeof index != 'number') { Ext.Logger.error("Invalid index of '" + index + "', must be a valid number"); } //</debug> if (Ext.isArray(item)) { for (i = item.length - 1; i >= 0; i--) { me.insert(index, item[i]); } return me; } item = this.factoryItem(item); this.doInsert(index, item); return item; }, /** * @private * @param index * @param item */ doInsert: function(index, item) { var me = this, items = me.items, itemsLength = items.length, currentIndex, isInnerItem; isInnerItem = item.isInnerItem(); if (index > itemsLength) { index = itemsLength; } if (items[index - 1] === item) { return me; } currentIndex = me.indexOf(item); if (currentIndex !== -1) { if (currentIndex < index) { index -= 1; } items.removeAt(currentIndex); } items.insert(index, item); if (currentIndex === -1) { item.setParent(me); } if (isInnerItem) { me.insertInner(item, index); } if (currentIndex !== -1) { me.onItemMove(item, index, currentIndex); } else { me.onItemAdd(item, index); } }, /** * @private */ insertFirst: function(item) { return this.insert(0, item); }, /** * @private */ insertLast: function(item) { return this.insert(this.getItems().length, item); }, /** * @private */ insertBefore: function(item, relativeToItem) { var index = this.indexOf(relativeToItem); if (index !== -1) { this.insert(index, item); } return this; }, /** * @private */ insertAfter: function(item, relativeToItem) { var index = this.indexOf(relativeToItem); if (index !== -1) { this.insert(index + 1, item); } return this; }, /** * @private */ onItemAdd: function(item, index) { this.doItemLayoutAdd(item, index); if (this.initialized) { this.fireEvent('add', this, item, index); } }, doItemLayoutAdd: function(item, index) { var layout = this.getLayout(); if (this.isRendered() && item.setRendered(true)) { item.fireAction('renderedchange', [this, item, true], 'onItemAdd', layout, { args: [item, index] }); } else { layout.onItemAdd(item, index); } }, /** * @private */ onItemRemove: function(item, index) { this.doItemLayoutRemove(item, index); this.fireEvent('remove', this, item, index); }, doItemLayoutRemove: function(item, index) { var layout = this.getLayout(); if (this.isRendered() && item.setRendered(false)) { item.fireAction('renderedchange', [this, item, false], 'onItemRemove', layout, { args: [item, index, undefined] }); } else { layout.onItemRemove(item, index); } }, /** * @private */ onItemMove: function(item, toIndex, fromIndex) { if (item.isDocked()) { item.setDocked(null); } this.doItemLayoutMove(item, toIndex, fromIndex); this.fireEvent('move', this, item, toIndex, fromIndex); }, doItemLayoutMove: function(item, toIndex, fromIndex) { this.getLayout().onItemMove(item, toIndex, fromIndex); }, onItemInnerStateChange: function(item, isInner) { var layout = this.getLayout(); if (isInner) { this.insertInner(item, this.items.indexOf(item)); } else { this.removeInner(item); } layout.onItemInnerStateChange.apply(layout, arguments); }, /** * Returns all inner {@link #property-items} of this container. `inner` means that the item is not `docked` or * `floating`. * @return {Array} The inner items of this container. */ getInnerItems: function() { return this.innerItems; }, /** * Returns all the {@link Ext.Component#docked} items in this container. * @return {Array} The docked items of this container. */ getDockedItems: function() { var items = this.getItems().items, dockedItems = [], ln = items.length, item, i; for (i = 0; i < ln; i++) { item = items[i]; if (item.isDocked()) { dockedItems.push(item); } } return dockedItems; }, /** * @private */ applyActiveItem: function(activeItem, currentActiveItem) { var innerItems = this.getInnerItems(); // Make sure the items are already initialized this.getItems(); // No items left to be active, reset back to 0 on falsy changes if (!activeItem && innerItems.length === 0) { return 0; } else if (typeof activeItem == 'number') { activeItem = Math.max(0, Math.min(activeItem, innerItems.length - 1)); activeItem = innerItems[activeItem]; if (activeItem) { return activeItem; } else if (currentActiveItem) { return null; } } else if (activeItem) { var item; //ComponentQuery selector? if (typeof activeItem == 'string') { item = this.child(activeItem); activeItem = { xtype : activeItem }; } if (!item || !item.isComponent) { item = this.factoryItem(activeItem); } this.pendingActiveItem = item; //<debug error> if (!item.isInnerItem()) { Ext.Logger.error("Setting activeItem to be a non-inner item"); } //</debug> if (!this.has(item)) { this.add(item); } return item; } }, /** * Animates to the supplied `activeItem` with a specified animation. Currently this only works * with a Card layout. This passed animation will override any default animations on the * container, for a single card switch. The animation will be destroyed when complete. * @param {Object/Number} activeItem The item or item index to make active. * @param {Object/Ext.fx.layout.Card} animation Card animation configuration or instance. */ animateActiveItem: function(activeItem, animation) { var layout = this.getLayout(), defaultAnimation; if (this.activeItemAnimation) { this.activeItemAnimation.destroy(); } this.activeItemAnimation = animation = new Ext.fx.layout.Card(animation); if (animation && layout.isCard) { animation.setLayout(layout); defaultAnimation = layout.getAnimation(); if (defaultAnimation) { defaultAnimation.disable(); } animation.on('animationend', function() { if (defaultAnimation) { defaultAnimation.enable(); } animation.destroy(); }, this); } return this.setActiveItem(activeItem); }, /** * @private */ doSetActiveItem: function(newActiveItem, oldActiveItem) { delete this.pendingActiveItem; if (oldActiveItem) { oldActiveItem.fireEvent('deactivate', oldActiveItem, this, newActiveItem); } if (newActiveItem) { newActiveItem.fireEvent('activate', newActiveItem, this, oldActiveItem); } }, doSetHidden: function(hidden) { var modal = this.getModal(); if (modal) { modal.setHidden(hidden); } this.callSuper(arguments); }, /** * @private */ setRendered: function(rendered) { if (this.callParent(arguments)) { var items = this.items.items, i, ln; for (i = 0,ln = items.length; i < ln; i++) { items[i].setRendered(rendered); } return true; } return false; }, /** * @private */ getScrollableBehavior: function() { var behavior = this.scrollableBehavior; if (!behavior) { behavior = this.scrollableBehavior = new Ext.behavior.Scrollable(this); } return behavior; }, /** * @private */ applyScrollable: function(config) { if (typeof config === 'boolean') { //<debug warn> if (config === false && !(this.getHeight() !== null || this.heightLayoutSized || (this.getTop() !== null && this.getBottom() !== null))) { Ext.Logger.warn("This container is set to scrollable: false but has no specified height. " + "You may need to set the container to scrollable: null or provide a height.", this); } //</debug> this.getScrollableBehavior().setConfig({disabled: !config}); } else if (config && !config.isObservable) { this.getScrollableBehavior().setConfig(config); } return config; }, doSetScrollable: function() { // Used for plugins when they need to reinitialize scroller listeners }, /** * Returns an the scrollable instance for this container, which is a {@link Ext.scroll.View} class. * * Please checkout the documentation for {@link Ext.scroll.View}, {@link Ext.scroll.View#getScroller} * and {@link Ext.scroll.Scroller} for more information. * @return {Ext.scroll.View} The scroll view. */ getScrollable: function() { return this.getScrollableBehavior().getScrollView(); }, // Used by ComponentQuery to retrieve all of the items // which can potentially be considered a child of this Container. // This should be overridden by components which have child items // that are not contained in items. For example `dockedItems`, `menu`, etc // @private getRefItems: function(deep) { var items = this.getItems().items.slice(), ln = items.length, i, item; if (deep) { for (i = 0; i < ln; i++) { item = items[i]; if (item.getRefItems) { items = items.concat(item.getRefItems(true)); } } } return items; }, /** * Examines this container's `{@link #property-items}` property * and gets a direct child component of this container. * @param {String/Number} component This parameter may be any of the following: * * - {String} : representing the `itemId` * or `{@link Ext.Component#getId id}` of the child component. * - {Number} : representing the position of the child component * within the `{@link #property-items}` property. * * For additional information see {@link Ext.util.MixedCollection#get}. * @return {Ext.Component} The component (if found). */ getComponent: function(component) { if (Ext.isObject(component)) { component = component.getItemId(); } return this.getItems().get(component); }, /** * Finds a docked item of this container using a reference, `id `or an `index` of its location * in {@link #getDockedItems}. * @param {String/Number} component The `id` or `index` of the component to find. * @return {Ext.Component/Boolean} The docked component, if found. */ getDockedComponent: function(component) { if (Ext.isObject(component)) { component = component.getItemId(); } var dockedItems = this.getDockedItems(), ln = dockedItems.length, item, i; if (Ext.isNumber(component)) { return dockedItems[component]; } for (i = 0; i < ln; i++) { item = dockedItems[i]; if (item.id == component) { return item; } } return false; }, /** * Retrieves all descendant components which match the passed selector. * Executes an Ext.ComponentQuery.query using this container as its root. * @param {String} selector Selector complying to an Ext.ComponentQuery selector. * @return {Array} Ext.Component's which matched the selector. */ query: function(selector) { return Ext.ComponentQuery.query(selector, this); }, /** * Retrieves the first direct child of this container which matches the passed selector. * The passed in selector must comply with an {@link Ext.ComponentQuery} selector. * @param {String} selector An {@link Ext.ComponentQuery} selector. * @return {Ext.Component} */ child: function(selector) { return this.query('> ' + selector)[0] || null; }, /** * Retrieves the first descendant of this container which matches the passed selector. * The passed in selector must comply with an {@link Ext.ComponentQuery} selector. * @param {String} selector An {@link Ext.ComponentQuery} selector. * @return {Ext.Component} */ down: function(selector) { return this.query(selector)[0] || null; }, //<deprecated product=touch since=2.0> onClassExtended: function(Class, members) { if ('onAdd' in members || 'onRemove' in members) { throw new Error("["+Class.$className+"] 'onAdd()' and 'onRemove()' methods " + "no longer exist in Ext.Container, please use 'onItemAdd()' " + "and 'onItemRemove()' instead }"); } }, //</deprecated> destroy: function() { var me = this, modal = me.getModal(); if (modal) { modal.destroy(); } me.removeAll(true, true); me.unlink('_scrollable'); Ext.destroy(me.items); me.callSuper(); } }, function() { this.addMember('defaultItemClass', this); //<deprecated product=touch since=2.0> /** * @method addAll * Adds an array of Components to this Container. * @deprecated 2.0.0 Please use {@link #method-add} instead. * @param {Array} items The array of items to add to this container. * @return {Array} The array of items after they have been added. */ Ext.deprecateClassMethod(this, 'addAll', 'add'); /** * @method removeDocked * Removes a docked item from this Container. * @deprecated 2.0.0 Please use {@link #method-remove} instead. * @param {Object} item The item to remove. * @param {Boolean} destroy Calls the Component's {@link Ext.Component#method-destroy destroy} * method if `true`. * @return {Ext.Component} this */ Ext.deprecateClassMethod(this, 'removeDocked', 'remove'); /** * @member Ext.Container * @property items * @type Ext.util.MixedCollection * The set of all items in this Container. * @deprecated 2.0.0 Please use {@link #getItems} method instead. */ this.override({ constructor: function(config) { config = config || {}; var dockedItems = config.dockedItems, i, ln, item; /** * @cfg {Boolean/String/Object} scroll * @inheritdoc Ext.Container#scrollable * @deprecated 2.0.0 Please use the {@link #scrollable} configuration. */ if (config.scroll) { //<debug warn> Ext.Logger.deprecate("'scroll' config is deprecated, please use 'scrollable' instead.", this); //</debug> config.scrollable = config.scroll; delete config.scroll; } this.callOverridden(arguments); if (dockedItems) { //<debug warn> Ext.Logger.deprecate("'dockedItems' config is deprecated, please add all docked items inside the 'items' config with a 'docked' property indicating the docking position instead, i.e { /*...*/ docked: 'top' /*...*/ }"); //</debug> dockedItems = Ext.Array.from(dockedItems); for (i = 0,ln = dockedItems.length; i < ln; i++) { item = dockedItems[i]; if ('dock' in item) { //<debug warn> Ext.Logger.deprecate("'dock' config for docked items is deprecated, please use 'docked' instead"); //</debug> item.docked = item.dock; } } this.add(dockedItems); } }, add: function() { var args = arguments; if (args.length > 1) { if (typeof args[0] == 'number') { //<debug warn> Ext.Logger.deprecate("add(index, item) method signature is deprecated, please use insert(index, item) instead"); //</debug> return this.insert(args[0], args[1]); } //<debug warn> Ext.Logger.deprecate("Passing items as multiple arguments is deprecated, please use one single array of items instead"); //</debug> args = [Array.prototype.slice.call(args)]; } return this.callOverridden(args); }, doAdd: function(item) { var docked = item.getDocked(), overlay = item.overlay, position; if (overlay && docked) { //<debug> Ext.Logger.deprecate("'overlay' config is deprecated on docked items, please set the top/left/right/bottom configurations instead.", this); //</debug> if (docked == "top") { position = { top: 0, bottom: 'auto', left: 0, right: 0 }; } else if (docked == "bottom") { position = { top: null, bottom: 0, left: 0, right: 0 }; } if (position) { item.setDocked(false); item.setTop(position.top); item.setBottom(position.bottom); item.setLeft(position.left); item.setRight(position.right); } } return this.callOverridden(arguments); }, applyDefaults: function(defaults) { if (typeof defaults == 'function') { //<debug warn> Ext.Logger.deprecate("Passing a function as 'defaults' is deprecated. To add custom logics when " + "'defaults' is applied to each item, have your own factoryItem() method in your sub-class instead"); //</debug> } return this.callOverridden(arguments); }, factoryItemWithDefaults: function(item) { var defaults = this.getDefaults(), customDefaults, ret; // Defaults is a function (must return a string, object, or class instance) if (typeof defaults == 'function') { customDefaults = defaults.call(this, item); } // String (must be the id of an existent component) if (typeof item == 'string') { //<debug warn> Ext.Logger.deprecate("Passing a string id of item ('"+item+"') is deprecated, please pass a reference to that item instead"); //</debug> item = Ext.getCmp(item); } if (customDefaults) { this._defaults = customDefaults; } ret = this.callParent([item]); if (customDefaults) { this._defaults = defaults; } return ret; }, applyMasked: function(masked) { if (Ext.isObject(masked) && !masked.isInstance && 'message' in masked && !('xtype' in masked) && !('xclass' in masked)) { masked.xtype = 'loadmask'; //<debug warn> Ext.Logger.deprecate("Using a 'message' config without specify an 'xtype' or 'xclass' will no longer implicitly set 'xtype' to 'loadmask'. Please set that explicitly."); //</debug> } return this.callOverridden(arguments); } }); Ext.deprecateClassMethod(this, 'setMask', 'setMasked'); //</deprecated> });