X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/c930e9176a5a85509c5b0230e2bff5c22a591432..6e39d509471fe9b4e2660e0d1631b350d0c66f40:/pkgs/cmp-foundation-debug.js diff --git a/pkgs/cmp-foundation-debug.js b/pkgs/cmp-foundation-debug.js index 94a91ae5..b791522b 100644 --- a/pkgs/cmp-foundation-debug.js +++ b/pkgs/cmp-foundation-debug.js @@ -1,5 +1,5 @@ /*! - * Ext JS Library 3.0.0 + * Ext JS Library 3.1.0 * Copyright(c) 2006-2009 Ext JS, LLC * licensing@extjs.com * http://www.extjs.com/license @@ -11,12 +11,12 @@ * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).

*

This object also provides a registry of available Component classes * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}. - * The {@link Ext.Component#xtype xtype} provides a way to avoid instantiating child Components + * The {@link Ext.Component#xtype xtype} provides a way to avoid instantiating child Components * when creating a full, nested config object for a complete Ext page.

*

A child Component may be specified simply as a config object - * as long as the correct {@link Ext.Component#xtype xtype} is specified so that if and when the Component + * as long as the correct {@link Ext.Component#xtype xtype} is specified so that if and when the Component * needs rendering, the correct type can be looked up for lazy instantiation.

- *

For a list of all available {@link Ext.Component#xtype xtypes}, see {@link Ext.Component}.

+ *

For a list of all available {@link Ext.Component#xtype xtypes}, see {@link Ext.Component}.

* @singleton */ Ext.ComponentMgr = function(){ @@ -45,7 +45,7 @@ Ext.ComponentMgr = function(){ * Returns a component by {@link Ext.Component#id id}. * For additional details see {@link Ext.util.MixedCollection#get}. * @param {String} id The component {@link Ext.Component#id id} - * @return Ext.Component The Component, undefined if not found, or null if a + * @return Ext.Component The Component, undefined if not found, or null if a * Class was found. */ get : function(id){ @@ -53,10 +53,10 @@ Ext.ComponentMgr = function(){ }, /** - * Registers a function that will be called when a specified component is added to ComponentMgr + * Registers a function that will be called when a Component with the specified id is added to ComponentMgr. This will happen on instantiation. * @param {String} id The component {@link Ext.Component#id id} * @param {Function} fn The callback function - * @param {Object} scope The scope of the callback + * @param {Object} scope The scope (this reference) in which the callback is executed. Defaults to the Component. */ onAvailable : function(id, fn, scope){ all.on("add", function(index, o){ @@ -103,7 +103,7 @@ Ext.ComponentMgr = function(){ * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate. * @param {Object} config A configuration object for the Component you wish to create. * @param {Constructor} defaultType The constructor to provide the default Component type if - * the config object does not contain a xtype. (Optional if the config contains a xtype). + * the config object does not contain a xtype. (Optional if the config contains a xtype). * @return {Ext.Component} The newly instantiated Component. */ create : function(config, defaultType){ @@ -129,11 +129,16 @@ Ext.ComponentMgr = function(){ * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate. * @param {Object} config A configuration object for the Plugin you wish to create. * @param {Constructor} defaultType The constructor to provide the default Plugin type if - * the config object does not contain a ptype. (Optional if the config contains a ptype). + * the config object does not contain a ptype. (Optional if the config contains a ptype). * @return {Ext.Component} The newly instantiated Plugin. */ createPlugin : function(config, defaultType){ - return new ptypes[config.ptype || defaultType](config); + var PluginCls = ptypes[config.ptype || defaultType]; + if (PluginCls.init) { + return PluginCls; + } else { + return new PluginCls(config); + } } }; }(); @@ -156,8 +161,18 @@ Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally * @method preg */ Ext.preg = Ext.ComponentMgr.registerPlugin; -Ext.create = Ext.ComponentMgr.create; /** + * Shorthand for {@link Ext.ComponentMgr#create} + * Creates a new Component from the specified config object using the + * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate. + * @param {Object} config A configuration object for the Component you wish to create. + * @param {Constructor} defaultType The constructor to provide the default Component type if + * the config object does not contain a xtype. (Optional if the config contains a xtype). + * @return {Ext.Component} The newly instantiated Component. + * @member Ext + * @method create + */ +Ext.create = Ext.ComponentMgr.create;/** * @class Ext.Component * @extends Ext.util.Observable *

Base class for all Ext components. All subclasses of Component may participate in the automated @@ -226,7 +241,7 @@ menutextitem {@link Ext.menu.TextItem} Form components --------------------------------------- -form {@link Ext.FormPanel} +form {@link Ext.form.FormPanel} checkbox {@link Ext.form.Checkbox} checkboxgroup {@link Ext.form.CheckboxGroup} combo {@link Ext.form.ComboBox} @@ -295,6 +310,14 @@ Ext.Component = function(config){ Ext.apply(this, config); this.addEvents( + /** + * @event added + * Fires when a component is added to an Ext.Container + * @param {Ext.Component} this + * @param {Ext.Container} ownerCt Container which holds the component + * @param {number} index Position at which the component was added + */ + 'added', /** * @event disable * Fires after the component is disabled. @@ -334,6 +357,13 @@ Ext.Component = function(config){ * @param {Ext.Component} this */ 'hide', + /** + * @event removed + * Fires when a component is removed from an Ext.Container + * @param {Ext.Component} this + * @param {Ext.Container} ownerCt Container which holds the component + */ + 'removed', /** * @event beforerender * Fires before the component is {@link #rendered}. Return false from an @@ -429,7 +459,7 @@ Ext.Component = function(config){ } if(this.stateful !== false){ - this.initState(config); + this.initState(); } if(this.applyTo){ @@ -445,7 +475,7 @@ Ext.Component = function(config){ Ext.Component.AUTO_ID = 1000; Ext.extend(Ext.Component, Ext.util.Observable, { - // Configs below are used for all Components when rendered by FormLayout. + // Configs below are used for all Components when rendered by FormLayout. /** * @cfg {String} fieldLabel

The label text to display next to this Component (defaults to '').

*

Note: this config is only used when this Component is rendered by a Container which @@ -544,7 +574,11 @@ new Ext.FormPanel({ *

See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.

*/ /** - * @cfg {String} itemCls

An additional CSS class to apply to the div wrapping the form item + * @cfg {String} itemCls + *

Note: this config is only used when this Component is rendered by a Container which + * has been configured to use the {@link Ext.layout.FormLayout FormLayout} layout manager (e.g. + * {@link Ext.form.FormPanel} or specifying layout:'form').


+ *

An additional CSS class to apply to the div wrapping the form item * element of this field. If supplied, itemCls at the field level will override * the default itemCls supplied at the container level. The value specified for * itemCls will be added to the default class ('x-form-item').

@@ -554,27 +588,27 @@ new Ext.FormPanel({ * any other element within the markup for the field.

*

Note: see the note for {@link #fieldLabel}.


* Example use:

-// Apply a style to the field's label:
+// Apply a style to the field's label:
 <style>
     .required .x-form-item-label {font-weight:bold;color:red;}
 </style>
 
 new Ext.FormPanel({
-	height: 100,
-	renderTo: Ext.getBody(),
-	items: [{
-		xtype: 'textfield',
-		fieldLabel: 'Name',
-		itemCls: 'required' //this label will be styled
-	},{
-		xtype: 'textfield',
-		fieldLabel: 'Favorite Color'
-	}]
+    height: 100,
+    renderTo: Ext.getBody(),
+    items: [{
+        xtype: 'textfield',
+        fieldLabel: 'Name',
+        itemCls: 'required' //this label will be styled
+    },{
+        xtype: 'textfield',
+        fieldLabel: 'Favorite Color'
+    }]
 });
 
*/ - // Configs below are used for all Components when rendered by AnchorLayout. + // Configs below are used for all Components when rendered by AnchorLayout. /** * @cfg {String} anchor

Note: this config is only used when this Component is rendered * by a Container which has been configured to use an {@link Ext.layout.AnchorLayout AnchorLayout} @@ -913,29 +947,76 @@ new Ext.Panel({ * @property el */ /** - * The component's owner {@link Ext.Container} (defaults to undefined, and is set automatically when - * the component is added to a container). Read-only. - *

Note: to access items within the container see {@link #itemId}.

+ * This Component's owner {@link Ext.Container Container} (defaults to undefined, and is set automatically when + * this Component is added to a Container). Read-only. + *

Note: to access items within the Container see {@link #itemId}.

* @type Ext.Container * @property ownerCt */ /** * True if this component is hidden. Read-only. * @type Boolean - * @property + * @property hidden */ /** * True if this component is disabled. Read-only. * @type Boolean - * @property + * @property disabled */ /** * True if this component has been rendered. Read-only. * @type Boolean - * @property + * @property rendered */ rendered : false, + /** + * @cfg {String} contentEl + *

Optional. Specify an existing HTML element, or the id of an existing HTML element to use as the content + * for this component.

+ * + */ + /** + * @cfg {String/Object} html + * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element + * content (defaults to ''). The HTML content is added after the component is rendered, + * so the document will not contain this HTML at the time the {@link #render} event is fired. + * This content is inserted into the body before any configured {@link #contentEl} is appended. + */ + + /** + * @cfg {Mixed} tpl + * An {@link Ext.Template}, {@link Ext.XTemplate} + * or an array of strings to form an Ext.XTemplate. + * Used in conjunction with the {@link #data} and + * {@link #tplWriteMode} configurations. + */ + + /** + * @cfg {String} tplWriteMode The Ext.(X)Template method to use when + * updating the content area of the Component. Defaults to 'overwrite' + * (see {@link Ext.XTemplate#overwrite}). + */ + tplWriteMode : 'overwrite', + + /** + * @cfg {Mixed} data + * The initial set of data to apply to the {@link #tpl} to + * update the content area of the Component. + */ + + // private ctype : 'Ext.Component', @@ -1051,7 +1132,32 @@ Ext.Foo = Ext.extend(Ext.Bar, { this.el.addClassOnOver(this.overCls); } this.fireEvent('render', this); + + + // Populate content of the component with html, contentEl or + // a tpl. + var contentTarget = this.getContentTarget(); + if (this.html){ + contentTarget.update(Ext.DomHelper.markup(this.html)); + delete this.html; + } + if (this.contentEl){ + var ce = Ext.getDom(this.contentEl); + Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']); + contentTarget.appendChild(ce); + } + if (this.tpl) { + if (!this.tpl.compile) { + this.tpl = new Ext.XTemplate(this.tpl); + } + if (this.data) { + this.tpl[this.tplWriteMode](contentTarget, this.data); + delete this.data; + } + } this.afterRender(this.container); + + if(this.hidden){ // call this so we don't fire initial hide events. this.doHide(); @@ -1064,17 +1170,70 @@ Ext.Foo = Ext.extend(Ext.Bar, { if(this.stateful !== false){ this.initStateEvents(); } - this.initRef(); this.fireEvent('afterrender', this); } return this; }, - initRef : function(){ + + /** + * Update the content area of a component. + * @param {Mixed} htmlOrData + * If this component has been configured with a template via the tpl config + * then it will use this argument as data to populate the template. + * If this component was not configured with a template, the components + * content area will be updated via Ext.Element update + * @param {Boolean} loadScripts + * (optional) Only legitimate when using the html configuration. Defaults to false + * @param {Function} callback + * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading + */ + update: function(htmlOrData, loadScripts, cb) { + var contentTarget = this.getContentTarget(); + if (this.tpl && typeof htmlOrData !== "string") { + this.tpl[this.tplWriteMode](contentTarget, htmlOrData || {}); + } else { + var html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData; + contentTarget.update(html, loadScripts, cb); + } + }, + + + /** + * @private + * Method to manage awareness of when components are added to their + * respective Container, firing an added event. + * References are established at add time rather than at render time. + * @param {Ext.Container} container Container which holds the component + * @param {number} pos Position at which the component was added + */ + onAdded : function(container, pos) { + this.ownerCt = container; + this.initRef(); + this.fireEvent('added', this, container, pos); + }, + + /** + * @private + * Method to manage awareness of when components are removed from their + * respective Container, firing an removed event. References are properly + * cleaned up after removing a component from its owning container. + */ + onRemoved : function() { + this.removeRef(); + this.fireEvent('removed', this, this.ownerCt); + delete this.ownerCt; + }, + + /** + * @private + * Method to establish a reference to a component. + */ + initRef : function() { /** * @cfg {String} ref - *

A path specification, relative to the Component's {@link #ownerCt} specifying into which - * ancestor Container to place a named reference to this Component.

+ *

A path specification, relative to the Component's {@link #ownerCt} + * specifying into which ancestor Container to place a named reference to this Component.

*

The ancestor axis can be traversed by using '/' characters in the path. * For example, to put a reference to a Toolbar Button into the Panel which owns the Toolbar:


 var myGrid = new Ext.grid.EditorGridPanel({
@@ -1095,33 +1254,50 @@ var myGrid = new Ext.grid.EditorGridPanel({
     }
 });
 
- *

In the code above, if the ref had been 'saveButton' the reference would - * have been placed into the Toolbar. Each '/' in the ref moves up one level from the - * Component's {@link #ownerCt}.

+ *

In the code above, if the ref had been 'saveButton' + * the reference would have been placed into the Toolbar. Each '/' in the ref + * moves up one level from the Component's {@link #ownerCt}.

+ *

Also see the {@link #added} and {@link #removed} events.

*/ - if(this.ref){ - var levels = this.ref.split('/'); - var last = levels.length, i = 0; - var t = this; - while(i < last){ - if(t.ownerCt){ - t = t.ownerCt; - } - i++; + if(this.ref && !this.refOwner){ + var levels = this.ref.split('/'), + last = levels.length, + i = 0, + t = this; + + while(t && i < last){ + t = t.ownerCt; + ++i; + } + if(t){ + t[this.refName = levels[--i]] = this; + /** + * @type Ext.Container + * @property refOwner + * The ancestor Container into which the {@link #ref} reference was inserted if this Component + * is a child of a Container, and has been configured with a ref. + */ + this.refOwner = t; } - t[levels[--i]] = this; + } + }, + + removeRef : function() { + if (this.refOwner && this.refName) { + delete this.refOwner[this.refName]; + delete this.refOwner; } }, // private - initState : function(config){ + initState : function(){ if(Ext.state.Manager){ var id = this.getStateId(); if(id){ var state = Ext.state.Manager.get(id); if(state){ if(this.fireEvent('beforestaterestore', this, state) !== false){ - this.applyState(state); + this.applyState(Ext.apply({}, state)); this.fireEvent('staterestore', this, state); } } @@ -1144,7 +1320,7 @@ var myGrid = new Ext.grid.EditorGridPanel({ }, // private - applyState : function(state, config){ + applyState : function(state){ if(state){ Ext.apply(this, state); } @@ -1226,6 +1402,10 @@ var myGrid = new Ext.grid.EditorGridPanel({ this.el = Ext.get(this.el); if(this.allowDomMove !== false){ ct.dom.insertBefore(this.el.dom, position); + if (div) { + Ext.removeNode(div); + div = null; + } } } }, @@ -1251,19 +1431,33 @@ var myGrid = new Ext.grid.EditorGridPanel({ * */ destroy : function(){ - if(this.fireEvent('beforedestroy', this) !== false){ - this.beforeDestroy(); - if(this.rendered){ - this.el.removeAllListeners(); - this.el.remove(); - if(this.actionMode == 'container' || this.removeMode == 'container'){ - this.container.remove(); + if(!this.isDestroyed){ + if(this.fireEvent('beforedestroy', this) !== false){ + this.destroying = true; + this.beforeDestroy(); + if(this.ownerCt && this.ownerCt.remove){ + this.ownerCt.remove(this, false); + } + if(this.rendered){ + this.el.remove(); + if(this.actionMode == 'container' || this.removeMode == 'container'){ + this.container.remove(); + } } + this.onDestroy(); + Ext.ComponentMgr.unregister(this); + this.fireEvent('destroy', this); + this.purgeListeners(); + this.destroying = false; + this.isDestroyed = true; } - this.onDestroy(); - Ext.ComponentMgr.unregister(this); - this.fireEvent('destroy', this); - this.purgeListeners(); + } + }, + + deleteMembers : function(){ + var args = arguments; + for(var i = 0, len = args.length; i < len; ++i){ + delete this[args[i]]; } }, @@ -1298,6 +1492,11 @@ new Ext.Panel({ return this.el; }, + // private + getContentTarget : function(){ + return this.el; + }, + /** * Returns the id of this component or automatically generates and * returns an id if an id is not defined yet:

@@ -1418,7 +1617,7 @@ new Ext.Panel({
 
     // private
     onShow : function(){
-        this.getVisibiltyEl().removeClass('x-hide-' + this.hideMode);
+        this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
     },
 
     /**
@@ -1446,11 +1645,11 @@ new Ext.Panel({
 
     // private
     onHide : function(){
-        this.getVisibiltyEl().addClass('x-hide-' + this.hideMode);
+        this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
     },
 
     // private
-    getVisibiltyEl : function(){
+    getVisibilityEl : function(){
         return this.hideParent ? this.container : this.getActionEl();
     },
 
@@ -1468,7 +1667,7 @@ new Ext.Panel({
      * @return {Boolean} True if this component is visible, false otherwise.
      */
     isVisible : function(){
-        return this.rendered && this.getVisibiltyEl().isVisible();
+        return this.rendered && this.getVisibilityEl().isVisible();
     },
 
     /**
@@ -1579,8 +1778,9 @@ alert(t.getXTypes());  // alerts 'component/box/field/textfield'
             });
     },
 
-    getDomPositionEl : function(){
-        return this.getPositionEl ? this.getPositionEl() : this.getEl();
+    // protected
+    getPositionEl : function(){
+        return this.positionEl || this.el;
     },
 
     // private
@@ -1599,15 +1799,38 @@ alert(t.getXTypes());  // alerts 'component/box/field/textfield'
         this.mons = [];
     },
 
-    // internal function for auto removal of assigned event handlers on destruction
-    mon : function(item, ename, fn, scope, opt){
+    // private
+    createMons: function(){
         if(!this.mons){
             this.mons = [];
             this.on('beforedestroy', this.clearMons, this, {single: true});
         }
+    },
 
+    /**
+     * 

Adds listeners to any Observable object (or Elements) which are automatically removed when this Component + * is destroyed. Usage:

+myGridPanel.mon(myGridPanel.getSelectionModel(), 'selectionchange', handleSelectionChange, null, {buffer: 50});
+
+ *

or:

+myGridPanel.mon(myGridPanel.getSelectionModel(), {
+    selectionchange: handleSelectionChange,
+    buffer: 50
+});
+
+ * @param {Observable|Element} item The item to which to add a listener/listeners. + * @param {Object|String} ename The event name, or an object containing event name properties. + * @param {Function} fn Optional. If the ename parameter was an event name, this + * is the handler function. + * @param {Object} scope Optional. If the ename parameter was an event name, this + * is the scope (this reference) in which the handler function is executed. + * @param {Object} opt Optional. If the ename parameter was an event name, this + * is the {@link Ext.util.Observable#addListener addListener} options. + */ + mon : function(item, ename, fn, scope, opt){ + this.createMons(); if(Ext.isObject(ename)){ - var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/; + var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/; var o = ename; for(var e in o){ @@ -1616,31 +1839,39 @@ alert(t.getXTypes()); // alerts 'component/box/field/textfield' } if(Ext.isFunction(o[e])){ // shared options - this.mons.push({ - item: item, ename: e, fn: o[e], scope: o.scope - }); - item.on(e, o[e], o.scope, o); + this.mons.push({ + item: item, ename: e, fn: o[e], scope: o.scope + }); + item.on(e, o[e], o.scope, o); }else{ // individual options - this.mons.push({ - item: item, ename: e, fn: o[e], scope: o.scope - }); - item.on(e, o[e]); + this.mons.push({ + item: item, ename: e, fn: o[e], scope: o.scope + }); + item.on(e, o[e]); } } return; } - this.mons.push({ item: item, ename: ename, fn: fn, scope: scope }); item.on(ename, fn, scope, opt); }, - // protected, opposite of mon + /** + * Removes listeners that were added by the {@link #mon} method. + * @param {Observable|Element} item The item from which to remove a listener/listeners. + * @param {Object|String} ename The event name, or an object containing event name properties. + * @param {Function} fn Optional. If the ename parameter was an event name, this + * is the handler function. + * @param {Object} scope Optional. If the ename parameter was an event name, this + * is the scope (this reference) in which the handler function is executed. + */ mun : function(item, ename, fn, scope){ var found, mon; + this.createMons(); for(var i = 0, len = this.mons.length; i < len; ++i){ mon = this.mons[i]; if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){ @@ -1690,8 +1921,7 @@ alert(t.getXTypes()); // alerts 'component/box/field/textfield' } }); -Ext.reg('component', Ext.Component); -/** +Ext.reg('component', Ext.Component);/** * @class Ext.Action *

An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI @@ -1744,13 +1974,7 @@ aRef.setText('New text'); * @constructor * @param {Object} config The configuration options */ -Ext.Action = function(config){ - this.initialConfig = config; - this.itemId = config.itemId = (config.itemId || config.id || Ext.id()); - this.items = []; -} - -Ext.Action.prototype = { +Ext.Action = Ext.extend(Object, { /** * @cfg {String} text The text to set for all components using this action (defaults to ''). */ @@ -1783,9 +2007,16 @@ Ext.Action.prototype = { * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}. */ /** - * @cfg {Object} scope The scope in which the {@link #handler} function will execute. + * @cfg {Object} scope The scope (this reference) in which the + * {@link #handler} is executed. Defaults to this Button. */ + constructor : function(config){ + this.initialConfig = config; + this.itemId = config.itemId = (config.itemId || config.id || Ext.id()); + this.items = []; + }, + // private isAction : true, @@ -1885,10 +2116,10 @@ Ext.Action.prototype = { }, /** - * Sets the function that will be called by each component using this action when its primary event is triggered. + * Sets the function that will be called by each Component using this action when its primary event is triggered. * @param {Function} fn The function that will be invoked by the action's components. The function * will be called with no arguments. - * @param {Object} scope The scope in which the function will execute + * @param {Object} scope The scope (this reference) in which the function is executed. Defaults to the Component firing the event. */ setHandler : function(fn, scope){ this.initialConfig.handler = fn; @@ -1897,10 +2128,10 @@ Ext.Action.prototype = { }, /** - * Executes the specified function once for each component currently tied to this action. The function passed + * Executes the specified function once for each Component currently tied to this action. The function passed * in should accept a single argument that will be an object that supports the basic Action config/method interface. * @param {Function} fn The function to execute for each component - * @param {Object} scope The scope in which the function will execute + * @param {Object} scope The scope (this reference) in which the function is executed. Defaults to the Component. */ each : function(fn, scope){ Ext.each(this.items, fn, scope); @@ -1936,7 +2167,7 @@ Ext.Action.prototype = { execute : function(){ this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments); } -}; +}); /** * @class Ext.Layer * @extends Ext.Element @@ -2107,7 +2338,7 @@ Ext.extend(Ext.Layer, Ext.Element, { } this.removeAllListeners(); Ext.removeNode(this.dom); - Ext.Element.uncache(this.id); + delete this.dom; }, remove : function(){ @@ -2396,7 +2627,8 @@ Ext.extend(Ext.Layer, Ext.Element, { return this; } }); -})();/** +})(); +/** * @class Ext.Shadow * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned, * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced @@ -2611,6 +2843,14 @@ var myImage = new Ext.BoxComponent({ */ Ext.BoxComponent = Ext.extend(Ext.Component, { + // tabTip config is used when a BoxComponent is a child of a TabPanel + /** + * @cfg {String} tabTip + *

Note: this config is only used when this BoxComponent is a child item of a TabPanel.

+ * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over + * the associated tab selector element. {@link Ext.QuickTips}.init() + * must be called in order for the tips to render. + */ // Configs below are used for all Components when rendered by BorderLayout. /** * @cfg {String} region

Note: this config is only used when this BoxComponent is rendered @@ -2671,6 +2911,26 @@ Ext.BoxComponent = Ext.extend(Ext.Component, { * The width of this component in pixels (defaults to auto). * Note to express this dimension as a percentage or offset see {@link Ext.Component#anchor}. */ + /** + * @cfg {Number} boxMinHeight + *

The minimum value in pixels which this BoxComponent will set its height to.

+ *

Warning: This will override any size management applied by layout managers.

+ */ + /** + * @cfg {Number} boxMinWidth + *

The minimum value in pixels which this BoxComponent will set its width to.

+ *

Warning: This will override any size management applied by layout managers.

+ */ + /** + * @cfg {Number} boxMaxHeight + *

The maximum value in pixels which this BoxComponent will set its height to.

+ *

Warning: This will override any size management applied by layout managers.

+ */ + /** + * @cfg {Number} boxMaxWidth + *

The maximum value in pixels which this BoxComponent will set its width to.

+ *

Warning: This will override any size management applied by layout managers.

+ */ /** * @cfg {Boolean} autoHeight *

True to use height:'auto', false to use fixed height (or allow it to be managed by its parent @@ -2745,6 +3005,11 @@ var myPanel = new Ext.Panel({ });

*/ + /** + * @cfg {Boolean} autoScroll + * true to use overflow:'auto' on the components layout element and show scroll bars automatically when + * necessary, false to clip any overflowing content (defaults to false). + */ /* // private internal config * {Boolean} deferHeight @@ -2800,15 +3065,26 @@ var myPanel = new Ext.Panel({ * @return {Ext.BoxComponent} this */ setSize : function(w, h){ + // support for standard size objects if(typeof w == 'object'){ - h = w.height; - w = w.width; + h = w.height, w = w.width; + } + if (Ext.isDefined(w) && Ext.isDefined(this.boxMinWidth) && (w < this.boxMinWidth)) { + w = this.boxMinWidth; + } + if (Ext.isDefined(h) && Ext.isDefined(this.boxMinHeight) && (h < this.boxMinHeight)) { + h = this.boxMinHeight; + } + if (Ext.isDefined(w) && Ext.isDefined(this.boxMaxWidth) && (w > this.boxMaxWidth)) { + w = this.boxMaxWidth; + } + if (Ext.isDefined(h) && Ext.isDefined(this.boxMaxHeight) && (h > this.boxMaxHeight)) { + h = this.boxMaxHeight; } // not rendered if(!this.boxReady){ - this.width = w; - this.height = h; + this.width = w, this.height = h; return this; } @@ -2817,10 +3093,12 @@ var myPanel = new Ext.Panel({ return this; } this.lastSize = {width: w, height: h}; - var adj = this.adjustSize(w, h); - var aw = adj.width, ah = adj.height; + var adj = this.adjustSize(w, h), + aw = adj.width, + ah = adj.height, + rz; if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters - var rz = this.getResizeEl(); + rz = this.getResizeEl(); if(!this.deferHeight && aw !== undefined && ah !== undefined){ rz.setSize(aw, ah); }else if(!this.deferHeight && ah !== undefined){ @@ -2829,7 +3107,6 @@ var myPanel = new Ext.Panel({ rz.setWidth(aw); } this.onResize(aw, ah, w, h); - this.fireEvent('resize', this, aw, ah, w, h); } return this; }, @@ -2939,14 +3216,23 @@ var myPanel = new Ext.Panel({ * contains both the <input> Element (which is what would be returned * by its {@link #getEl} method, and the trigger button Element. * This Element is returned as the resizeEl. + * @return {Ext.Element} The Element which is to be resized by size managing layouts. */ getResizeEl : function(){ return this.resizeEl || this.el; }, - // protected - getPositionEl : function(){ - return this.positionEl || this.el; + /** + * Sets the overflow on the content element of the component. + * @param {Boolean} scroll True to allow the Component to auto scroll. + * @return {Ext.BoxComponent} this + */ + setAutoScroll : function(scroll){ + if(this.rendered){ + this.getContentTarget().setOverflow(scroll ? 'auto' : ''); + } + this.autoScroll = scroll; + return this; }, /** @@ -3010,20 +3296,16 @@ var myPanel = new Ext.Panel({ }, // private - onRender : function(ct, position){ - Ext.BoxComponent.superclass.onRender.call(this, ct, position); + afterRender : function(){ + Ext.BoxComponent.superclass.afterRender.call(this); if(this.resizeEl){ this.resizeEl = Ext.get(this.resizeEl); } if(this.positionEl){ this.positionEl = Ext.get(this.positionEl); } - }, - - // private - afterRender : function(){ - Ext.BoxComponent.superclass.afterRender.call(this); this.boxReady = true; + this.setAutoScroll(this.autoScroll); this.setSize(this.width, this.height); if(this.x || this.y){ this.setPosition(this.x, this.y); @@ -3051,7 +3333,7 @@ var myPanel = new Ext.Panel({ * @param {Number} rawHeight The height that was originally specified */ onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){ - + this.fireEvent('resize', this, adjWidth, adjHeight, rawWidth, rawHeight); }, /* // protected @@ -3113,12 +3395,12 @@ split.on('moved', splitterMoved); * @param {Mixed} dragElement The element to be dragged and act as the SplitBar. * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL) - * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or + * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial position of the SplitBar). */ Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){ - + /** @private */ this.el = Ext.get(dragElement, true); this.el.dom.unselectable = "on"; @@ -3132,7 +3414,7 @@ Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, ex * @type Number */ this.orientation = orientation || Ext.SplitBar.HORIZONTAL; - + /** * The increment, in pixels by which to move this SplitBar. When undefined, the SplitBar moves smoothly. * @type Number @@ -3143,28 +3425,28 @@ Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, ex * @type Number */ this.minSize = 0; - + /** * The maximum size of the resizing element. (Defaults to 2000) * @type Number */ this.maxSize = 2000; - + /** * Whether to animate the transition to the new size * @type Boolean */ this.animate = false; - + /** * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes. * @type Boolean */ this.useShim = false; - + /** @private */ this.shim = null; - + if(!existingProxy){ /** @private */ this.proxy = Ext.SplitBar.createProxy(this.orientation); @@ -3173,22 +3455,22 @@ Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, ex } /** @private */ this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id}); - + /** @private */ this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this); - + /** @private */ this.dd.endDrag = this.onEndProxyDrag.createDelegate(this); - + /** @private */ this.dragSpecs = {}; - + /** * @private The adapter to use to positon and resize elements */ this.adapter = new Ext.SplitBar.BasicLayoutAdapter(); this.adapter.init(this); - + if(this.orientation == Ext.SplitBar.HORIZONTAL){ /** @private */ this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT); @@ -3198,7 +3480,7 @@ Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, ex this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM); this.el.addClass("x-splitbar-v"); } - + this.addEvents( /** * @event resize @@ -3243,7 +3525,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { if(this.orientation == Ext.SplitBar.HORIZONTAL){ this.dd.resetConstraints(); this.dd.setXConstraint( - this.placement == Ext.SplitBar.LEFT ? c1 : c2, + this.placement == Ext.SplitBar.LEFT ? c1 : c2, this.placement == Ext.SplitBar.LEFT ? c2 : c1, this.tickSize ); @@ -3252,7 +3534,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { this.dd.resetConstraints(); this.dd.setXConstraint(0, 0); this.dd.setYConstraint( - this.placement == Ext.SplitBar.TOP ? c1 : c2, + this.placement == Ext.SplitBar.TOP ? c1 : c2, this.placement == Ext.SplitBar.TOP ? c2 : c1, this.tickSize ); @@ -3261,8 +3543,8 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { this.dragSpecs.startPoint = [x, y]; Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y); }, - - /** + + /** * @private Called after the drag operation by the DDProxy */ onEndProxyDrag : function(e){ @@ -3274,13 +3556,13 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { } var newSize; if(this.orientation == Ext.SplitBar.HORIZONTAL){ - newSize = this.dragSpecs.startSize + + newSize = this.dragSpecs.startSize + (this.placement == Ext.SplitBar.LEFT ? endPoint[0] - this.dragSpecs.startPoint[0] : this.dragSpecs.startPoint[0] - endPoint[0] ); }else{ - newSize = this.dragSpecs.startSize + + newSize = this.dragSpecs.startSize + (this.placement == Ext.SplitBar.TOP ? endPoint[1] - this.dragSpecs.startPoint[1] : this.dragSpecs.startPoint[1] - endPoint[1] @@ -3295,7 +3577,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { } } }, - + /** * Get the adapter this SplitBar uses * @return The adapter object @@ -3303,7 +3585,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { getAdapter : function(){ return this.adapter; }, - + /** * Set the adapter this SplitBar uses * @param {Object} adapter A SplitBar adapter object @@ -3312,7 +3594,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { this.adapter = adapter; this.adapter.init(this); }, - + /** * Gets the minimum size for the resizing element * @return {Number} The minimum size @@ -3320,7 +3602,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { getMinimumSize : function(){ return this.minSize; }, - + /** * Sets the minimum size for the resizing element * @param {Number} minSize The minimum size @@ -3328,7 +3610,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { setMinimumSize : function(minSize){ this.minSize = minSize; }, - + /** * Gets the maximum size for the resizing element * @return {Number} The maximum size @@ -3336,7 +3618,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { getMaximumSize : function(){ return this.maxSize; }, - + /** * Sets the maximum size for the resizing element * @param {Number} maxSize The maximum size @@ -3344,7 +3626,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { setMaximumSize : function(maxSize){ this.maxSize = maxSize; }, - + /** * Sets the initialize size for the resizing element * @param {Number} size The initial size @@ -3355,18 +3637,18 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { this.adapter.setElementSize(this, size); this.animate = oldAnimate; }, - + /** - * Destroy this splitbar. + * Destroy this splitbar. * @param {Boolean} removeEl True to remove the element */ destroy : function(removeEl){ - Ext.destroy(this.shim, Ext.get(this.proxy)); + Ext.destroy(this.shim, Ext.get(this.proxy)); this.dd.unreg(); if(removeEl){ this.el.remove(); } - this.purgeListeners(); + this.purgeListeners(); } }); @@ -3375,14 +3657,14 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { */ Ext.SplitBar.createProxy = function(dir){ var proxy = new Ext.Element(document.createElement("div")); + document.body.appendChild(proxy.dom); proxy.unselectable(); var cls = 'x-splitbar-proxy'; proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v')); - document.body.appendChild(proxy.dom); return proxy.dom; }; -/** +/** * @class Ext.SplitBar.BasicLayoutAdapter * Default Adapter. It assumes the splitter and resizing element are not positioned * elements and only gets/sets the width of the element. Generally used for table based layouts. @@ -3393,10 +3675,10 @@ Ext.SplitBar.BasicLayoutAdapter = function(){ Ext.SplitBar.BasicLayoutAdapter.prototype = { // do nothing for now init : function(s){ - + }, /** - * Called before drag operations to get the current size of the resizing element. + * Called before drag operations to get the current size of the resizing element. * @param {Ext.SplitBar} s The SplitBar using this adapter */ getElementSize : function(s){ @@ -3406,7 +3688,7 @@ Ext.SplitBar.BasicLayoutAdapter.prototype = { return s.resizingEl.getHeight(); } }, - + /** * Called after drag operations to set the size of the resizing element. * @param {Ext.SplitBar} s The SplitBar using this adapter @@ -3424,7 +3706,7 @@ Ext.SplitBar.BasicLayoutAdapter.prototype = { s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut'); } }else{ - + if(!s.animate){ s.resizingEl.setHeight(newSize); if(onComplete){ @@ -3437,10 +3719,10 @@ Ext.SplitBar.BasicLayoutAdapter.prototype = { } }; -/** +/** *@class Ext.SplitBar.AbsoluteLayoutAdapter * @extends Ext.SplitBar.BasicLayoutAdapter - * Adapter that moves the splitter element to align with the resized sizing element. + * Adapter that moves the splitter element to align with the resized sizing element. * Used with an absolute positioned SplitBar. * @param {Mixed} container The container that wraps around the absolute positioned content. If it's * document.body, make sure you assign an id to the body element. @@ -3454,15 +3736,15 @@ Ext.SplitBar.AbsoluteLayoutAdapter.prototype = { init : function(s){ this.basic.init(s); }, - + getElementSize : function(s){ return this.basic.getElementSize(s); }, - + setElementSize : function(s, newSize, onComplete){ this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s])); }, - + moveSplitter : function(s){ var yes = Ext.SplitBar; switch(s.placement){ @@ -3532,12 +3814,12 @@ Ext.SplitBar.BOTTOM = 4; *

The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}. * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight * Container to be encapsulated by an HTML element to your specifications by using the - * {@link Ext.Component#autoEl autoEl} config option. This is a useful technique when creating + * {@link Ext.Component#autoEl autoEl} config option. This is a useful technique when creating * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels} * for example.

* *

The code below illustrates both how to explicitly create a Container, and how to implicitly - * create one using the 'container' xtype:


+ * create one using the 'container' xtype:

 // explicitly create a Container
 var embeddedColumns = new Ext.Container({
     autoEl: 'div',  // This is the default
@@ -3582,7 +3864,7 @@ var embeddedColumns = new Ext.Container({
  * Container, and does not apply any sizing at all.

*

A common mistake is when a developer neglects to specify a * {@link #layout} (e.g. widgets like GridPanels or - * TreePanels are added to Containers for which no {@link #layout} + * TreePanels are added to Containers for which no {@link #layout} * has been specified). If a Container is left to use the default * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its * child components will be resized, or changed in any way when the Container @@ -3608,12 +3890,11 @@ myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid); *

Overnesting is a common problem. * An example of overnesting occurs when a GridPanel is added to a TabPanel * by wrapping the GridPanel inside a wrapping Panel (that has no - * {@link #layout} specified) and then add that wrapping Panel + * {@link #layout} specified) and then add that wrapping Panel * to the TabPanel. The point to realize is that a GridPanel is a * Component which can be added directly to a Container. If the wrapping Panel - * has no {@link #layout} configuration, then the overnested + * has no {@link #layout} configuration, then the overnested * GridPanel will not be sized as expected.

-

* *

Adding via remote configuration

* @@ -3641,7 +3922,7 @@ Ext.Ajax.request({ });
*

The server script needs to return an executable Javascript statement which, when processed - * using eval(), will return either a config object with an {@link Ext.Component#xtype xtype}, + * using eval(), will return either a config object with an {@link Ext.Component#xtype xtype}, * or an instantiated Component. The server might return this for example:


 (function() {
     function formatDate(value){
@@ -3681,10 +3962,10 @@ Ext.Ajax.request({
     return grid;  // return instantiated component
 })();
 
- *

When the above code fragment is passed through the eval function in the success handler + *

When the above code fragment is passed through the eval function in the success handler * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function * runs, and returns the instantiated grid component.

- *

Note: since the code above is generated by a server script, the baseParams for + *

Note: since the code above is generated by a server script, the baseParams for * the Store, the metadata to allow generation of the Record layout, and the ColumnModel * can all be generated into the code since these are all known on the server.

* @@ -3699,11 +3980,12 @@ Ext.Container = Ext.extend(Ext.BoxComponent, { */ /** * @cfg {String/Object} layout - * When creating complex UIs, it is important to remember that sizing and - * positioning of child items is the responsibility of the Container's - * layout manager. If you expect child items to be sized in response to - * user interactions, you must specify a layout manager which - * creates and manages the type of layout you have in mind. For example:

+     * 

*Important: In order for child items to be correctly sized and + * positioned, typically a layout manager must be specified through + * the layout configuration option.

+ *

The sizing and positioning of child {@link items} is the responsibility of + * the Container's layout manager which creates and manages the type of layout + * you have in mind. For example:


 new Ext.Window({
     width:300, height: 300,
     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
@@ -3712,13 +3994,17 @@ new Ext.Window({
     }]
 }).show();
      * 
- *

Omitting the {@link #layout} config means that the - * {@link Ext.layout.ContainerLayout default layout manager} will be used which does - * nothing but render child components sequentially into the Container (no sizing or - * positioning will be performed in this situation).

- *

The layout manager class for this container may be specified as either as an - * Object or as a String:

- *
* - *
  • type
  • + *
  • type
  • *

    The layout type to be used for this container. If not specified, * a default {@link Ext.layout.ContainerLayout} will be created and used.

    - *

    Valid layout type values are:

    + *

    Valid layout type values are:

    *
    * *
  • Layout specific configuration properties
  • *

    Additional layout specific configuration properties may also be * specified. For complete details regarding the valid config options for - * each layout type, see the layout class corresponding to the type + * each layout type, see the layout class corresponding to the type * specified.

    * * @@ -3770,13 +4056,13 @@ layoutConfig: { align: 'left' } - *
  • layout
  • - *

    The layout type to be used for this container (see list + *

  • layout
  • + *

    The layout type to be used for this container (see list * of valid layout type values above).


    - *
  • {@link #layoutConfig}
  • + *
  • {@link #layoutConfig}
  • *

    Additional layout specific configuration properties. For complete * details regarding the valid config options for each layout type, see the - * layout class corresponding to the layout specified.

    + * layout class corresponding to the layout specified.

    * */ /** @@ -3787,12 +4073,12 @@ layoutConfig: { */ /** * @cfg {Boolean/Number} bufferResize - * When set to true (100 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer + * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers - * with a large quantity of sub-components for which frequent layout calls would be expensive. + * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to 50. */ - bufferResize: 100, - + bufferResize: 50, + /** * @cfg {String/Number} activeItem * A string component id or the numeric index of the component that should be initially activated within the @@ -3803,7 +4089,7 @@ layoutConfig: { */ /** * @cfg {Object/Array} items - *
    ** IMPORTANT: be sure to specify a {@link #layout} ! **
    + *
    ** IMPORTANT: be sure to {@link #layout specify a layout} if needed ! **
    *

    A single item, or an array of child Components to be added to this container, * for example:

    *
    
    @@ -3840,14 +4126,18 @@ layout: 'anchor', // specify a layout!
          * 
          */
         /**
    -     * @cfg {Object} defaults
    -     * 

    A config object that will be applied to all components added to this container either via the {@link #items} - * config or via the {@link #add} or {@link #insert} methods. The defaults config can contain any - * number of name/value property pairs to be added to each item, and should be valid for the types of items - * being added to the container. For example, to automatically apply padding to the body of each of a set of - * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.


    - *

    Note: defaults will not be applied to config objects if the option is already specified. - * For example:

    
    +     * @cfg {Object|Function} defaults
    +     * 

    This option is a means of applying default settings to all added items whether added through the {@link #items} + * config or via the {@link #add} or {@link #insert} methods.

    + *

    If an added item is a config object, and not an instantiated Component, then the default properties are + * unconditionally applied. If the added item is an instantiated Component, then the default properties are + * applied conditionally so as not to override existing properties in the item.

    + *

    If the defaults option is specified as a function, then the function will be called using this Container as the + * scope (this reference) and passing the added item as the first parameter. Any resulting object + * from that call is then applied to the item as default properties.

    + *

    For example, to automatically apply padding to the body of each of a set of + * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.

    + *

    Usage:

    
     defaults: {               // defaults are applied to items, not the container
         autoScroll:true
     },
    @@ -3885,11 +4175,24 @@ items: [
         /** @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.

    - *

    Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', - * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

    + *

    Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', + * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

    */ defaultType : 'panel', + /** @cfg {String} resizeEvent + * The event to listen to for resizing in layouts. Defaults to 'resize'. + */ + resizeEvent: 'resize', + + /** + * @cfg {Array} bubbleEvents + *

    An array of events that, when fired, should be bubbled to any parent container. + * See {@link Ext.util.Observable#enableBubble}. + * Defaults to ['add', 'remove']. + */ + bubbleEvents: ['add', 'remove'], + // private initComponent : function(){ Ext.Container.superclass.initComponent.call(this); @@ -3938,7 +4241,7 @@ items: [ 'remove' ); - this.enableBubble('add', 'remove'); + this.enableBubble(this.bubbleEvents); /** * The collection of components in this container as a {@link Ext.util.MixedCollection} @@ -3948,11 +4251,7 @@ items: [ var items = this.items; if(items){ delete this.items; - if(Ext.isArray(items) && items.length > 0){ - this.add.apply(this, items); - }else{ - this.add(items); - } + this.add(items); } }, @@ -3974,29 +4273,35 @@ items: [ layout.setContainer(this); }, - // private - render : function(){ - Ext.Container.superclass.render.apply(this, arguments); - if(this.layout){ - if(Ext.isObject(this.layout) && !this.layout.layout){ - this.layoutConfig = this.layout; - this.layout = this.layoutConfig.type; - } - if(typeof this.layout == 'string'){ - this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); - } - this.setLayout(this.layout); + afterRender: function(){ + this.layoutDone = false; + if(!this.layout){ + this.layout = 'auto'; + } + if(Ext.isObject(this.layout) && !this.layout.layout){ + this.layoutConfig = this.layout; + this.layout = this.layoutConfig.type; + } + if(Ext.isString(this.layout)){ + this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); + } + this.setLayout(this.layout); - if(this.activeItem !== undefined){ - var item = this.activeItem; - delete this.activeItem; - this.layout.setActiveItem(item); - } + // BoxComponent's afterRender will set the size. + // This will will trigger a layout if the layout is configured to monitor resize + Ext.Container.superclass.afterRender.call(this); + + if(Ext.isDefined(this.activeItem)){ + var item = this.activeItem; + delete this.activeItem; + this.layout.setActiveItem(item); } - if(!this.ownerCt){ - // force a layout if no ownerCt is set + + // If we have no ownerCt and the BoxComponent's sizing did not trigger a layout, force a layout + if(!this.ownerCt && !this.layoutDone){ this.doLayout(false, true); } + if(this.monitorResize === true){ Ext.EventManager.onWindowResize(this.doLayout, this, [false]); } @@ -4029,10 +4334,10 @@ items: [ * *

    Notes : *

      - *
    • If the Container is already rendered when add + *
    • If the Container is already rendered when add * is called, you may need to call {@link #doLayout} to refresh the view which causes * any unrendered child Components to be rendered. This is required so that you can - * add multiple child components if needed while only refreshing the layout + * add multiple child components if needed while only refreshing the layout * once. For example:
      
       var tb = new {@link Ext.Toolbar}();
       tb.render(document.body);  // toolbar is rendered
      @@ -4055,21 +4360,40 @@ tb.{@link #doLayout}();             // refresh the layout
               this.initItems();
               var args = arguments.length > 1;
               if(args || Ext.isArray(comp)){
      +            var result = [];
                   Ext.each(args ? arguments : comp, function(c){
      -                this.add(c);
      +                result.push(this.add(c));
                   }, this);
      -            return;
      +            return result;
               }
               var c = this.lookupComponent(this.applyDefaults(comp));
      -        var pos = this.items.length;
      -        if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){
      +        var index = this.items.length;
      +        if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
                   this.items.add(c);
      -            c.ownerCt = this;
      -            this.fireEvent('add', this, c, pos);
      +            // *onAdded
      +            c.onAdded(this, index);
      +            this.onAdd(c);
      +            this.fireEvent('add', this, c, index);
               }
               return c;
           },
       
      +    onAdd : function(c){
      +        // Empty template method
      +    },
      +    
      +    // private
      +    onAdded : function(container, pos) {
      +        //overridden here so we can cascade down, not worth creating a template method.
      +        this.ownerCt = container;
      +        this.initRef();
      +        //initialize references for child items
      +        this.cascade(function(c){
      +            c.initRef();
      +        });
      +        this.fireEvent('added', this, container, pos);
      +    },
      +
           /**
            * Inserts a Component into this Container at a specified index. Fires the
            * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
      @@ -4092,20 +4416,21 @@ tb.{@link #doLayout}();             // refresh the layout
               this.initItems();
               var a = arguments, len = a.length;
               if(len > 2){
      +            var result = [];
                   for(var i = len-1; i >= 1; --i) {
      -                this.insert(index, a[i]);
      +                result.push(this.insert(index, a[i]));
                   }
      -            return;
      +            return result;
               }
               var c = this.lookupComponent(this.applyDefaults(comp));
      -
      -        if(c.ownerCt == this && this.items.indexOf(c) < index){
      -            --index;
      -        }
      -
      +        index = Math.min(index, this.items.length);
               if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
      +            if(c.ownerCt == this){
      +                this.items.remove(c);
      +            }
                   this.items.insert(index, c);
      -            c.ownerCt = this;
      +            c.onAdded(this, index);
      +            this.onAdd(c);
                   this.fireEvent('add', this, c, index);
               }
               return c;
      @@ -4113,14 +4438,18 @@ tb.{@link #doLayout}();             // refresh the layout
       
           // private
           applyDefaults : function(c){
      -        if(this.defaults){
      -            if(typeof c == 'string'){
      +        var d = this.defaults;
      +        if(d){
      +            if(Ext.isFunction(d)){
      +                d = d.call(this, c);
      +            }
      +            if(Ext.isString(c)){
                       c = Ext.ComponentMgr.get(c);
      -                Ext.apply(c, this.defaults);
      +                Ext.apply(c, d);
                   }else if(!c.events){
      -                Ext.applyIf(c, this.defaults);
      +                Ext.applyIf(c, d);
                   }else{
      -                Ext.apply(c, this.defaults);
      +                Ext.apply(c, d);
                   }
               }
               return c;
      @@ -4148,19 +4477,29 @@ tb.{@link #doLayout}();             // refresh the layout
               this.initItems();
               var c = this.getComponent(comp);
               if(c && this.fireEvent('beforeremove', this, c) !== false){
      -            this.items.remove(c);
      -            delete c.ownerCt;
      -            if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
      -                c.destroy();
      -            }
      -            if(this.layout && this.layout.activeItem == c){
      -                delete this.layout.activeItem;
      -            }
      +            this.doRemove(c, autoDestroy);
                   this.fireEvent('remove', this, c);
               }
               return c;
           },
       
      +    onRemove: function(c){
      +        // Empty template method
      +    },
      +
      +    // private
      +    doRemove: function(c, autoDestroy){
      +        if(this.layout && this.rendered){
      +            this.layout.onRemove(c);
      +        }
      +        this.items.remove(c);
      +        c.onRemoved();
      +        this.onRemove(c);
      +        if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
      +            c.destroy();
      +        }
      +    },
      +
           /**
            * Removes all components from this container.
            * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
      @@ -4188,9 +4527,9 @@ tb.{@link #doLayout}();             // refresh the layout
            * and gets a direct child component of this container.
            * @param {String/Number} comp This parameter may be any of the following:
            * 
        - *
      • a String : representing the {@link Ext.Component#itemId itemId} + *
      • a String : representing the {@link Ext.Component#itemId itemId} * or {@link Ext.Component#id id} of the child component
      • - *
      • a Number : representing the position of the child component + *
      • a Number : representing the position of the child component * within the {@link #items} property
      • *
      *

      For additional information see {@link Ext.util.MixedCollection#get}. @@ -4198,14 +4537,14 @@ tb.{@link #doLayout}(); // refresh the layout */ getComponent : function(comp){ if(Ext.isObject(comp)){ - return comp; + comp = comp.getItemId(); } return this.items.get(comp); }, // private lookupComponent : function(comp){ - if(typeof comp == 'string'){ + if(Ext.isString(comp)){ return Ext.ComponentMgr.get(comp); }else if(!comp.events){ return this.createComponent(comp); @@ -4214,13 +4553,28 @@ tb.{@link #doLayout}(); // refresh the layout }, // private - createComponent : function(config){ - return Ext.create(config, this.defaultType); + createComponent : function(config, defaultType){ + // add in ownerCt at creation time but then immediately + // remove so that onBeforeAdd can handle it + var c = config.render ? config : Ext.create(Ext.apply({ + ownerCt: this + }, config), defaultType || this.defaultType); + delete c.ownerCt; + return c; }, /** - * Force this container's layout to be recalculated. A call to this function is required after adding a new component - * to an already rendered container, or possibly after changing sizing/position properties of child components. + * We can only lay out if there is a view area in which to layout. + * display:none on the layout target, *or any of its parent elements* will mean it has no view area. + */ + canLayout: function() { + var el = this.getLayoutTarget(), vs; + return !!(el && (vs = el.dom.offsetWidth || el.dom.offsetHeight)); + }, + + /** + * Force this container's layout to be recalculated. A call to this function is required after adding a new component + * to an already rendered container, or possibly after changing sizing/position properties of child components. * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer) * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden. @@ -4228,42 +4582,80 @@ tb.{@link #doLayout}(); // refresh the layout */ doLayout: function(shallow, force){ var rendered = this.rendered, - forceLayout = this.forceLayout; + forceLayout = force || this.forceLayout, + cs, i, len, c; - if(!this.isVisible() || this.collapsed){ + this.layoutDone = true; + if(!this.canLayout() || this.collapsed){ this.deferLayout = this.deferLayout || !shallow; - if(!(force || forceLayout)){ + if(!forceLayout){ return; } shallow = shallow && !this.deferLayout; } else { delete this.deferLayout; } + + cs = (shallow !== true && this.items) ? this.items.items : []; + +// Inhibit child Containers from relaying on resize since we are about to to explicitly call doLayout on them all! + for(i = 0, len = cs.length; i < len; i++){ + if ((c = cs[i]).layout) { + c.suspendLayoutResize = true; + } + } + +// Tell the layout manager to ensure all child items are rendered, and sized according to their rules. +// Will not cause the child items to relayout. if(rendered && this.layout){ this.layout.layout(); } - if(shallow !== true && this.items){ - var cs = this.items.items; - for(var i = 0, len = cs.length; i < len; i++){ - var c = cs[i]; - if(c.doLayout){ - c.forceLayout = forceLayout; - c.doLayout(); - } + +// Explicitly lay out all child items + for(i = 0; i < len; i++){ + if((c = cs[i]).doLayout){ + c.doLayout(false, forceLayout); } } if(rendered){ - this.onLayout(shallow, force); + this.onLayout(shallow, forceLayout); } + // Initial layout completed + this.hasLayout = true; delete this.forceLayout; + +// Re-enable child layouts relaying on resize. + for(i = 0; i < len; i++){ + if ((c = cs[i]).layout) { + delete c.suspendLayoutResize; + } + } }, //private onLayout : Ext.emptyFn, + onResize: function(adjWidth, adjHeight, rawWidth, rawHeight){ + Ext.Container.superclass.onResize.apply(this, arguments); + if ((this.rendered && this.layout && this.layout.monitorResize) && !this.suspendLayoutResize) { + this.layout.onResize(); + } + }, + + // private + hasLayoutPending: function(){ + // Traverse hierarchy to see if any parent container has a pending layout. + var pending = this.layoutPending; + this.ownerCt.bubble(function(c){ + return !(pending = c.layoutPending); + }); + return pending; + + }, + onShow : function(){ Ext.Container.superclass.onShow.call(this); - if(this.deferLayout !== undefined){ + if(Ext.isDefined(this.deferLayout)){ this.doLayout(true); } }, @@ -4283,8 +4675,11 @@ tb.{@link #doLayout}(); // refresh the layout // private beforeDestroy : function(){ + var c; if(this.items){ - Ext.destroy.apply(Ext, this.items.items); + while(c = this.items.first()){ + this.doRemove(c, true); + } } if(this.monitorResize){ Ext.EventManager.removeResizeListener(this.doLayout, this); @@ -4422,11 +4817,7 @@ Ext.reg('container', Ext.Container); *

      This class is intended to be extended or created via the {@link Ext.Container#layout layout} * configuration property. See {@link Ext.Container#layout} for additional details.

      */ -Ext.layout.ContainerLayout = function(config){ - Ext.apply(this, config); -}; - -Ext.layout.ContainerLayout.prototype = { +Ext.layout.ContainerLayout = Ext.extend(Object, { /** * @cfg {String} extraCls *

      An optional extra CSS class that will be added to the container. This can be useful for adding @@ -4466,11 +4857,19 @@ Ext.layout.ContainerLayout.prototype = { // private activeItem : null, + constructor : function(config){ + Ext.apply(this, config); + }, + // private layout : function(){ var target = this.container.getLayoutTarget(); + if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){ + target.addClass(this.targetCls) + } this.onLayout(this.container, target); this.container.fireEvent('afterlayout', this.container, this); + this.hasLayout = true; }, // private @@ -4480,7 +4879,7 @@ Ext.layout.ContainerLayout.prototype = { // private isValidParent : function(c, target){ - return target && c.getDomPositionEl().dom.parentNode == (target.dom || target); + return target && c.getPositionEl().dom.parentNode == (target.dom || target); }, // private @@ -4500,73 +4899,95 @@ Ext.layout.ContainerLayout.prototype = { c.render(target, position); this.configureItem(c, position); }else if(c && !this.isValidParent(c, target)){ - if(typeof position == 'number'){ + if(Ext.isNumber(position)){ position = target.dom.childNodes[position]; } - target.dom.insertBefore(c.getDomPositionEl().dom, position || null); + target.dom.insertBefore(c.getPositionEl().dom, position || null); c.container = target; this.configureItem(c, position); } }, - + // private configureItem: function(c, position){ if(this.extraCls){ var t = c.getPositionEl ? c.getPositionEl() : c; t.addClass(this.extraCls); } + // If we are forcing a layout, do so *before* we hide so elements have height/width + if(c.doLayout && this.forceLayout){ + c.doLayout(false, true); + } if (this.renderHidden && c != this.activeItem) { c.hide(); } - if(c.doLayout){ - c.doLayout(false, this.forceLayout); + }, + + onRemove: function(c){ + if(this.activeItem == c){ + delete this.activeItem; + } + if(c.rendered && this.extraCls){ + var t = c.getPositionEl ? c.getPositionEl() : c; + t.removeClass(this.extraCls); } }, // private onResize: function(){ - if(this.container.collapsed){ + var ct = this.container, + b = ct.bufferResize; + + if (ct.collapsed){ return; } - var b = this.container.bufferResize; - if(b){ - if(!this.resizeTask){ - this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this); - this.resizeBuffer = typeof b == 'number' ? b : 100; + + // Not having an ownerCt negates the buffering: floating and top level + // Containers (Viewport, Window, ToolTip, Menu) need to lay out ASAP. + if (b && ct.ownerCt) { + // If we do NOT already have a layout pending from an ancestor, schedule one. + // If there is a layout pending, we do nothing here. + // buffering to be deprecated soon + if (!ct.hasLayoutPending()){ + if(!this.resizeTask){ + this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this); + this.resizeBuffer = Ext.isNumber(b) ? b : 50; + } + ct.layoutPending = true; + this.resizeTask.delay(this.resizeBuffer); } - this.resizeTask.delay(this.resizeBuffer); }else{ - this.runLayout(); + ct.doLayout(false, this.forceLayout); } }, - + // private runLayout: function(){ - this.layout(); - this.container.onLayout(); + var ct = this.container; + ct.doLayout(); + delete ct.layoutPending; }, // private setContainer : function(ct){ + // No longer use events to handle resize. Instead this will be handled through a direct function call. + /* if(this.monitorResize && ct != this.container){ - if(this.container){ - this.container.un('resize', this.onResize, this); - this.container.un('bodyresize', this.onResize, this); + var old = this.container; + if(old){ + old.un(old.resizeEvent, this.onResize, this); } if(ct){ - ct.on({ - scope: this, - resize: this.onResize, - bodyresize: this.onResize - }); + ct.on(ct.resizeEvent, this.onResize, this); } } + */ this.container = ct; }, // private parseMargins : function(v){ - if(typeof v == 'number'){ + if(Ext.isNumber(v)){ v = v.toString(); } var ms = v.split(' '); @@ -4592,7 +5013,7 @@ Ext.layout.ContainerLayout.prototype = { }, /** - * The {@link Template Ext.Template} used by Field rendering layout classes (such as + * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped, * labeled and styled form Field. A default Template is supplied, but this may be * overriden to create custom field structures. The template processes values returned from @@ -4611,15 +5032,23 @@ Ext.layout.ContainerLayout.prototype = { t.disableFormats = true; return t.compile(); })(), - + /* * Destroys this layout. This is a template method that is empty by default, but should be implemented * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes. * @protected */ - destroy : Ext.emptyFn -}; -Ext.Container.LAYOUTS['auto'] = Ext.layout.ContainerLayout;/** + destroy : function(){ + if(!Ext.isEmpty(this.targetCls)){ + var target = this.container.getLayoutTarget(); + if(target){ + target.removeClass(this.targetCls); + } + } + } +}); +Ext.Container.LAYOUTS['auto'] = Ext.layout.ContainerLayout; +/** * @class Ext.layout.FitLayout * @extends Ext.layout.ContainerLayout *

      This is a base class for layouts that contain a single item that automatically expands to fill the layout's @@ -4648,8 +5077,7 @@ Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, { onLayout : function(ct, target){ Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target); if(!this.container.collapsed){ - var sz = (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getViewSize() : target.getStyleSize(); - this.setItemSize(this.activeItem || ct.items.itemAt(0), sz); + this.setItemSize(this.activeItem || ct.items.itemAt(0), target.getViewSize(true)); } }, @@ -4749,7 +5177,7 @@ Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, { constructor: function(config){ Ext.layout.CardLayout.superclass.constructor.call(this, config); - this.forceLayout = (this.deferredRender === false); + // this.forceLayout = (this.deferredRender === false); }, /** @@ -4757,17 +5185,23 @@ Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, { * @param {String/Number} item The string component id or numeric index of the item to activate */ setActiveItem : function(item){ + var ai = this.activeItem; item = this.container.getComponent(item); - if(this.activeItem != item){ - if(this.activeItem){ - this.activeItem.hide(); + if(ai != item){ + if(ai){ + ai.hide(); + ai.fireEvent('deactivate', ai); } + var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered); this.activeItem = item; - item.show(); - this.container.doLayout(); - if(this.layoutOnCardChange && item.doLayout){ + if(item){ + item.show(); + } + this.layout(); + if(item && layout){ item.doLayout(); } + item.fireEvent('activate', item); } }, @@ -4780,200 +5214,203 @@ Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, { } } }); -Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/** - * @class Ext.layout.AnchorLayout - * @extends Ext.layout.ContainerLayout - *

      This is a layout that enables anchoring of contained elements relative to the container's dimensions. - * If the container is resized, all anchored items are automatically rerendered according to their - * {@link #anchor} rules.

      - *

      This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout} - * config, and should generally not need to be created directly via the new keyword.

      - *

      AnchorLayout does not have any direct config options (other than inherited ones). By default, - * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the - * container using the AnchorLayout can supply an anchoring-specific config property of anchorSize. - * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating - * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring - * logic if necessary. For example:

      - *
      
      -var viewport = new Ext.Viewport({
      -    layout:'anchor',
      -    anchorSize: {width:800, height:600},
      -    items:[{
      -        title:'Item 1',
      -        html:'Content 1',
      -        width:800,
      -        anchor:'right 20%'
      -    },{
      -        title:'Item 2',
      -        html:'Content 2',
      -        width:300,
      -        anchor:'50% 30%'
      -    },{
      -        title:'Item 3',
      -        html:'Content 3',
      -        width:600,
      -        anchor:'-100 50%'
      -    }]
      -});
      - * 
      - */ -Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, { - /** - * @cfg {String} anchor - *

      This configuation option is to be applied to child items of a container managed by - * this layout (ie. configured with layout:'anchor').


      - * - *

      This value is what tells the layout how an item should be anchored to the container. items - * added to an AnchorLayout accept an anchoring-specific config property of anchor which is a string - * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%'). - * The following types of anchor values are supported:

        - * - *
      • Percentage : Any value between 1 and 100, expressed as a percentage.
        - * The first anchor is the percentage width that the item should take up within the container, and the - * second is the percentage height. For example:
        
        -// two values specified
        -anchor: '100% 50%' // render item complete width of the container and
        -                   // 1/2 height of the container
        -// one value specified
        -anchor: '100%'     // the width value; the height will default to auto
        -     * 
      • - * - *
      • Offsets : Any positive or negative integer value.
        - * This is a raw adjustment where the first anchor is the offset from the right edge of the container, - * and the second is the offset from the bottom edge. For example:
        
        -// two values specified
        -anchor: '-50 -100' // render item the complete width of the container
        -                   // minus 50 pixels and
        -                   // the complete height minus 100 pixels.
        -// one value specified
        -anchor: '-50'      // anchor value is assumed to be the right offset value
        -                   // bottom offset will default to 0
        -     * 
      • - * - *
      • Sides : Valid values are 'right' (or 'r') and 'bottom' - * (or 'b').
        - * Either the container must have a fixed size or an anchorSize config value defined at render time in - * order for these to have any effect.
      • - * - *
      • Mixed :
        - * Anchor values can also be mixed as needed. For example, to render the width offset from the container - * right edge by 50 pixels and 75% of the container's height use: - *
        
        -anchor: '-50 75%' 
        -     * 
      • - * - * - *
      - */ - - // private - monitorResize:true, - - // private - getAnchorViewSize : function(ct, target){ - return target.dom == document.body ? - target.getViewSize() : target.getStyleSize(); - }, - - // private - onLayout : function(ct, target){ - Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target); - - var size = this.getAnchorViewSize(ct, target); - - var w = size.width, h = size.height; - - if(w < 20 && h < 20){ - return; - } - - // find the container anchoring size - var aw, ah; - if(ct.anchorSize){ - if(typeof ct.anchorSize == 'number'){ - aw = ct.anchorSize; - }else{ - aw = ct.anchorSize.width; - ah = ct.anchorSize.height; - } - }else{ - aw = ct.initialConfig.width; - ah = ct.initialConfig.height; - } - - var cs = ct.items.items, len = cs.length, i, c, a, cw, ch; - for(i = 0; i < len; i++){ - c = cs[i]; - if(c.anchor){ - a = c.anchorSpec; - if(!a){ // cache all anchor values - var vs = c.anchor.split(' '); - c.anchorSpec = a = { - right: this.parseAnchor(vs[0], c.initialConfig.width, aw), - bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah) - }; - } - cw = a.right ? this.adjustWidthAnchor(a.right(w), c) : undefined; - ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h), c) : undefined; - - if(cw || ch){ - c.setSize(cw || undefined, ch || undefined); - } - } - } - }, - - // private - parseAnchor : function(a, start, cstart){ - if(a && a != 'none'){ - var last; - if(/^(r|right|b|bottom)$/i.test(a)){ // standard anchor - var diff = cstart - start; - return function(v){ - if(v !== last){ - last = v; - return v - diff; - } - } - }else if(a.indexOf('%') != -1){ - var ratio = parseFloat(a.replace('%', ''))*.01; // percentage - return function(v){ - if(v !== last){ - last = v; - return Math.floor(v*ratio); - } - } - }else{ - a = parseInt(a, 10); - if(!isNaN(a)){ // simple offset adjustment - return function(v){ - if(v !== last){ - last = v; - return v + a; - } - } - } - } - } - return false; - }, - - // private - adjustWidthAnchor : function(value, comp){ - return value; - }, - - // private - adjustHeightAnchor : function(value, comp){ - return value; - } - - /** - * @property activeItem - * @hide - */ -}); -Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;/** +Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/** + * @class Ext.layout.AnchorLayout + * @extends Ext.layout.ContainerLayout + *

      This is a layout that enables anchoring of contained elements relative to the container's dimensions. + * If the container is resized, all anchored items are automatically rerendered according to their + * {@link #anchor} rules.

      + *

      This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.

      + *

      AnchorLayout does not have any direct config options (other than inherited ones). By default, + * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the + * container using the AnchorLayout can supply an anchoring-specific config property of anchorSize. + * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating + * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring + * logic if necessary. For example:

      + *
      
      +var viewport = new Ext.Viewport({
      +    layout:'anchor',
      +    anchorSize: {width:800, height:600},
      +    items:[{
      +        title:'Item 1',
      +        html:'Content 1',
      +        width:800,
      +        anchor:'right 20%'
      +    },{
      +        title:'Item 2',
      +        html:'Content 2',
      +        width:300,
      +        anchor:'50% 30%'
      +    },{
      +        title:'Item 3',
      +        html:'Content 3',
      +        width:600,
      +        anchor:'-100 50%'
      +    }]
      +});
      + * 
      + */ +Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, { + /** + * @cfg {String} anchor + *

      This configuation option is to be applied to child items of a container managed by + * this layout (ie. configured with layout:'anchor').


      + * + *

      This value is what tells the layout how an item should be anchored to the container. items + * added to an AnchorLayout accept an anchoring-specific config property of anchor which is a string + * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%'). + * The following types of anchor values are supported:

        + * + *
      • Percentage : Any value between 1 and 100, expressed as a percentage.
        + * The first anchor is the percentage width that the item should take up within the container, and the + * second is the percentage height. For example:
        
        +// two values specified
        +anchor: '100% 50%' // render item complete width of the container and
        +                   // 1/2 height of the container
        +// one value specified
        +anchor: '100%'     // the width value; the height will default to auto
        +     * 
      • + * + *
      • Offsets : Any positive or negative integer value.
        + * This is a raw adjustment where the first anchor is the offset from the right edge of the container, + * and the second is the offset from the bottom edge. For example:
        
        +// two values specified
        +anchor: '-50 -100' // render item the complete width of the container
        +                   // minus 50 pixels and
        +                   // the complete height minus 100 pixels.
        +// one value specified
        +anchor: '-50'      // anchor value is assumed to be the right offset value
        +                   // bottom offset will default to 0
        +     * 
      • + * + *
      • Sides : Valid values are 'right' (or 'r') and 'bottom' + * (or 'b').
        + * Either the container must have a fixed size or an anchorSize config value defined at render time in + * order for these to have any effect.
      • + * + *
      • Mixed :
        + * Anchor values can also be mixed as needed. For example, to render the width offset from the container + * right edge by 50 pixels and 75% of the container's height use: + *
        
        +anchor: '-50 75%'
        +     * 
      • + * + * + *
      + */ + + // private + monitorResize:true, + + // private + // deprecate + getAnchorViewSize : function(ct, target){ + return target.dom == document.body ? + target.getViewSize(true) : target.getStyleSize(); + }, + + // private + onLayout : function(ct, target){ + Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target); + + var size = target.getViewSize(true); + + var w = size.width, h = size.height; + + if(w < 20 && h < 20){ + return; + } + + // find the container anchoring size + var aw, ah; + if(ct.anchorSize){ + if(typeof ct.anchorSize == 'number'){ + aw = ct.anchorSize; + }else{ + aw = ct.anchorSize.width; + ah = ct.anchorSize.height; + } + }else{ + aw = ct.initialConfig.width; + ah = ct.initialConfig.height; + } + + var cs = ct.items.items, len = cs.length, i, c, a, cw, ch, el, vs; + for(i = 0; i < len; i++){ + c = cs[i]; + el = c.getPositionEl(); + if(c.anchor){ + a = c.anchorSpec; + if(!a){ // cache all anchor values + vs = c.anchor.split(' '); + c.anchorSpec = a = { + right: this.parseAnchor(vs[0], c.initialConfig.width, aw), + bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah) + }; + } + cw = a.right ? this.adjustWidthAnchor(a.right(w) - el.getMargins('lr'), c) : undefined; + ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h) - el.getMargins('tb'), c) : undefined; + + if(cw || ch){ + c.setSize(cw || undefined, ch || undefined); + } + } + } + }, + + // private + parseAnchor : function(a, start, cstart){ + if(a && a != 'none'){ + var last; + if(/^(r|right|b|bottom)$/i.test(a)){ // standard anchor + var diff = cstart - start; + return function(v){ + if(v !== last){ + last = v; + return v - diff; + } + } + }else if(a.indexOf('%') != -1){ + var ratio = parseFloat(a.replace('%', ''))*.01; // percentage + return function(v){ + if(v !== last){ + last = v; + return Math.floor(v*ratio); + } + } + }else{ + a = parseInt(a, 10); + if(!isNaN(a)){ // simple offset adjustment + return function(v){ + if(v !== last){ + last = v; + return v + a; + } + } + } + } + } + return false; + }, + + // private + adjustWidthAnchor : function(value, comp){ + return value; + }, + + // private + adjustHeightAnchor : function(value, comp){ + return value; + } + + /** + * @property activeItem + * @hide + */ +}); +Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout; +/** * @class Ext.layout.ColumnLayout * @extends Ext.layout.ContainerLayout *

      This is the layout style of choice for creating structural layouts in a multi-column format where the width of @@ -5002,7 +5439,7 @@ var p = new Ext.Panel({ layout:'column', items: [{ title: 'Column 1', - columnWidth: .25 + columnWidth: .25 },{ title: 'Column 2', columnWidth: .6 @@ -5034,14 +5471,17 @@ var p = new Ext.Panel({ Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, { // private monitorResize:true, - + extraCls: 'x-column', scrollOffset : 0, // private + + targetCls: 'x-column-layout-ct', + isValidParent : function(c, target){ - return (c.getPositionEl ? c.getPositionEl() : c.getEl()).dom.parentNode == this.innerCt.dom; + return c.getPositionEl().dom.parentNode == this.innerCt.dom; }, // private @@ -5049,8 +5489,6 @@ Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, { var cs = ct.items.items, len = cs.length, c, i; if(!this.innerCt){ - target.addClass('x-column-layout-ct'); - // the innerCt prevents wrapping and shuffling while // the container is resizing this.innerCt = target.createChild({cls:'x-column-inner'}); @@ -5058,25 +5496,25 @@ Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, { } this.renderAll(ct, this.innerCt); - var size = Ext.isIE && target.dom != Ext.getBody().dom ? target.getStyleSize() : target.getViewSize(); + var size = target.getViewSize(true); if(size.width < 1 && size.height < 1){ // display none? return; } - var w = size.width - target.getPadding('lr') - this.scrollOffset, - h = size.height - target.getPadding('tb'), + var w = size.width - this.scrollOffset, + h = size.height, pw = w; this.innerCt.setWidth(w); - + // some columns can be percentages while others are fixed // so we need to make 2 passes for(i = 0; i < len; i++){ c = cs[i]; if(!c.columnWidth){ - pw -= (c.getSize().width + c.getEl().getMargins('lr')); + pw -= (c.getSize().width + c.getPositionEl().getMargins('lr')); } } @@ -5085,11 +5523,11 @@ Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, { for(i = 0; i < len; i++){ c = cs[i]; if(c.columnWidth){ - c.setSize(Math.floor(c.columnWidth*pw) - c.getEl().getMargins('lr')); + c.setSize(Math.floor(c.columnWidth * pw) - c.getPositionEl().getMargins('lr')); } } } - + /** * @property activeItem * @hide @@ -5123,7 +5561,7 @@ var myBorderPanel = new Ext.Panel({ {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south', // position for region {@link Ext.BoxComponent#height height}: 100, {@link Ext.layout.BorderLayout.Region#split split}: true, // enable resizing - {@link Ext.SplitBar#minSize minSize}: 75, // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50} + {@link Ext.SplitBar#minSize minSize}: 75, // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50} {@link Ext.SplitBar#maxSize maxSize}: 150, {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5' },{ @@ -5181,11 +5619,12 @@ Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, { // private rendered : false, + targetCls: 'x-border-layout-ct', + // private onLayout : function(ct, target){ var collapsed; if(!this.rendered){ - target.addClass('x-border-layout-ct'); var items = ct.items.items; collapsed = []; for(var i = 0, len = items.length; i < len; i++) { @@ -5196,8 +5635,8 @@ Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, { } c.collapsed = false; if(!c.rendered){ - c.cls = c.cls ? c.cls +' x-border-panel' : 'x-border-panel'; c.render(target, i); + c.getPositionEl().addClass('x-border-panel'); } this[pos] = pos != 'center' && c.split ? new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) : @@ -5207,7 +5646,7 @@ Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, { this.rendered = true; } - var size = target.getViewSize(); + var size = target.getViewSize(false); if(size.width < 20 || size.height < 20){ // display none? if(collapsed){ this.restoreCollapsed = collapsed; @@ -5434,19 +5873,19 @@ Ext.layout.BorderLayout.Region.prototype = { collapsible : false, /** * @cfg {Boolean} split - *

      true to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and + *

      true to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to * resize the regions dynamically. Defaults to false creating a * {@link Ext.layout.BorderLayout.Region Region}.


      *

      Notes:

        - *
      • this configuration option is ignored if region='center'
      • + *
      • this configuration option is ignored if region='center'
      • *
      • when split == true, it is common to specify a * {@link Ext.SplitBar#minSize minSize} and {@link Ext.SplitBar#maxSize maxSize} * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.
      • *
      • if {@link #collapseMode} = 'mini' requires split = true to reserve space - * for the collapse tool
      • - *
      + * for the collapse tool
    • + *
    */ split:false, /** @@ -5460,8 +5899,8 @@ Ext.layout.BorderLayout.Region.prototype = { * @cfg {Number} minWidth *

    The minimum allowable width in pixels for this region (defaults to 50). * maxWidth may also be specified.


    - *

    Note: setting the {@link Ext.SplitBar#minSize minSize} / - * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified + *

    Note: setting the {@link Ext.SplitBar#minSize minSize} / + * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified * minWidth / maxWidth.

    */ minWidth:50, @@ -5469,8 +5908,8 @@ Ext.layout.BorderLayout.Region.prototype = { * @cfg {Number} minHeight * The minimum allowable height in pixels for this region (defaults to 50) * maxHeight may also be specified.


    - *

    Note: setting the {@link Ext.SplitBar#minSize minSize} / - * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified + *

    Note: setting the {@link Ext.SplitBar#minSize minSize} / + * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified * minHeight / maxHeight.

    */ minHeight:50, @@ -5585,7 +6024,6 @@ Ext.layout.BorderLayout.Region.prototype = { // private onExpandClick : function(e){ if(this.isSlid){ - this.afterSlideIn(); this.panel.expand(false); }else{ this.panel.expand(); @@ -5604,7 +6042,9 @@ Ext.layout.BorderLayout.Region.prototype = { this.splitEl.hide(); } this.getCollapsedEl().show(); - this.panel.el.setStyle('z-index', 100); + var el = this.panel.getEl(); + this.originalZIndex = el.getStyle('z-index'); + el.setStyle('z-index', 100); this.isCollapsed = true; this.layout.layout(); }, @@ -5623,6 +6063,9 @@ Ext.layout.BorderLayout.Region.prototype = { // private beforeExpand : function(animate){ + if(this.isSlid){ + this.afterSlideIn(); + } var c = this.getCollapsedEl(); this.el.show(); if(this.position == 'east' || this.position == 'west'){ @@ -5642,7 +6085,7 @@ Ext.layout.BorderLayout.Region.prototype = { this.splitEl.show(); } this.layout.layout(); - this.panel.el.setStyle('z-index', 1); + this.panel.el.setStyle('z-index', this.originalZIndex); this.state.collapsed = false; this.panel.saveState(); }, @@ -5774,6 +6217,7 @@ Ext.layout.BorderLayout.Region.prototype = { }; } this.el.on(this.autoHideHd); + this.collapsedEl.on(this.autoHideHd); } }, @@ -5782,6 +6226,8 @@ Ext.layout.BorderLayout.Region.prototype = { if(this.autoHide !== false){ this.el.un("mouseout", this.autoHideHd.mouseout); this.el.un("mouseover", this.autoHideHd.mouseover); + this.collapsedEl.un("mouseout", this.autoHideHd.mouseout); + this.collapsedEl.un("mouseover", this.autoHideHd.mouseover); } }, @@ -5957,6 +6403,10 @@ Ext.layout.BorderLayout.Region.prototype = { return [0, cm.top+cm.bottom+c.getHeight()]; break; } + }, + + destroy : function(){ + Ext.destroy(this.miniCollapsedEl, this.collapsedEl); } }; @@ -6189,11 +6639,8 @@ Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, // inherit docs destroy : function() { - Ext.destroy( - this.miniSplitEl, - this.split, - this.splitEl - ); + Ext.destroy(this.miniSplitEl, this.split, this.splitEl); + Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this); } }); @@ -6308,6 +6755,34 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { * @property labelStyle */ + /** + * @cfg {Boolean} trackLabels + * True to show/hide the field label when the field is hidden. Defaults to false. + */ + trackLabels: false, + + + onRemove: function(c){ + Ext.layout.FormLayout.superclass.onRemove.call(this, c); + if(this.trackLabels){ + c.un('show', this.onFieldShow, this); + c.un('hide', this.onFieldHide, this); + } + // check for itemCt, since we may be removing a fieldset or something similar + var el = c.getPositionEl(), + ct = c.getItemCt && c.getItemCt(); + if(c.rendered && ct){ + if (el && el.dom) { + el.insertAfter(ct); + } + Ext.destroy(ct); + Ext.destroyMembers(c, 'label', 'itemCt'); + if(c.customItemCt){ + Ext.destroyMembers(c, 'getItemCt', 'customItemCt'); + } + } + }, + // private setContainer : function(ct){ Ext.layout.FormLayout.superclass.setContainer.call(this, ct); @@ -6316,26 +6791,45 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { } if(ct.hideLabels){ - this.labelStyle = "display:none"; - this.elementStyle = "padding-left:0;"; - this.labelAdjust = 0; + Ext.apply(this, { + labelStyle: 'display:none', + elementStyle: 'padding-left:0;', + labelAdjust: 0 + }); }else{ this.labelSeparator = ct.labelSeparator || this.labelSeparator; ct.labelWidth = ct.labelWidth || 100; - if(typeof ct.labelWidth == 'number'){ - var pad = (typeof ct.labelPad == 'number' ? ct.labelPad : 5); - this.labelAdjust = ct.labelWidth+pad; - this.labelStyle = "width:"+ct.labelWidth+"px;"; - this.elementStyle = "padding-left:"+(ct.labelWidth+pad)+'px'; + if(Ext.isNumber(ct.labelWidth)){ + var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5; + Ext.apply(this, { + labelAdjust: ct.labelWidth + pad, + labelStyle: 'width:' + ct.labelWidth + 'px;', + elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px' + }); } if(ct.labelAlign == 'top'){ - this.labelStyle = "width:auto;"; - this.labelAdjust = 0; - this.elementStyle = "padding-left:0;"; + Ext.apply(this, { + labelStyle: 'width:auto;', + labelAdjust: 0, + elementStyle: 'padding-left:0;' + }); } } }, + // private + isHide: function(c){ + return c.hideLabel || this.container.hideLabels; + }, + + onFieldShow: function(c){ + c.getItemCt().removeClass('x-hide-' + c.hideMode); + }, + + onFieldHide: function(c){ + c.getItemCt().addClass('x-hide-' + c.hideMode); + }, + //private getLabelStyle: function(s){ var ls = '', items = [this.labelStyle, s]; @@ -6386,17 +6880,43 @@ new Ext.Template( // private renderItem : function(c, position, target){ - if(c && !c.rendered && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){ + if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){ var args = this.getTemplateArgs(c); - if(typeof position == 'number'){ + if(Ext.isNumber(position)){ position = target.dom.childNodes[position] || null; } if(position){ - this.fieldTpl.insertBefore(position, args); + c.itemCt = this.fieldTpl.insertBefore(position, args, true); }else{ - this.fieldTpl.append(target, args); + c.itemCt = this.fieldTpl.append(target, args, true); + } + if(!c.getItemCt){ + // Non form fields don't have getItemCt, apply it here + // This will get cleaned up in onRemove + Ext.apply(c, { + getItemCt: function(){ + return c.itemCt; + }, + customItemCt: true + }); } - c.render('x-form-el-'+c.id); + c.label = c.getItemCt().child('label.x-form-item-label'); + if(!c.rendered){ + c.render('x-form-el-' + c.id); + }else if(!this.isValidParent(c, target)){ + Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl()); + } + if(this.trackLabels){ + if(c.hidden){ + this.onFieldHide(c); + } + c.on({ + scope: this, + show: this.onFieldShow, + hide: this.onFieldHide + }); + } + this.configureItem(c); }else { Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments); } @@ -6424,7 +6944,7 @@ new Ext.Template( *
  • clearCls : String
    The CSS class to apply to the special clearing div * rendered directly after each form field wrapper (defaults to 'x-form-clear-left')
  • * - * @param field The {@link Field Ext.form.Field} being rendered. + * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered. * @return An object hash containing the properties required to render the Field. */ getTemplateArgs: function(field) { @@ -6432,22 +6952,33 @@ new Ext.Template( return { id: field.id, label: field.fieldLabel, - labelStyle: field.labelStyle||this.labelStyle||'', + labelStyle: this.getLabelStyle(field.labelStyle), elementStyle: this.elementStyle||'', - labelSeparator: noLabelSep ? '' : (typeof field.labelSeparator == 'undefined' ? this.labelSeparator : field.labelSeparator), + labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator), itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''), clearCls: field.clearCls || 'x-form-clear-left' }; }, // private - adjustWidthAnchor : function(value, comp){ - return value - (comp.isFormField || comp.fieldLabel ? (comp.hideLabel ? 0 : this.labelAdjust) : 0); + adjustWidthAnchor: function(value, c){ + if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){ + var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict); + return value - this.labelAdjust + (adjust ? -3 : 0); + } + return value; + }, + + adjustHeightAnchor : function(value, c){ + if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){ + return value - c.label.getHeight(); + } + return value; }, // private isValidParent : function(c, target){ - return true; + return target && this.container.getEl().contains(c.getPositionEl()); } /** @@ -6456,11 +6987,13 @@ new Ext.Template( */ }); -Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;/** +Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout; +/** * @class Ext.layout.AccordionLayout * @extends Ext.layout.FitLayout - *

    This is a layout that contains multiple panels in an expandable accordion style such that only - * one panel can be open at any given time. Each panel has built-in support for expanding and collapsing. + *

    This is a layout that manages multiple Panels in an expandable accordion style such that only + * one Panel can be expanded at any given time. Each Panel has built-in support for expanding and collapsing.

    + *

    Note: Only Ext.Panels and all subclasses of Ext.Panel may be used in an accordion layout Container.

    *

    This class is intended to be extended or created via the {@link Ext.Container#layout layout} * configuration property. See {@link Ext.Container#layout} for additional details.

    *

    Example usage:

    @@ -6561,7 +7094,7 @@ Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, { c.collapseFirst = this.collapseFirst; } if(!this.activeItem && !c.collapsed){ - this.activeItem = c; + this.setActiveItem(c, true); }else if(this.activeItem && this.activeItem != c){ c.collapsed = true; } @@ -6569,6 +7102,14 @@ Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, { c.header.addClass('x-accordion-hd'); c.on('beforeexpand', this.beforeExpand, this); }, + + onRemove: function(c){ + Ext.layout.AccordionLayout.superclass.onRemove.call(this, c); + if(c.rendered){ + c.header.removeClass('x-accordion-hd'); + } + c.un('beforeexpand', this.beforeExpand, this); + }, // private beforeExpand : function(p, anim){ @@ -6586,7 +7127,7 @@ Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, { ai.collapse(this.animate); } } - this.activeItem = p; + this.setActive(p); if(this.activeOnTop){ p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild); } @@ -6612,15 +7153,24 @@ Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, { * @param {String/Number} item The string component id or numeric index of the item to activate */ setActiveItem : function(item){ + this.setActive(item, true); + }, + + // private + setActive : function(item, expand){ + var ai = this.activeItem; item = this.container.getComponent(item); - if(this.activeItem != item){ - if(item.rendered && item.collapsed){ + if(ai != item){ + if(item.rendered && item.collapsed && expand){ item.expand(); }else{ + if(ai){ + ai.fireEvent('deactivate', ai); + } this.activeItem = item; + item.fireEvent('activate', item); } } - } }); Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout; @@ -6694,6 +7244,8 @@ Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { // private monitorResize:false, + + targetCls: 'x-table-layout-ct', /** * @cfg {Object} tableAttrs @@ -6728,8 +7280,6 @@ Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { var cs = ct.items.items, len = cs.length, c, i; if(!this.table){ - target.addClass('x-table-layout-ct'); - this.table = target.createChild( Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true); } @@ -6795,16 +7345,18 @@ Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { renderItem : function(c, position, target){ if(c && !c.rendered){ c.render(this.getNextCell(c)); - if(this.extraCls){ - var t = c.getPositionEl ? c.getPositionEl() : c; - t.addClass(this.extraCls); - } + this.configureItem(c, position); + }else if(c && !this.isValidParent(c, target)){ + var container = this.getNextCell(c); + container.insertBefore(c.getPositionEl().dom, null); + c.container = Ext.get(container); + this.configureItem(c, position); } }, // private isValidParent : function(c, target){ - return true; + return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target); } /** @@ -6889,416 +7441,441 @@ Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, { */ }); Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout; -/** - * @class Ext.layout.BoxLayout - * @extends Ext.layout.ContainerLayout - *

    Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.

    - */ -Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, { - /** - * @cfg {Object} defaultMargins - *

    If the individual contained items do not have a margins - * property specified, the default margins from this property will be - * applied to each item.

    - *

    This property may be specified as an object containing margins - * to apply in the format:

    
    -{
    -    top: (top margin),
    -    right: (right margin),
    -    bottom: (bottom margin),
    -    left: (left margin)
    -}
    - *

    This property may also be specified as a string containing - * space-separated, numeric margin values. The order of the sides associated - * with each value matches the way CSS processes margin values:

    - *
      - *
    • If there is only one value, it applies to all sides.
    • - *
    • If there are two values, the top and bottom borders are set to the - * first value and the right and left are set to the second.
    • - *
    • If there are three values, the top is set to the first value, the left - * and right are set to the second, and the bottom is set to the third.
    • - *
    • If there are four values, they apply to the top, right, bottom, and - * left, respectively.
    • - *
    - *

    Defaults to:

    
    -     * {top:0, right:0, bottom:0, left:0}
    -     * 
    - */ - defaultMargins : {left:0,top:0,right:0,bottom:0}, - /** - * @cfg {String} padding - * Defaults to '0'. Sets the padding to be applied to all child items managed by this - * container's layout. - */ - padding : '0', - // documented in subclasses - pack : 'start', - - // private - monitorResize : true, - scrollOffset : 0, - extraCls : 'x-box-item', - ctCls : 'x-box-layout-ct', - innerCls : 'x-box-inner', - - // private - isValidParent : function(c, target){ - return c.getEl().dom.parentNode == this.innerCt.dom; - }, - - // private - onLayout : function(ct, target){ - var cs = ct.items.items, len = cs.length, c, i, last = len-1, cm; - - if(!this.innerCt){ - target.addClass(this.ctCls); - - // the innerCt prevents wrapping and shuffling while - // the container is resizing - this.innerCt = target.createChild({cls:this.innerCls}); - this.padding = this.parseMargins(this.padding); - } - this.renderAll(ct, this.innerCt); - }, - - // private - renderItem : function(c){ - if(typeof c.margins == 'string'){ - c.margins = this.parseMargins(c.margins); - }else if(!c.margins){ - c.margins = this.defaultMargins; - } - Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments); - }, - +/** + * @class Ext.layout.BoxLayout + * @extends Ext.layout.ContainerLayout + *

    Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.

    + */ +Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, { + /** + * @cfg {Object} defaultMargins + *

    If the individual contained items do not have a margins + * property specified, the default margins from this property will be + * applied to each item.

    + *

    This property may be specified as an object containing margins + * to apply in the format:

    
    +{
    +    top: (top margin),
    +    right: (right margin),
    +    bottom: (bottom margin),
    +    left: (left margin)
    +}
    + *

    This property may also be specified as a string containing + * space-separated, numeric margin values. The order of the sides associated + * with each value matches the way CSS processes margin values:

    + *
      + *
    • If there is only one value, it applies to all sides.
    • + *
    • If there are two values, the top and bottom borders are set to the + * first value and the right and left are set to the second.
    • + *
    • If there are three values, the top is set to the first value, the left + * and right are set to the second, and the bottom is set to the third.
    • + *
    • If there are four values, they apply to the top, right, bottom, and + * left, respectively.
    • + *
    + *

    Defaults to:

    
    +     * {top:0, right:0, bottom:0, left:0}
    +     * 
    + */ + defaultMargins : {left:0,top:0,right:0,bottom:0}, + /** + * @cfg {String} padding + *

    Sets the padding to be applied to all child items managed by this layout.

    + *

    This property must be specified as a string containing + * space-separated, numeric padding values. The order of the sides associated + * with each value matches the way CSS processes padding values:

    + *
      + *
    • If there is only one value, it applies to all sides.
    • + *
    • If there are two values, the top and bottom borders are set to the + * first value and the right and left are set to the second.
    • + *
    • If there are three values, the top is set to the first value, the left + * and right are set to the second, and the bottom is set to the third.
    • + *
    • If there are four values, they apply to the top, right, bottom, and + * left, respectively.
    • + *
    + *

    Defaults to: "0"

    + */ + padding : '0', + // documented in subclasses + pack : 'start', + + // private + monitorResize : true, + scrollOffset : 0, + extraCls : 'x-box-item', + targetCls : 'x-box-layout-ct', + innerCls : 'x-box-inner', + + constructor : function(config){ + Ext.layout.BoxLayout.superclass.constructor.call(this, config); + if(Ext.isString(this.defaultMargins)){ + this.defaultMargins = this.parseMargins(this.defaultMargins); + } + }, + + // private + isValidParent : function(c, target){ + return c.getPositionEl().dom.parentNode == this.innerCt.dom; + }, + + // private + onLayout : function(ct, target){ + var cs = ct.items.items, len = cs.length, c, i, last = len-1, cm; + + if(!this.innerCt){ + // the innerCt prevents wrapping and shuffling while + // the container is resizing + this.innerCt = target.createChild({cls:this.innerCls}); + this.padding = this.parseMargins(this.padding); + } + this.renderAll(ct, this.innerCt); + }, + + // private + renderItem : function(c){ + if(Ext.isString(c.margins)){ + c.margins = this.parseMargins(c.margins); + }else if(!c.margins){ + c.margins = this.defaultMargins; + } + Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments); + }, + + // deprecate getTargetSize : function(target){ - return (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getStyleSize() : target.getViewSize(); - }, - - getItems: function(ct){ - var items = []; - ct.items.each(function(c){ - if(c.isVisible()){ - items.push(c); - } - }); - return items; - } - - /** - * @property activeItem - * @hide - */ -}); - -/** - * @class Ext.layout.VBoxLayout - * @extends Ext.layout.BoxLayout - * A layout that arranges items vertically - */ -Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, { - /** - * @cfg {String} align - * Controls how the child items of the container are aligned. Acceptable configuration values for this - * property are: - *
      - *
    • left : Default
      child items are aligned horizontally - * at the left side of the container
    • - *
    • center :
      child items are aligned horizontally at the - * mid-width of the container
    • - *
    • stretch :
      child items are stretched horizontally to fill - * the width of the container
    • - *
    • stretchmax :
      child items are stretched horizontally to - * the size of the largest item.
    • - *
    - */ - align : 'left', // left, center, stretch, strechmax - /** - * @cfg {String} pack - * Controls how the child items of the container are packed together. Acceptable configuration values - * for this property are: - *
      - *
    • start : Default
      child items are packed together at - * top side of container
    • - *
    • center :
      child items are packed together at - * mid-height of container
    • - *
    • end :
      child items are packed together at bottom - * side of container
    • - *
    - */ - /** - * @cfg {Number} flex - * This configuation option is to be applied to child items of the container managed - * by this layout. Each child item with a flex property will be flexed vertically - * according to each item's relative flex value compared to the sum of all items with - * a flex value specified. Any child items that have either a flex = 0 or - * flex = undefined will not be 'flexed' (the initial size will not be changed). - */ - - // private - onLayout : function(ct, target){ - Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target); - - - var cs = this.getItems(ct), cm, ch, margin, - size = this.getTargetSize(target), - w = size.width - target.getPadding('lr') - this.scrollOffset, - h = size.height - target.getPadding('tb'), - l = this.padding.left, t = this.padding.top, - isStart = this.pack == 'start', - isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1, - stretchWidth = w - (this.padding.left + this.padding.right), - extraHeight = 0, - maxWidth = 0, - totalFlex = 0, - flexHeight = 0, - usedHeight = 0; - - Ext.each(cs, function(c){ - cm = c.margins; - totalFlex += c.flex || 0; - ch = c.getHeight(); - margin = cm.top + cm.bottom; - extraHeight += ch + margin; - flexHeight += margin + (c.flex ? 0 : ch); - maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right); - }); - extraHeight = h - extraHeight - this.padding.top - this.padding.bottom; - - var innerCtWidth = maxWidth + this.padding.left + this.padding.right; - switch(this.align){ - case 'stretch': - this.innerCt.setSize(w, h); - break; - case 'stretchmax': - case 'left': - this.innerCt.setSize(innerCtWidth, h); - break; - case 'center': - this.innerCt.setSize(w = Math.max(w, innerCtWidth), h); - break; - } - - var availHeight = Math.max(0, h - this.padding.top - this.padding.bottom - flexHeight), - leftOver = availHeight, - heights = [], - restore = [], - idx = 0, - availableWidth = Math.max(0, w - this.padding.left - this.padding.right); - - - Ext.each(cs, function(c){ - if(isStart && c.flex){ - ch = Math.floor(availHeight * (c.flex / totalFlex)); - leftOver -= ch; - heights.push(ch); - } - }); - - if(this.pack == 'center'){ - t += extraHeight ? extraHeight / 2 : 0; - }else if(this.pack == 'end'){ - t += extraHeight; - } - Ext.each(cs, function(c){ - cm = c.margins; - t += cm.top; - c.setPosition(l + cm.left, t); - if(isStart && c.flex){ - ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0)); - if(isRestore){ - restore.push(c.getWidth()); - } - c.setSize(availableWidth, ch); - }else{ - ch = c.getHeight(); - } - t += ch + cm.bottom; - }); - - idx = 0; - Ext.each(cs, function(c){ - cm = c.margins; - if(this.align == 'stretch'){ - c.setWidth((stretchWidth - (cm.left + cm.right)).constrain( - c.minWidth || 0, c.maxWidth || 1000000)); - }else if(this.align == 'stretchmax'){ - c.setWidth((maxWidth - (cm.left + cm.right)).constrain( - c.minWidth || 0, c.maxWidth || 1000000)); - }else{ - if(this.align == 'center'){ - var diff = availableWidth - (c.getWidth() + cm.left + cm.right); - if(diff > 0){ - c.setPosition(l + cm.left + (diff/2), c.y); - } - } - if(isStart && c.flex){ - c.setWidth(restore[idx++]); - } - } - }, this); - } - /** - * @property activeItem - * @hide - */ -}); - -Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout; - -/** - * @class Ext.layout.HBoxLayout - * @extends Ext.layout.BoxLayout - * A layout that arranges items horizontally - */ -Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, { - /** - * @cfg {String} align - * Controls how the child items of the container are aligned. Acceptable configuration values for this - * property are: - *
      - *
    • top : Default
      child items are aligned vertically - * at the left side of the container
    • - *
    • middle :
      child items are aligned vertically at the - * mid-height of the container
    • - *
    • stretch :
      child items are stretched vertically to fill - * the height of the container
    • - *
    • stretchmax :
      child items are stretched vertically to - * the size of the largest item.
    • - */ - align : 'top', // top, middle, stretch, strechmax - /** - * @cfg {String} pack - * Controls how the child items of the container are packed together. Acceptable configuration values - * for this property are: - *
        - *
      • start : Default
        child items are packed together at - * left side of container
      • - *
      • center :
        child items are packed together at - * mid-width of container
      • - *
      • end :
        child items are packed together at right - * side of container
      • - *
      - */ - /** - * @cfg {Number} flex - * This configuation option is to be applied to child items of the container managed - * by this layout. Each child item with a flex property will be flexed horizontally - * according to each item's relative flex value compared to the sum of all items with - * a flex value specified. Any child items that have either a flex = 0 or - * flex = undefined will not be 'flexed' (the initial size will not be changed). - */ - - // private - onLayout : function(ct, target){ - Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target); - - var cs = this.getItems(ct), cm, cw, margin, - size = this.getTargetSize(target), - w = size.width - target.getPadding('lr') - this.scrollOffset, - h = size.height - target.getPadding('tb'), - l = this.padding.left, t = this.padding.top, - isStart = this.pack == 'start', - isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1, - stretchHeight = h - (this.padding.top + this.padding.bottom), - extraWidth = 0, - maxHeight = 0, - totalFlex = 0, - flexWidth = 0, - usedWidth = 0; - - Ext.each(cs, function(c){ - cm = c.margins; - totalFlex += c.flex || 0; - cw = c.getWidth(); - margin = cm.left + cm.right; - extraWidth += cw + margin; - flexWidth += margin + (c.flex ? 0 : cw); - maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom); - }); - extraWidth = w - extraWidth - this.padding.left - this.padding.right; - - var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom; - switch(this.align){ - case 'stretch': - this.innerCt.setSize(w, h); - break; - case 'stretchmax': - case 'top': - this.innerCt.setSize(w, innerCtHeight); - break; - case 'middle': - this.innerCt.setSize(w, h = Math.max(h, innerCtHeight)); - break; - } - - - var availWidth = Math.max(0, w - this.padding.left - this.padding.right - flexWidth), - leftOver = availWidth, - widths = [], - restore = [], - idx = 0, - availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom); - - - Ext.each(cs, function(c){ - if(isStart && c.flex){ - cw = Math.floor(availWidth * (c.flex / totalFlex)); - leftOver -= cw; - widths.push(cw); - } - }); - - if(this.pack == 'center'){ - l += extraWidth ? extraWidth / 2 : 0; - }else if(this.pack == 'end'){ - l += extraWidth; - } - Ext.each(cs, function(c){ - cm = c.margins; - l += cm.left; - c.setPosition(l, t + cm.top); - if(isStart && c.flex){ - cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0)); - if(isRestore){ - restore.push(c.getHeight()); - } - c.setSize(cw, availableHeight); - }else{ - cw = c.getWidth(); - } - l += cw + cm.right; - }); - - idx = 0; - Ext.each(cs, function(c){ - var cm = c.margins; - if(this.align == 'stretch'){ - c.setHeight((stretchHeight - (cm.top + cm.bottom)).constrain( - c.minHeight || 0, c.maxHeight || 1000000)); - }else if(this.align == 'stretchmax'){ - c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain( - c.minHeight || 0, c.maxHeight || 1000000)); - }else{ - if(this.align == 'middle'){ - var diff = availableHeight - (c.getHeight() + cm.top + cm.bottom); - if(diff > 0){ - c.setPosition(c.x, t + cm.top + (diff/2)); - } - } - if(isStart && c.flex){ - c.setHeight(restore[idx++]); - } - } - }, this); - } - - /** - * @property activeItem - * @hide - */ -}); - + return (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getStyleSize() : target.getViewSize(true); + }, + + getItems: function(ct){ + var items = []; + ct.items.each(function(c){ + if(c.isVisible()){ + items.push(c); + } + }); + return items; + } +}); + +/** + * @class Ext.layout.VBoxLayout + * @extends Ext.layout.BoxLayout + *

      A layout that arranges items vertically down a Container. This layout optionally divides available vertical + * space between child items containing a numeric flex configuration.

      + * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option. + */ +Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, { + /** + * @cfg {String} align + * Controls how the child items of the container are aligned. Acceptable configuration values for this + * property are: + *
        + *
      • left : Default
        child items are aligned horizontally + * at the left side of the container
      • + *
      • center :
        child items are aligned horizontally at the + * mid-width of the container
      • + *
      • stretch :
        child items are stretched horizontally to fill + * the width of the container
      • + *
      • stretchmax :
        child items are stretched horizontally to + * the size of the largest item.
      • + *
      + */ + align : 'left', // left, center, stretch, strechmax + /** + * @cfg {String} pack + * Controls how the child items of the container are packed together. Acceptable configuration values + * for this property are: + *
        + *
      • start : Default
        child items are packed together at + * top side of container
      • + *
      • center :
        child items are packed together at + * mid-height of container
      • + *
      • end :
        child items are packed together at bottom + * side of container
      • + *
      + */ + /** + * @cfg {Number} flex + * This configuation option is to be applied to child items of the container managed + * by this layout. Each child item with a flex property will be flexed vertically + * according to each item's relative flex value compared to the sum of all items with + * a flex value specified. Any child items that have either a flex = 0 or + * flex = undefined will not be 'flexed' (the initial size will not be changed). + */ + + // private + onLayout : function(ct, target){ + Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target); + + var cs = this.getItems(ct), cm, ch, margin, cl, diff, aw, + size = target.getViewSize(true), + w = size.width, + h = size.height - this.scrollOffset, + l = this.padding.left, t = this.padding.top, + isStart = this.pack == 'start', + stretchWidth = w - (this.padding.left + this.padding.right), + extraHeight = 0, + maxWidth = 0, + totalFlex = 0, + flexHeight = 0, + usedHeight = 0, + idx = 0, + heights = [], + restore = [], + c, + csLen = cs.length; + + // Do only width calculations and apply those first, as they can affect height + for (i = 0 ; i < csLen; i++) { + c = cs[i]; + cm = c.margins; + margin = cm.top + cm.bottom; + maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right); + } + + var innerCtWidth = maxWidth + this.padding.left + this.padding.right; + switch(this.align){ + case 'stretch': + this.innerCt.setSize(w, h); + break; + case 'stretchmax': + case 'left': + this.innerCt.setSize(innerCtWidth, h); + break; + case 'center': + this.innerCt.setSize(w = Math.max(w, innerCtWidth), h); + break; + } + + var availableWidth = Math.max(0, w - this.padding.left - this.padding.right); + // Apply widths + for (i = 0 ; i < csLen; i++) { + c = cs[i]; + cm = c.margins; + if(this.align == 'stretch'){ + c.setWidth((stretchWidth - (cm.left + cm.right)).constrain( + c.minWidth || 0, c.maxWidth || 1000000)); + }else if(this.align == 'stretchmax'){ + c.setWidth((maxWidth - (cm.left + cm.right)).constrain( + c.minWidth || 0, c.maxWidth || 1000000)); + }else if(isStart && c.flex){ + c.setWidth(); + } + + } + + // Do height calculations + for (i = 0 ; i < csLen; i++) { + c = cs[i]; + cm = c.margins; + totalFlex += c.flex || 0; + ch = c.getHeight(); + margin = cm.top + cm.bottom; + extraHeight += ch + margin; + flexHeight += margin + (c.flex ? 0 : ch); + } + extraHeight = h - extraHeight - this.padding.top - this.padding.bottom; + + var availHeight = Math.max(0, h - this.padding.top - this.padding.bottom - flexHeight), + leftOver = availHeight; + for (i = 0 ; i < csLen; i++) { + c = cs[i]; + if(isStart && c.flex){ + ch = Math.floor(availHeight * (c.flex / totalFlex)); + leftOver -= ch; + heights.push(ch); + } + } + if(this.pack == 'center'){ + t += extraHeight ? extraHeight / 2 : 0; + }else if(this.pack == 'end'){ + t += extraHeight; + } + idx = 0; + // Apply heights + for (i = 0 ; i < csLen; i++) { + c = cs[i]; + cm = c.margins; + t += cm.top; + aw = availableWidth; + cl = l + cm.left // default left pos + +// Adjust left pos for centering + if(this.align == 'center'){ + if((diff = availableWidth - (c.getWidth() + cm.left + cm.right)) > 0){ + cl += (diff/2); + aw -= diff; + } + } + + c.setPosition(cl, t); + if(isStart && c.flex){ + ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0)); + c.setSize(aw, ch); + }else{ + ch = c.getHeight(); + } + t += ch + cm.bottom; + } + } +}); + +Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout; + +/** + * @class Ext.layout.HBoxLayout + * @extends Ext.layout.BoxLayout + *

      A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal + * space between child items containing a numeric flex configuration.

      + * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option. + */ +Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, { + /** + * @cfg {String} align + * Controls how the child items of the container are aligned. Acceptable configuration values for this + * property are: + *
        + *
      • top : Default
        child items are aligned vertically + * at the top of the container
      • + *
      • middle :
        child items are aligned vertically in the + * middle of the container
      • + *
      • stretch :
        child items are stretched vertically to fill + * the height of the container
      • + *
      • stretchmax :
        child items are stretched vertically to + * the height of the largest item.
      • + */ + align : 'top', // top, middle, stretch, strechmax + /** + * @cfg {String} pack + * Controls how the child items of the container are packed together. Acceptable configuration values + * for this property are: + *
          + *
        • start : Default
          child items are packed together at + * left side of container
        • + *
        • center :
          child items are packed together at + * mid-width of container
        • + *
        • end :
          child items are packed together at right + * side of container
        • + *
        + */ + /** + * @cfg {Number} flex + * This configuation option is to be applied to child items of the container managed + * by this layout. Each child item with a flex property will be flexed horizontally + * according to each item's relative flex value compared to the sum of all items with + * a flex value specified. Any child items that have either a flex = 0 or + * flex = undefined will not be 'flexed' (the initial size will not be changed). + */ + + // private + onLayout : function(ct, target){ + Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target); + + var cs = this.getItems(ct), cm, cw, margin, ch, diff, + size = target.getViewSize(true), + w = size.width - this.scrollOffset, + h = size.height, + l = this.padding.left, t = this.padding.top, + isStart = this.pack == 'start', + isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1, + stretchHeight = h - (this.padding.top + this.padding.bottom), + extraWidth = 0, + maxHeight = 0, + totalFlex = 0, + flexWidth = 0, + usedWidth = 0; + + Ext.each(cs, function(c){ + cm = c.margins; + totalFlex += c.flex || 0; + cw = c.getWidth(); + margin = cm.left + cm.right; + extraWidth += cw + margin; + flexWidth += margin + (c.flex ? 0 : cw); + maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom); + }); + extraWidth = w - extraWidth - this.padding.left - this.padding.right; + + var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom; + switch(this.align){ + case 'stretch': + this.innerCt.setSize(w, h); + break; + case 'stretchmax': + case 'top': + this.innerCt.setSize(w, innerCtHeight); + break; + case 'middle': + this.innerCt.setSize(w, h = Math.max(h, innerCtHeight)); + break; + } + + + var availWidth = Math.max(0, w - this.padding.left - this.padding.right - flexWidth), + leftOver = availWidth, + widths = [], + restore = [], + idx = 0, + availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom); + + + Ext.each(cs, function(c){ + if(isStart && c.flex){ + cw = Math.floor(availWidth * (c.flex / totalFlex)); + leftOver -= cw; + widths.push(cw); + } + }); + + if(this.pack == 'center'){ + l += extraWidth ? extraWidth / 2 : 0; + }else if(this.pack == 'end'){ + l += extraWidth; + } + Ext.each(cs, function(c){ + cm = c.margins; + l += cm.left; + c.setPosition(l, t + cm.top); + if(isStart && c.flex){ + cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0)); + if(isRestore){ + restore.push(c.getHeight()); + } + c.setSize(cw, availableHeight); + }else{ + cw = c.getWidth(); + } + l += cw + cm.right; + }); + + idx = 0; + Ext.each(cs, function(c){ + cm = c.margins; + ch = c.getHeight(); + if(isStart && c.flex){ + ch = restore[idx++]; + } + if(this.align == 'stretch'){ + c.setHeight((stretchHeight - (cm.top + cm.bottom)).constrain( + c.minHeight || 0, c.maxHeight || 1000000)); + }else if(this.align == 'stretchmax'){ + c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain( + c.minHeight || 0, c.maxHeight || 1000000)); + }else{ + if(this.align == 'middle'){ + diff = availableHeight - (ch + cm.top + cm.bottom); + ch = t + cm.top + (diff/2); + if(diff > 0){ + c.setPosition(c.x, ch); + } + } + if(isStart && c.flex){ + c.setHeight(ch); + } + } + }, this); + } +}); + Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout; /** * @class Ext.Viewport @@ -7325,7 +7902,7 @@ new Ext.Viewport({ collapsible: true, title: 'Navigation', width: 200 - // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout} + // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout} }, { region: 'south', title: 'Title for Panel', @@ -7360,44 +7937,45 @@ new Ext.Viewport({ * @xtype viewport */ Ext.Viewport = Ext.extend(Ext.Container, { - /* - * Privatize config options which, if used, would interfere with the - * correct operation of the Viewport as the sole manager of the - * layout of the document body. - */ + /* + * Privatize config options which, if used, would interfere with the + * correct operation of the Viewport as the sole manager of the + * layout of the document body. + */ /** * @cfg {Mixed} applyTo @hide - */ + */ /** * @cfg {Boolean} allowDomMove @hide - */ + */ /** * @cfg {Boolean} hideParent @hide - */ + */ /** * @cfg {Mixed} renderTo @hide - */ + */ /** * @cfg {Boolean} hideParent @hide - */ + */ /** * @cfg {Number} height @hide - */ + */ /** * @cfg {Number} width @hide - */ + */ /** * @cfg {Boolean} autoHeight @hide - */ + */ /** * @cfg {Boolean} autoWidth @hide - */ + */ /** * @cfg {Boolean} deferHeight @hide - */ + */ /** * @cfg {Boolean} monitorResize @hide - */ + */ + initComponent : function() { Ext.Viewport.superclass.initComponent.call(this); document.getElementsByTagName('html')[0].className += ' x-viewport'; @@ -7414,10 +7992,11 @@ Ext.Viewport = Ext.extend(Ext.Container, { }, fireResize : function(w, h){ - this.fireEvent('resize', this, w, h, w, h); + this.onResize(w, h, w, h); } }); -Ext.reg('viewport', Ext.Viewport);/** +Ext.reg('viewport', Ext.Viewport); +/** * @class Ext.Panel * @extends Ext.Container *

        Panel is a container that has specific functionality and structural components that make @@ -7426,7 +8005,7 @@ Ext.reg('viewport', Ext.Viewport);/** * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.

        *

        When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether - * those child elements need to be sized using one of Ext's built-in {@link Ext.Container#layout layout} schemes. By + * those child elements need to be sized using one of Ext's built-in {@link Ext.Container#layout layout} schemes. By * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders * child components, appending them one after the other inside the Container, and does not apply any sizing * at all.

        @@ -7445,7 +8024,7 @@ Ext.Panel = Ext.extend(Ext.Container, { /** * The Panel's header {@link Ext.Element Element}. Read-only. *

        This Element is used to house the {@link #title} and {@link #tools}

        - *

        Note: see the Note for {@link Ext.Component#el el} also.

        + *

        Note: see the Note for {@link Ext.Component#el el} also.

        * @type Ext.Element * @property header */ @@ -7458,7 +8037,7 @@ Ext.Panel = Ext.extend(Ext.Container, { *

        If this Panel is intended to be used as the host of a Layout (See {@link #layout} * then the body Element must not be loaded or changed - it is under the control * of the Panel's Layout. - *

        Note: see the Note for {@link Ext.Component#el el} also.

        + *

        Note: see the Note for {@link Ext.Component#el el} also.

        * @type Ext.Element * @property body */ @@ -7478,8 +8057,8 @@ Ext.Panel = Ext.extend(Ext.Container, { *

        A {@link Ext.DomHelper DomHelper} element specification object may be specified for any * Panel Element.

        *

        By default, the Default element in the table below will be used for the html markup to - * create a child element with the commensurate Default class name (baseCls will be - * replaced by {@link #baseCls}):

        + * create a child element with the commensurate Default class name (baseCls will be + * replaced by {@link #baseCls}):

        *
              * Panel      Default  Default             Custom      Additional       Additional
              * Element    element  class               element     class            style
        @@ -7515,51 +8094,51 @@ new Ext.Panel({
             footerStyle:    'background-color:red' // see {@link #bodyStyle}
         });
              * 
        - *

        The example above also explicitly creates a {@link #footer} with custom markup and + *

        The example above also explicitly creates a {@link #footer} with custom markup and * styling applied.

        */ /** * @cfg {Object} headerCfg *

        A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure - * of this Panel's {@link #header} Element. See {@link #bodyCfg} also.

        + * of this Panel's {@link #header} Element. See {@link #bodyCfg} also.

        */ /** * @cfg {Object} bwrapCfg *

        A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure - * of this Panel's {@link #bwrap} Element. See {@link #bodyCfg} also.

        + * of this Panel's {@link #bwrap} Element. See {@link #bodyCfg} also.

        */ /** * @cfg {Object} tbarCfg *

        A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure - * of this Panel's {@link #tbar} Element. See {@link #bodyCfg} also.

        + * of this Panel's {@link #tbar} Element. See {@link #bodyCfg} also.

        */ /** * @cfg {Object} bbarCfg *

        A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure - * of this Panel's {@link #bbar} Element. See {@link #bodyCfg} also.

        + * of this Panel's {@link #bbar} Element. See {@link #bodyCfg} also.

        */ /** * @cfg {Object} footerCfg *

        A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure - * of this Panel's {@link #footer} Element. See {@link #bodyCfg} also.

        + * of this Panel's {@link #footer} Element. See {@link #bodyCfg} also.

        */ /** * @cfg {Boolean} closable * Panels themselves do not directly support being closed, but some Panel subclasses do (like - * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}. Specify true - * to enable closing in such situations. Defaults to false. + * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}. Specify true + * to enable closing in such situations. Defaults to false. */ /** * The Panel's footer {@link Ext.Element Element}. Read-only. - *

        This Element is used to house the Panel's {@link #buttons} or {@link #fbar}.

        - *

        Note: see the Note for {@link Ext.Component#el el} also.

        + *

        This Element is used to house the Panel's {@link #buttons} or {@link #fbar}.

        + *

        Note: see the Note for {@link Ext.Component#el el} also.

        * @type Ext.Element * @property footer */ /** * @cfg {Mixed} applyTo *

        The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in - * the document that specifies some panel-specific structural markup. When applyTo is used, + * the document that specifies some panel-specific structural markup. When applyTo is used, * constituent parts of the panel can be specified by CSS class name within the main element, and the panel * will automatically create those components from that markup. Any required components not specified in the * markup will be autogenerated if necessary.

        @@ -7590,7 +8169,7 @@ new Ext.Panel({ *

        The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of * buttons/button configs to be added to the toolbar. Note that this is not available as a property after render. * To access the bottom toolbar after render, use {@link #getBottomToolbar}.

        - *

        Note: Although a Toolbar may contain Field components, these will not be updated by a load + *

        Note: Although a Toolbar may contain Field components, these will not be updated by a load * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and * so are not scanned to collect form items. However, the values will be submitted because form * submission parameters are collected from the DOM tree.

        @@ -7599,8 +8178,8 @@ new Ext.Panel({ *

        A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of * {@link Ext.Button Button}s/{@link Ext.Button Button} configs, describing a {@link Ext.Toolbar Toolbar} to be rendered into this Panel's footer element.

        *

        After render, the fbar property will be an {@link Ext.Toolbar Toolbar} instance.

        - *

        If {@link #buttons} are specified, they will supersede the fbar configuration property.

        - * The Panel's {@link #buttonAlign} configuration affects the layout of these items, for example: + *

        If {@link #buttons} are specified, they will supersede the fbar configuration property.

        + * The Panel's {@link #buttonAlign} configuration affects the layout of these items, for example: *
        
         var w = new Ext.Window({
             height: 250,
        @@ -7612,7 +8191,7 @@ var w = new Ext.Window({
                     text: 'bbar Right'
                 }]
             }),
        -    {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use "-", and "->"
        +    {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
                                           // to control the alignment of fbar items
             fbar: [{
                 text: 'fbar Left'
        @@ -7621,40 +8200,40 @@ var w = new Ext.Window({
             }]
         }).show();
              * 
        - *

        Note: Although a Toolbar may contain Field components, these will not be updated by a load + *

        Note: Although a Toolbar may contain Field components, these will not be updated by a load * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and * so are not scanned to collect form items. However, the values will be submitted because form * submission parameters are collected from the DOM tree.

        */ /** * @cfg {Boolean} header - * true to create the Panel's header element explicitly, false to skip creating - * it. If a {@link #title} is set the header will be created automatically, otherwise it will not. - * If a {@link #title} is set but header is explicitly set to false, the header + * true to create the Panel's header element explicitly, false to skip creating + * it. If a {@link #title} is set the header will be created automatically, otherwise it will not. + * If a {@link #title} is set but header is explicitly set to false, the header * will not be rendered. */ /** * @cfg {Boolean} footer - * true to create the footer element explicitly, false to skip creating it. The footer - * will be created automatically if {@link #buttons} or a {@link #fbar} have - * been configured. See {@link #bodyCfg} for an example. + * true to create the footer element explicitly, false to skip creating it. The footer + * will be created automatically if {@link #buttons} or a {@link #fbar} have + * been configured. See {@link #bodyCfg} for an example. */ /** * @cfg {String} title * The title text to be used as innerHTML (html tags are accepted) to display in the panel - * {@link #header} (defaults to ''). When a title is specified the - * {@link #header} element will automatically be created and displayed unless - * {@link #header} is explicitly set to false. If you do not want to specify a - * title at config time, but you may want one later, you must either specify a non-empty - * title (a blank space ' ' will do) or header:true so that the container + * {@link #header} (defaults to ''). When a title is specified the + * {@link #header} element will automatically be created and displayed unless + * {@link #header} is explicitly set to false. If you do not want to specify a + * title at config time, but you may want one later, you must either specify a non-empty + * title (a blank space ' ' will do) or header:true so that the container * element will get created. */ /** * @cfg {Array} buttons - * buttons will be used as {@link Ext.Container#items items} for the toolbar in - * the footer ({@link #fbar}). Typically the value of this configuration property will be + * buttons will be used as {@link Ext.Container#items items} for the toolbar in + * the footer ({@link #fbar}). Typically the value of this configuration property will be * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects. - * If an item is configured with minWidth or the Panel is configured with minButtonWidth, + * If an item is configured with minWidth or the Panel is configured with minButtonWidth, * that width will be applied to the item. */ /** @@ -7667,7 +8246,7 @@ var w = new Ext.Window({ */ /** * @cfg {Boolean} frame - * false by default to render with plain 1px square borders. true to render with + * false by default to render with plain 1px square borders. true to render with * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}). *

        The template generated for each condition is depicted below:

        
              *
        @@ -7739,40 +8318,40 @@ var w = new Ext.Window({
             /**
              * @cfg {Array} tools
              * An array of tool button configs to be added to the header tool area. When rendered, each tool is
        -     * stored as an {@link Ext.Element Element} referenced by a public property called tools.<tool-type>
        +     * stored as an {@link Ext.Element Element} referenced by a public property called tools.<tool-type>
              * 

        Each tool config may contain the following properties: *

          *
        • id : String
          Required. The type - * of tool to create. By default, this assigns a CSS class of the form x-tool-<tool-type> to the + * of tool to create. By default, this assigns a CSS class of the form x-tool-<tool-type> to the * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below. * The developer may implement custom tools by supplying alternate CSS rules and background images: *
            - *
            toggle (Created by default when {@link #collapsible} is true)
            - *
            close
            - *
            minimize
            - *
            maximize
            - *
            restore
            - *
            gear
            - *
            pin
            - *
            unpin
            - *
            right
            - *
            left
            - *
            up
            - *
            down
            - *
            refresh
            - *
            minus
            - *
            plus
            - *
            help
            - *
            search
            - *
            save
            - *
            print
            + *
            toggle (Created by default when {@link #collapsible} is true)
            + *
            close
            + *
            minimize
            + *
            maximize
            + *
            restore
            + *
            gear
            + *
            pin
            + *
            unpin
            + *
            right
            + *
            left
            + *
            up
            + *
            down
            + *
            refresh
            + *
            minus
            + *
            plus
            + *
            help
            + *
            search
            + *
            save
            + *
            print
            *
        • *
        • handler : Function
          Required. The function to * call when clicked. Arguments passed are:
            *
          • event : Ext.EventObject
            The click event.
          • *
          • toolEl : Ext.Element
            The tool Element.
          • *
          • panel : Ext.Panel
            The host Panel
          • - *
          • tc : Ext.Panel
            The tool configuration object
          • + *
          • tc : Object
            The tool configuration object
          • *
        • *
        • stopEvent : Boolean
          Defaults to true. Specify as false to allow click event to propagate.
        • *
        • scope : Object
          The scope in which to call the handler.
        • @@ -7803,7 +8382,7 @@ tools:[{ } }]
        - *

        For the custom id of 'help' define two relevant css classes with a link to + *

        For the custom id of 'help' define two relevant css classes with a link to * a 15x15 image:

        *
        
         .x-tool-help {background-image: url(images/help.png);}
        @@ -7838,7 +8417,7 @@ var win = new Ext.Window({
             height:300,
             closeAction:'hide'
         });
        - *

        Note that the CSS class "x-tool-pdf" should have an associated style rule which provides an + *

        Note that the CSS class 'x-tool-pdf' should have an associated style rule which provides an * appropriate background image, something like:

        
             a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
        @@ -7846,96 +8425,63 @@ var win = new Ext.Window({
              */
             /**
              * @cfg {Boolean} hideCollapseTool
        -     * true to hide the expand/collapse toggle button when {@link #collapsible} == true,
        -     * false to display it (defaults to false).
        +     * true to hide the expand/collapse toggle button when {@link #collapsible} == true,
        +     * false to display it (defaults to false).
              */
             /**
              * @cfg {Boolean} titleCollapse
        -     * true to allow expanding and collapsing the panel (when {@link #collapsible} = true)
        -     * by clicking anywhere in the header bar, false) to allow it only by clicking to tool button
        -     * (defaults to false)). If this panel is a child item of a border layout also see the
        +     * true to allow expanding and collapsing the panel (when {@link #collapsible} = true)
        +     * by clicking anywhere in the header bar, false) to allow it only by clicking to tool button
        +     * (defaults to false)). If this panel is a child item of a border layout also see the
              * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
        -     * {@link Ext.layout.BorderLayout.Region#floatable floatable} config option.
        -     */
        -    /**
        -     * @cfg {Boolean} autoScroll
        -     * true to use overflow:'auto' on the panel's body element and show scroll bars automatically when
        -     * necessary, false to clip any overflowing content (defaults to false).
        +     * {@link Ext.layout.BorderLayout.Region#floatable floatable} config option.
              */
        +
             /**
              * @cfg {Mixed} floating
              * 

        This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this * configuration property are:

          - *
        • false : Default.
          Display the panel inline where it is + *
        • false : Default.
          Display the panel inline where it is * rendered.
        • - *
        • true :
          Float the panel (absolute position it with automatic + *
        • true :
          Float the panel (absolute position it with automatic * shimming and shadow).
            *
            Setting floating to true will create an Ext.Layer for this panel and display the * panel at negative offsets so that it is hidden.
            *
            Since the panel will be absolute positioned, the position must be set explicitly - * after render (e.g., myPanel.setPosition(100,100);).
            + * after render (e.g., myPanel.setPosition(100,100);).
          *
          Note: when floating a panel you should always assign a fixed width, * otherwise it will be auto width and will expand to fill to the right edge of the viewport.
          *
        - *
      • {@link Ext.Layer object} :
        The specified object will be used + *
      • {@link Ext.Layer object} :
        The specified object will be used * as the configuration object for the {@link Ext.Layer} that will be created.
      • *
      */ /** * @cfg {Boolean/String} shadow - * true (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the - * panel, false to display no shadow (defaults to 'sides'). Note that this option - * only applies when {@link #floating} = true. + * true (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the + * panel, false to display no shadow (defaults to 'sides'). Note that this option + * only applies when {@link #floating} = true. */ /** * @cfg {Number} shadowOffset - * The number of pixels to offset the shadow if displayed (defaults to 4). Note that this - * option only applies when {@link #floating} = true. + * The number of pixels to offset the shadow if displayed (defaults to 4). Note that this + * option only applies when {@link #floating} = true. */ /** * @cfg {Boolean} shim - * false to disable the iframe shim in browsers which need one (defaults to true). - * Note that this option only applies when {@link #floating} = true. - */ - /** - * @cfg {String/Object} html - * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the panel's body - * content (defaults to ''). The HTML content is added by the Panel's {@link #afterRender} method, - * and so the document will not contain this HTML at the time the {@link #render} event is fired. - * This content is inserted into the body before any configured {@link #contentEl} is appended. - */ - /** - * @cfg {String} contentEl - *

      Specify the id of an existing HTML node to use as the panel's body content - * (defaults to '').

        - *
      • Description :
          - *
          This config option is used to take an existing HTML element and place it in the body - * of a new panel (it simply moves the specified DOM element into the body element of the Panel - * when the Panel is rendered to use as the content (it is not going to be the - * actual panel itself).
          - *
      • - *
      • Notes :
          - *
          The specified HTML Element is appended to the Panel's {@link #body} Element by the - * Panel's {@link #afterRender} method after any configured {@link #html HTML} has - * been inserted, and so the document will not contain this HTML at the time the - * {@link #render} event is fired.
          - *
          The specified HTML element used will not participate in any layout scheme that the - * Panel may use. It's just HTML. Layouts operate on child items.
          - *
          Add either the x-hidden or the x-hide-display CSS class to - * prevent a brief flicker of the content before it is rendered to the panel.
          - *
      • - *
      + * false to disable the iframe shim in browsers which need one (defaults to true). + * Note that this option only applies when {@link #floating} = true. */ /** * @cfg {Object/Array} keys * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding} - * used to assign custom key handling to this panel (defaults to null). + * used to assign custom key handling to this panel (defaults to null). */ /** * @cfg {Boolean/Object} draggable - *

      true to enable dragging of this Panel (defaults to false).

      + *

      true to enable dragging of this Panel (defaults to false).

      *

      For custom drag/drop implementations, an Ext.Panel.DD config could also be passed - * in this config instead of true. Ext.Panel.DD is an internal, undocumented class which + * in this config instead of true. Ext.Panel.DD is an internal, undocumented class which * moves a proxy Element around in place of the Panel's element, but provides no other behaviour * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.: @@ -7977,15 +8523,9 @@ new Ext.Panel({ }).show();

    */ - /** - * @cfg {String} tabTip - * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over - * the tab of a Ext.Panel which is an item of a {@link Ext.TabPanel}. {@link Ext.QuickTips}.init() - * must be called in order for the tips to render. - */ /** * @cfg {Boolean} disabled - * Render this panel disabled (default is false). An important note when using the disabled + * Render this panel disabled (default is false). An important note when using the disabled * config on panels is that IE will often fail to initialize the disabled mask element correectly if * the panel's layout has not yet completed by the time the Panel is disabled during the render process. * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize @@ -8006,10 +8546,10 @@ new Ext.Panel({ */ /** * @cfg {Boolean} autoHeight - * true to use height:'auto', false to use fixed height (defaults to false). - * Note: Setting autoHeight:true means that the browser will manage the panel's height + * true to use height:'auto', false to use fixed height (defaults to false). + * Note: Setting autoHeight: true means that the browser will manage the panel's height * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that - * manages dimensions (fit, border, etc.) then setting autoHeight:true + * manages dimensions (fit, border, etc.) then setting autoHeight: true * can cause issues with scrolling and will not generally work as expected since the panel will take * on the height of its contents rather than the height required by the Ext layout. */ @@ -8017,64 +8557,64 @@ new Ext.Panel({ /** * @cfg {String} baseCls - * The base CSS class to apply to this panel's element (defaults to 'x-panel'). - *

    Another option available by default is to specify 'x-plain' which strips all styling + * The base CSS class to apply to this panel's element (defaults to 'x-panel'). + *

    Another option available by default is to specify 'x-plain' which strips all styling * except for required attributes for Ext layouts to function (e.g. overflow:hidden). - * See {@link #unstyled} also.

    + * See {@link #unstyled} also.

    */ baseCls : 'x-panel', /** * @cfg {String} collapsedCls * A CSS class to add to the panel's element after it has been collapsed (defaults to - * 'x-panel-collapsed'). + * 'x-panel-collapsed'). */ collapsedCls : 'x-panel-collapsed', /** * @cfg {Boolean} maskDisabled - * true to mask the panel when it is {@link #disabled}, false to not mask it (defaults - * to true). Either way, the panel will always tell its contained elements to disable themselves + * true to mask the panel when it is {@link #disabled}, false to not mask it (defaults + * to true). Either way, the panel will always tell its contained elements to disable themselves * when it is disabled, but masking the panel can provide an additional visual cue that the panel is * disabled. */ maskDisabled : true, /** * @cfg {Boolean} animCollapse - * true to animate the transition when the panel is collapsed, false to skip the - * animation (defaults to true if the {@link Ext.Fx} class is available, otherwise false). + * true to animate the transition when the panel is collapsed, false to skip the + * animation (defaults to true if the {@link Ext.Fx} class is available, otherwise false). */ animCollapse : Ext.enableFx, /** * @cfg {Boolean} headerAsText - * true to display the panel {@link #title} in the {@link #header}, - * false to hide it (defaults to true). + * true to display the panel {@link #title} in the {@link #header}, + * false to hide it (defaults to true). */ headerAsText : true, /** * @cfg {String} buttonAlign - * The alignment of any {@link #buttons} added to this panel. Valid values are 'right', - * 'left' and 'center' (defaults to 'right'). + * The alignment of any {@link #buttons} added to this panel. Valid values are 'right', + * 'left' and 'center' (defaults to 'right'). */ buttonAlign : 'right', /** * @cfg {Boolean} collapsed - * true to render the panel collapsed, false to render it expanded (defaults to - * false). + * true to render the panel collapsed, false to render it expanded (defaults to + * false). */ collapsed : false, /** * @cfg {Boolean} collapseFirst - * true to make sure the collapse/expand toggle button always renders first (to the left of) - * any other tools in the panel's title bar, false to render it last (defaults to true). + * true to make sure the collapse/expand toggle button always renders first (to the left of) + * any other tools in the panel's title bar, false to render it last (defaults to true). */ collapseFirst : true, /** * @cfg {Number} minButtonWidth - * Minimum width in pixels of all {@link #buttons} in this panel (defaults to 75) + * Minimum width in pixels of all {@link #buttons} in this panel (defaults to 75) */ minButtonWidth : 75, /** * @cfg {Boolean} unstyled - * Overrides the {@link #baseCls} setting to {@link #baseCls} = 'x-plain' which renders + * Overrides the {@link #baseCls} setting to {@link #baseCls} = 'x-plain' which renders * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden). */ /** @@ -8084,24 +8624,38 @@ new Ext.Panel({ * make sure a structural element is rendered even if not specified at config time (for example, you may want * to add a button or toolbar dynamically after the panel has been rendered). Adding those elements to this * list will allocate the required placeholders in the panel when it is rendered. Valid values are
      - *
    • header
    • - *
    • tbar (top bar)
    • - *
    • body
    • - *
    • bbar (bottom bar)
    • - *
    • footer
    • + *
    • header
    • + *
    • tbar (top bar)
    • + *
    • body
    • + *
    • bbar (bottom bar)
    • + *
    • footer
    • *
    - * Defaults to 'body'. + * Defaults to 'body'. */ elements : 'body', /** * @cfg {Boolean} preventBodyReset - * Defaults to false. When set to true, an extra css class 'x-panel-normal' + * Defaults to false. When set to true, an extra css class 'x-panel-normal' * will be added to the panel's element, effectively applying css styles suggested by the W3C * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's body element (not the header, * footer, etc.). */ preventBodyReset : false, + /** + * @cfg {Number/String} padding + * A shortcut for setting a padding style on the body element. The value can either be + * a number to be applied to all sides, or a normal css string describing padding. + * Defaults to undefined. + * + */ + padding: undefined, + + /** @cfg {String} resizeEvent + * The event to listen to for resizing in layouts. Defaults to 'bodyresize'. + */ + resizeEvent: 'bodyresize', + // protected - these could be used to customize the behavior of the window, // but changing them would not be useful without further mofifications and // could lead to unexpected or undesirable results. @@ -8215,19 +8769,18 @@ new Ext.Panel({ this.baseCls = 'x-plain'; } + + this.toolbars = []; // shortcuts if(this.tbar){ this.elements += ',tbar'; - if(Ext.isObject(this.tbar)){ - this.topToolbar = this.tbar; - } + this.topToolbar = this.createToolbar(this.tbar); delete this.tbar; + } if(this.bbar){ this.elements += ',bbar'; - if(Ext.isObject(this.bbar)){ - this.bottomToolbar = this.bbar; - } + this.bottomToolbar = this.createToolbar(this.bbar); delete this.bbar; } @@ -8244,33 +8797,61 @@ new Ext.Panel({ } if(this.buttons){ - this.elements += ',footer'; - var btns = this.buttons; - /** - * This Panel's Array of buttons as created from the {@link #buttons} - * config property. Read only. - * @type Array - * @property buttons - */ - this.buttons = []; - for(var i = 0, len = btns.length; i < len; i++) { - if(btns[i].render){ // button instance - this.buttons.push(btns[i]); - }else if(btns[i].xtype){ - this.buttons.push(Ext.create(btns[i], 'button')); - }else{ - this.addButton(btns[i]); - } - } + this.fbar = this.buttons; + delete this.buttons; } if(this.fbar){ - this.elements += ',footer'; + this.createFbar(this.fbar); } if(this.autoLoad){ this.on('render', this.doAutoLoad, this, {delay:10}); } }, + // private + createFbar : function(fbar){ + var min = this.minButtonWidth; + this.elements += ',footer'; + this.fbar = this.createToolbar(fbar, { + buttonAlign: this.buttonAlign, + toolbarCls: 'x-panel-fbar', + enableOverflow: false, + defaults: function(c){ + return { + minWidth: c.minWidth || min + }; + } + }); + //@compat addButton and buttons could possibly be removed + //@target 4.0 + /** + * This Panel's Array of buttons as created from the {@link #buttons} + * config property. Read only. + * @type Array + * @property buttons + */ + this.fbar.items.each(function(c){ + c.minWidth = c.minWidth || this.minButtonWidth; + }, this); + this.buttons = this.fbar.items.items; + }, + + // private + createToolbar: function(tb, options){ + var result; + // Convert array to proper toolbar config + if(Ext.isArray(tb)){ + tb = { + items: tb + }; + } + result = tb.events ? Ext.apply(tb, options) : this.createComponent(Ext.apply({}, tb, options), 'toolbar'); + result.ownerCt = this; + result.bufferResize = false; + this.toolbars.push(result); + return result; + }, + // private createElement : function(name, pnode){ if(this[name]){ @@ -8302,7 +8883,25 @@ new Ext.Panel({ var el = this.el, d = el.dom, - bw; + bw, + ts; + + + if(this.collapsible && !this.hideCollapseTool){ + this.tools = this.tools ? this.tools.slice(0) : []; + this.tools[this.collapseFirst?'unshift':'push']({ + id: 'toggle', + handler : this.toggleCollapse, + scope: this + }); + } + + if(this.tools){ + ts = this.tools; + this.elements += (this.header !== false) ? ',header' : ''; + } + this.tools = {}; + el.addClass(this.baseCls); if(d.firstChild){ // existing markup this.header = el.down('.'+this.headerCls); @@ -8349,6 +8948,13 @@ new Ext.Panel({ if(!this.footer){ this.bwrap.dom.lastChild.className += ' x-panel-nofooter'; } + /* + * Store a reference to this element so: + * a) We aren't looking it up all the time + * b) The last element is reported incorrectly when using a loadmask + */ + this.ft = Ext.get(this.bwrap.dom.lastChild); + this.mc = Ext.get(mc); }else{ this.createElement('header', d); this.createElement('bwrap', d); @@ -8368,7 +8974,7 @@ new Ext.Panel({ } } - if(this.padding !== undefined) { + if(Ext.isDefined(this.padding)){ this.body.setStyle('padding', this.body.addUnits(this.padding)); } @@ -8413,79 +9019,26 @@ new Ext.Panel({ this.makeFloating(this.floating); } - if(this.collapsible){ - this.tools = this.tools ? this.tools.slice(0) : []; - if(!this.hideCollapseTool){ - this.tools[this.collapseFirst?'unshift':'push']({ - id: 'toggle', - handler : this.toggleCollapse, - scope: this - }); - } - if(this.titleCollapse && this.header){ - this.mon(this.header, 'click', this.toggleCollapse, this); - this.header.setStyle('cursor', 'pointer'); - } + if(this.collapsible && this.titleCollapse && this.header){ + this.mon(this.header, 'click', this.toggleCollapse, this); + this.header.setStyle('cursor', 'pointer'); } - if(this.tools){ - var ts = this.tools; - this.tools = {}; + if(ts){ this.addTool.apply(this, ts); - }else{ - this.tools = {}; - } - - if(this.buttons && this.buttons.length > 0){ - this.fbar = new Ext.Toolbar({ - items: this.buttons, - toolbarCls: 'x-panel-fbar' - }); } - this.toolbars = []; if(this.fbar){ - this.fbar = Ext.create(this.fbar, 'toolbar'); - this.fbar.enableOverflow = false; - if(this.fbar.items){ - this.fbar.items.each(function(c){ - c.minWidth = c.minWidth || this.minButtonWidth; - }, this); - } - this.fbar.toolbarCls = 'x-panel-fbar'; - - var bct = this.footer.createChild({cls: 'x-panel-btns x-panel-btns-'+this.buttonAlign}); - this.fbar.ownerCt = this; - this.fbar.render(bct); - bct.createChild({cls:'x-clear'}); - this.toolbars.push(this.fbar); + this.footer.addClass('x-panel-btns'); + this.fbar.render(this.footer); + this.footer.createChild({cls:'x-clear'}); } if(this.tbar && this.topToolbar){ - if(Ext.isArray(this.topToolbar)){ - this.topToolbar = new Ext.Toolbar(this.topToolbar); - }else if(!this.topToolbar.events){ - this.topToolbar = Ext.create(this.topToolbar, 'toolbar'); - } - this.topToolbar.ownerCt = this; this.topToolbar.render(this.tbar); - this.toolbars.push(this.topToolbar); } if(this.bbar && this.bottomToolbar){ - if(Ext.isArray(this.bottomToolbar)){ - this.bottomToolbar = new Ext.Toolbar(this.bottomToolbar); - }else if(!this.bottomToolbar.events){ - this.bottomToolbar = Ext.create(this.bottomToolbar, 'toolbar'); - } - this.bottomToolbar.ownerCt = this; this.bottomToolbar.render(this.bbar); - this.toolbars.push(this.bottomToolbar); + } - Ext.each(this.toolbars, function(tb){ - tb.on({ - scope: this, - afterlayout: this.syncHeight, - remove: this.syncHeight - }); - }, this); }, /** @@ -8501,12 +9054,12 @@ new Ext.Panel({ this.header.addClass('x-panel-icon'); this.header.replaceClass(old, this.iconCls); }else{ - var hd = this.header.dom; - var img = hd.firstChild && String(hd.firstChild.tagName).toLowerCase() == 'img' ? hd.firstChild : null; + var hd = this.header, + img = hd.child('img.x-panel-inline-icon'); if(img){ Ext.fly(img).replaceClass(old, this.iconCls); }else{ - Ext.DomHelper.insertBefore(hd.firstChild, { + Ext.DomHelper.insertBefore(hd.dom.firstChild, { tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls }); } @@ -8518,18 +9071,16 @@ new Ext.Panel({ // private makeFloating : function(cfg){ this.floating = true; - this.el = new Ext.Layer( - Ext.isObject(cfg) ? cfg : { - shadow: this.shadow !== undefined ? this.shadow : 'sides', - shadowOffset: this.shadowOffset, - constrain:false, - shim: this.shim === false ? false : undefined - }, this.el - ); + this.el = new Ext.Layer(Ext.apply({}, cfg, { + shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides', + shadowOffset: this.shadowOffset, + constrain:false, + shim: this.shim === false ? false : undefined + }), this.el); }, /** - * Returns the {@link Ext.Toolbar toolbar} from the top ({@link #tbar}) section of the panel. + * Returns the {@link Ext.Toolbar toolbar} from the top ({@link #tbar}) section of the panel. * @return {Ext.Toolbar} The toolbar */ getTopToolbar : function(){ @@ -8537,7 +9088,7 @@ new Ext.Panel({ }, /** - * Returns the {@link Ext.Toolbar toolbar} from the bottom ({@link #bbar}) section of the panel. + * Returns the {@link Ext.Toolbar toolbar} from the bottom ({@link #bbar}) section of the panel. * @return {Ext.Toolbar} The toolbar */ getBottomToolbar : function(){ @@ -8550,32 +9101,38 @@ new Ext.Panel({ * @param {String/Object} config A valid {@link Ext.Button} config. A string will become the text for a default * button config, an object will be treated as a button config object. * @param {Function} handler The function to be called on button {@link Ext.Button#click} - * @param {Object} scope The scope to use for the button handler function + * @param {Object} scope The scope (this reference) in which the button handler function is executed. Defaults to the Button. * @return {Ext.Button} The button that was added */ addButton : function(config, handler, scope){ - var bc = { - handler: handler, - scope: scope, - minWidth: this.minButtonWidth, - hideParent:true - }; - if(typeof config == "string"){ - bc.text = config; - }else{ - Ext.apply(bc, config); + if(!this.fbar){ + this.createFbar([]); } - var btn = new Ext.Button(bc); - if(!this.buttons){ - this.buttons = []; + if(handler){ + if(Ext.isString(config)){ + config = {text: config}; + } + config = Ext.apply({ + handler: handler, + scope: scope + }, config) } - this.buttons.push(btn); - return btn; + return this.fbar.add(config); }, // private addTool : function(){ - if(!this[this.toolTarget]) { // no where to render tools! + if(!this.rendered){ + if(!this.tools){ + this.tools = []; + } + Ext.each(arguments, function(arg){ + this.tools.push(arg) + }, this); + return; + } + // nowhere to render tools! + if(!this[this.toolTarget]){ return; } if(!this.toolTemplate){ @@ -8615,35 +9172,32 @@ new Ext.Panel({ } }, - onLayout : function(){ - if(this.toolbars.length > 0){ - this.duringLayout = true; + onLayout : function(shallow, force){ + if(this.hasLayout && this.toolbars.length > 0){ Ext.each(this.toolbars, function(tb){ - tb.doLayout(); + tb.doLayout(undefined, force); }); - delete this.duringLayout; this.syncHeight(); } }, syncHeight : function(){ - if(!(this.autoHeight || this.duringLayout)){ - var last = this.lastSize; - if(last && !Ext.isEmpty(last.height)){ - var old = last.height, h = this.el.getHeight(); - if(old != 'auto' && old != h){ - var bd = this.body, bdh = bd.getHeight(); - h = Math.max(bdh + old - h, 0); - if(bdh > 0 && bdh != h){ - bd.setHeight(h); - if(Ext.isIE && h <= 0){ - return; - } - var sz = bd.getSize(); - this.fireEvent('bodyresize', sz.width, sz.height); - } - } - } + var h = this.toolbarHeight, + bd = this.body, + lsh = this.lastSize.height, + sz; + + if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){ + return; + } + + + if(h != this.getToolbarHeight()){ + h = Math.max(0, this.adjustBodyHeight(lsh - this.getFrameHeight())); + bd.setHeight(h); + sz = bd.getSize(); + this.toolbarHeight = this.getToolbarHeight(); + this.onBodyResize(sz.width, sz.height); } }, @@ -8676,7 +9230,7 @@ new Ext.Panel({ }; }, - // private + // private afterRender : function(){ if(this.floating && !this.hidden){ this.el.show(); @@ -8684,35 +9238,13 @@ new Ext.Panel({ if(this.title){ this.setTitle(this.title); } - this.setAutoScroll(); - if(this.html){ - this.body.update(Ext.isObject(this.html) ? - Ext.DomHelper.markup(this.html) : - this.html); - delete this.html; - } - if(this.contentEl){ - var ce = Ext.getDom(this.contentEl); - Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']); - this.body.dom.appendChild(ce); - } if(this.collapsed){ this.collapsed = false; this.collapse(false); } Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last this.initEvents(); - }, - - // private - setAutoScroll : function(){ - if(this.rendered && this.autoScroll){ - var el = this.body || this.el; - if(el){ - el.setOverflow('auto'); - } - } - }, + }, // private getKeyMap : function(){ @@ -8730,6 +9262,20 @@ new Ext.Panel({ if(this.draggable){ this.initDraggable(); } + if(this.toolbars.length > 0){ + Ext.each(this.toolbars, function(tb){ + tb.doLayout(); + tb.on({ + scope: this, + afterlayout: this.syncHeight, + remove: this.syncHeight + }); + }, this); + if(!this.ownerCt){ + this.syncHeight(); + } + } + }, // private @@ -8742,21 +9288,25 @@ new Ext.Panel({ * @type Ext.dd.DragSource. * @property dd */ - this.dd = new Ext.Panel.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable); + this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable); }, // private - beforeEffect : function(){ + beforeEffect : function(anim){ if(this.floating){ this.el.beforeAction(); } - this.el.addClass('x-panel-animated'); + if(anim !== false){ + this.el.addClass('x-panel-animated'); + } }, // private - afterEffect : function(){ + afterEffect : function(anim){ this.syncShadow(); - this.el.removeClass('x-panel-animated'); + if(anim !== false){ + this.el.removeClass('x-panel-animated'); + } }, // private - wraps up an animation param with internal callbacks @@ -8791,7 +9341,7 @@ new Ext.Panel({ return; } var doAnim = animate === true || (animate !== false && this.animCollapse); - this.beforeEffect(); + this.beforeEffect(doAnim); this.onCollapse(doAnim, animate); return this; }, @@ -8804,15 +9354,15 @@ new Ext.Panel({ this.collapseDefaults)); }else{ this[this.collapseEl].hide(); - this.afterCollapse(); + this.afterCollapse(false); } }, // private - afterCollapse : function(){ + afterCollapse : function(anim){ this.collapsed = true; this.el.addClass(this.collapsedCls); - this.afterEffect(); + this.afterEffect(anim); this.fireEvent('collapse', this); }, @@ -8829,7 +9379,7 @@ new Ext.Panel({ } var doAnim = animate === true || (animate !== false && this.animCollapse); this.el.removeClass(this.collapsedCls); - this.beforeEffect(); + this.beforeEffect(doAnim); this.onExpand(doAnim, animate); return this; }, @@ -8842,15 +9392,15 @@ new Ext.Panel({ this.expandDefaults)); }else{ this[this.collapseEl].show(); - this.afterExpand(); + this.afterExpand(false); } }, // private - afterExpand : function(){ + afterExpand : function(anim){ this.collapsed = false; - this.afterEffect(); - if(this.deferLayout !== undefined){ + this.afterEffect(anim); + if(Ext.isDefined(this.deferLayout)){ this.doLayout(true); } this.fireEvent('expand', this); @@ -8885,52 +9435,46 @@ new Ext.Panel({ // private onResize : function(w, h){ - if(w !== undefined || h !== undefined){ + if(Ext.isDefined(w) || Ext.isDefined(h)){ if(!this.collapsed){ - if(typeof w == 'number'){ - w = this.adjustBodyWidth(w - this.getFrameWidth()); - if(this.tbar){ - this.tbar.setWidth(w); - if(this.topToolbar){ - this.topToolbar.setSize(w); - } + // First, set the the Panel's body width. + // If we have auto-widthed it, get the resulting full offset width so we can size the Toolbars to match + // The Toolbars must not buffer this resize operation because we need to know their heights. + + if(Ext.isNumber(w)){ + this.body.setWidth(w = this.adjustBodyWidth(w - this.getFrameWidth())); + } else if (w == 'auto') { + w = this.body.setWidth('auto').dom.offsetWidth; + } else { + w = this.body.dom.offsetWidth; + } + + if(this.tbar){ + this.tbar.setWidth(w); + if(this.topToolbar){ + this.topToolbar.setSize(w); } - if(this.bbar){ - this.bbar.setWidth(w); - if(this.bottomToolbar){ - this.bottomToolbar.setSize(w); + } + if(this.bbar){ + this.bbar.setWidth(w); + if(this.bottomToolbar){ + this.bottomToolbar.setSize(w); + // The bbar does not move on resize without this. + if (Ext.isIE) { + this.bbar.setStyle('position', 'static'); + this.bbar.setStyle('position', ''); } } + } + if(this.footer){ + this.footer.setWidth(w); if(this.fbar){ - var f = this.fbar, - fWidth = 1, - strict = Ext.isStrict; - if(this.buttonAlign == 'left'){ - fWidth = w - f.container.getFrameWidth('lr'); - }else{ - //center/right alignment off in webkit - if(Ext.isIE || Ext.isWebKit){ - //center alignment ok on webkit. - //right broken in both, center on IE - if(!(this.buttonAlign == 'center' && Ext.isWebKit) && (!strict || (!Ext.isIE8 && strict))){ - (function(){ - f.setWidth(f.getEl().child('.x-toolbar-ct').getWidth()); - }).defer(1); - }else{ - fWidth = 'auto'; - } - }else{ - fWidth = 'auto'; - } - } - f.setWidth(fWidth); + this.fbar.setSize(Ext.isIE ? (w - this.footer.getFrameWidth('lr')) : 'auto'); } - this.body.setWidth(w); - }else if(w == 'auto'){ - this.body.setWidth(w); } - if(typeof h == 'number'){ + // At this point, the Toolbars must be layed out for getFrameHeight to find a result. + if(Ext.isNumber(h)){ h = Math.max(0, this.adjustBodyHeight(h - this.getFrameHeight())); this.body.setHeight(h); }else if(h == 'auto'){ @@ -8947,13 +9491,29 @@ new Ext.Panel({ this.on('expand', function(){ delete this.queuedExpand; this.onResize(this.queuedBodySize.width, this.queuedBodySize.height); - this.doLayout(); }, this, {single:true}); } } - this.fireEvent('bodyresize', this, w, h); + this.onBodyResize(w, h); } this.syncShadow(); + Ext.Panel.superclass.onResize.call(this); + }, + + // private + onBodyResize: function(w, h){ + this.fireEvent('bodyresize', this, w, h); + }, + + // private + getToolbarHeight: function(){ + var h = 0; + if(this.rendered){ + Ext.each(this.toolbars, function(tb){ + h += tb.getHeight(); + }, this); + } + return h; }, // private @@ -8977,13 +9537,12 @@ new Ext.Panel({ * @return {Number} The frame width */ getFrameWidth : function(){ - var w = this.el.getFrameWidth('lr')+this.bwrap.getFrameWidth('lr'); + var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr'); if(this.frame){ var l = this.bwrap.dom.firstChild; w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r')); - var mc = this.bwrap.dom.firstChild.firstChild.firstChild; - w += Ext.fly(mc).getFrameWidth('lr'); + w += this.mc.getFrameWidth('lr'); } return w; }, @@ -8994,16 +9553,12 @@ new Ext.Panel({ * @return {Number} The frame height */ getFrameHeight : function(){ - var h = this.el.getFrameWidth('tb')+this.bwrap.getFrameWidth('tb'); + var h = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb'); h += (this.tbar ? this.tbar.getHeight() : 0) + (this.bbar ? this.bbar.getHeight() : 0); if(this.frame){ - var hd = this.el.dom.firstChild; - var ft = this.bwrap.dom.lastChild; - h += (hd.offsetHeight + ft.offsetHeight); - var mc = this.bwrap.dom.firstChild.firstChild.firstChild; - h += Ext.fly(mc).getFrameWidth('tb'); + h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb'); }else{ h += (this.header ? this.header.getHeight() : 0) + (this.footer ? this.footer.getHeight() : 0); @@ -9041,11 +9596,16 @@ new Ext.Panel({ return this.body; }, + // private + getContentTarget : function(){ + return this.body; + }, + /** *

    Sets the title text for the panel and optionally the {@link #iconCls icon class}.

    *

    In order to be able to set the title, a header element must have been created - * for the Panel. This is triggered either by configuring the Panel with a non-blank {@link #title}, - * or configuring it with {@link #header}: true.

    + * for the Panel. This is triggered either by configuring the Panel with a non-blank {@link #title}, + * or configuring it with {@link #header}: true.

    * @param {String} title The title text to set * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel */ @@ -9074,13 +9634,13 @@ new Ext.Panel({ * @param {Object/String/Function} config A config object containing any of the following options:
    
     panel.load({
    -    url: "your-url.php",
    -    params: {param1: "foo", param2: "bar"}, // or a URL encoded string
    +    url: 'your-url.php',
    +    params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
         callback: yourFunction,
         scope: yourObject, // optional scope for the callback
         discardUrl: false,
         nocache: false,
    -    text: "Loading...",
    +    text: 'Loading...',
         timeout: 30,
         scripts: false
     });
    @@ -9098,32 +9658,44 @@ panel.load({
     
         // private
         beforeDestroy : function(){
    +        Ext.Panel.superclass.beforeDestroy.call(this);
             if(this.header){
                 this.header.removeAllListeners();
    -            if(this.headerAsText){
    -                Ext.Element.uncache(this.header.child('span'));
    -            }
             }
    -        Ext.Element.uncache(
    -            this.header,
    -            this.tbar,
    -            this.bbar,
    -            this.footer,
    -            this.body,
    -            this.bwrap
    -        );
             if(this.tools){
                 for(var k in this.tools){
                     Ext.destroy(this.tools[k]);
                 }
             }
    -        if(this.buttons){
    -            for(var b in this.buttons){
    -                Ext.destroy(this.buttons[b]);
    +        if(Ext.isArray(this.buttons)){
    +            while(this.buttons.length) {
    +                Ext.destroy(this.buttons[0]);
                 }
             }
    -        Ext.destroy(this.toolbars);
    -        Ext.Panel.superclass.beforeDestroy.call(this);
    +        if(this.rendered){
    +            Ext.destroy(
    +                this.ft,
    +                this.header,
    +                this.footer,
    +                this.toolbars,
    +                this.tbar,
    +                this.bbar,
    +                this.body,
    +                this.mc,
    +                this.bwrap
    +            );
    +            if (this.fbar) {
    +                Ext.destroy(
    +                    this.fbar,
    +                    this.fbar.el
    +                );
    +            }
    +        }else{
    +            Ext.destroy(
    +                this.topToolbar,
    +                this.bottomToolbar
    +            );
    +        }
         },
     
         // private
    @@ -9215,8 +9787,8 @@ Ext.extend(Ext.Editor, Ext.Component, {
          */
         /**
          * @cfg {Boolean/String} autoSize
    -     * True for the editor to automatically adopt the size of the element being edited, "width" to adopt the width only,
    -     * or "height" to adopt the height only (defaults to false)
    +     * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
    +     * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
          */
         /**
          * @cfg {Boolean} revertInvalid
    @@ -9243,6 +9815,11 @@ Ext.extend(Ext.Editor, Ext.Component, {
          * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
          */
         alignment: "c-c?",
    +    /**
    +     * @cfg {Array} offsets
    +     * The offsets to use when aligning (see {@link Ext.Element#alignTo} for more details. Defaults to [0, 0].
    +     */
    +    offsets: [0, 0],
         /**
          * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
          * for bottom-right shadow (defaults to "frame")
    @@ -9257,13 +9834,13 @@ Ext.extend(Ext.Editor, Ext.Component, {
          */
         swallowKeys : true,
         /**
    -     * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
    +     * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to true.
          */
    -    completeOnEnter : false,
    +    completeOnEnter : true,
         /**
    -     * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
    +     * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to true.
          */
    -    cancelOnEsc : false,
    +    cancelOnEsc : true,
         /**
          * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
          */
    @@ -9345,35 +9922,41 @@ Ext.extend(Ext.Editor, Ext.Component, {
                 this.field.msgTarget = 'qtip';
             }
             this.field.inEditor = true;
    -        this.field.render(this.el);
    -        if(Ext.isGecko){
    -            this.field.el.dom.setAttribute('autocomplete', 'off');
    +        this.mon(this.field, {
    +            scope: this,
    +            blur: this.onBlur,
    +            specialkey: this.onSpecialKey
    +        });
    +        if(this.field.grow){
    +            this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
             }
    -        this.mon(this.field, "specialkey", this.onSpecialKey, this);
    +        this.field.render(this.el).show();
    +        this.field.getEl().dom.name = '';
             if(this.swallowKeys){
    -            this.field.el.swallowEvent(['keydown','keypress']);
    -        }
    -        this.field.show();
    -        this.mon(this.field, "blur", this.onBlur, this);
    -        if(this.field.grow){
    -        	this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
    +            this.field.el.swallowEvent([
    +                'keypress', // *** Opera
    +                'keydown'   // *** all other browsers
    +            ]);
             }
         },
     
         // private
         onSpecialKey : function(field, e){
    -        var key = e.getKey();
    -        if(this.completeOnEnter && key == e.ENTER){
    +        var key = e.getKey(),
    +            complete = this.completeOnEnter && key == e.ENTER,
    +            cancel = this.cancelOnEsc && key == e.ESC;
    +        if(complete || cancel){
                 e.stopEvent();
    -            this.completeEdit();
    -        }else if(this.cancelOnEsc && key == e.ESC){
    -            this.cancelEdit();
    -        }else{
    -            this.fireEvent('specialkey', field, e);
    -        }
    -        if(this.field.triggerBlur && (key == e.ENTER || key == e.ESC || key == e.TAB)){
    -            this.field.triggerBlur();
    +            if(complete){
    +                this.completeEdit();
    +            }else{
    +                this.cancelEdit();
    +            }
    +            if(field.triggerBlur){
    +                field.triggerBlur(); 
    +            }
             }
    +        this.fireEvent('specialkey', field, e);
         },
     
         /**
    @@ -9391,30 +9974,34 @@ Ext.extend(Ext.Editor, Ext.Component, {
             if(!this.rendered){
                 this.render(this.parentEl || document.body);
             }
    -        if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
    -            return;
    +        if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
    +            this.startValue = v;
    +            this.field.reset();
    +            this.field.setValue(v);
    +            this.realign(true);
    +            this.editing = true;
    +            this.show();
             }
    -        this.startValue = v;
    -        this.field.setValue(v);
    -        this.doAutoSize();
    -        this.el.alignTo(this.boundEl, this.alignment);
    -        this.editing = true;
    -        this.show();
         },
     
         // private
         doAutoSize : function(){
             if(this.autoSize){
    -            var sz = this.boundEl.getSize();
    +            var sz = this.boundEl.getSize(),
    +                fs = this.field.getSize();
    +
                 switch(this.autoSize){
                     case "width":
    -                    this.setSize(sz.width,  "");
    -                break;
    +                    this.setSize(sz.width, fs.height);
    +                    break;
                     case "height":
    -                    this.setSize("",  sz.height);
    -                break;
    +                    this.setSize(fs.width, sz.height);
    +                    break;
    +                case "none":
    +                    this.setSize(fs.width, fs.height);
    +                    break;
                     default:
    -                    this.setSize(sz.width,  sz.height);
    +                    this.setSize(sz.width, sz.height);
                 }
             }
         },
    @@ -9438,9 +10025,13 @@ Ext.extend(Ext.Editor, Ext.Component, {
     
         /**
          * Realigns the editor to the bound field based on the current alignment config value.
    +     * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
          */
    -    realign : function(){
    -        this.el.alignTo(this.boundEl, this.alignment);
    +    realign : function(autoSize){
    +        if(autoSize === true){
    +            this.doAutoSize();
    +        }
    +        this.el.alignTo(this.boundEl, this.alignment, this.offsets);
         },
     
         /**
    @@ -9478,22 +10069,10 @@ Ext.extend(Ext.Editor, Ext.Component, {
             if(this.hideEl !== false){
                 this.boundEl.hide();
             }
    -        this.field.show();
    -        if(Ext.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
    -            this.fixIEFocus = true;
    -            this.deferredFocus.defer(50, this);
    -        }else{
    -            this.field.focus();
    -        }
    +        this.field.show().focus(false, true);
             this.fireEvent("startedit", this.boundEl, this.startValue);
         },
     
    -    deferredFocus : function(){
    -        if(this.editing){
    -            this.field.focus();
    -        }
    -    },
    -
         /**
          * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
          * reverted to the original starting value.
    @@ -9557,11 +10136,14 @@ Ext.extend(Ext.Editor, Ext.Component, {
         },
     
         beforeDestroy : function(){
    -        Ext.destroy(this.field);
    -        this.field = null;
    +        Ext.destroyMembers(this, 'field');
    +        
    +        delete this.parentEl;
    +        delete this.boundEl;
         }
     });
    -Ext.reg('editor', Ext.Editor);/**
    +Ext.reg('editor', Ext.Editor);
    +/**
      * @class Ext.ColorPalette
      * @extends Ext.Component
      * Simple color palette class for choosing colors.  The palette can be rendered to any container.
    @@ -9579,40 +10161,29 @@ cp.on('select', function(palette, selColor){ * @param {Object} config The config object * @xtype colorpalette */ -Ext.ColorPalette = function(config){ - Ext.ColorPalette.superclass.constructor.call(this, config); - this.addEvents( - /** - * @event select - * Fires when a color is selected - * @param {ColorPalette} this - * @param {String} color The 6-digit color hex code (without the # symbol) - */ - 'select' - ); - - if(this.handler){ - this.on("select", this.handler, this.scope, true); - } -}; -Ext.extend(Ext.ColorPalette, Ext.Component, { +Ext.ColorPalette = Ext.extend(Ext.Component, { /** * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component. */ /** * @cfg {String} itemCls - * The CSS class to apply to the containing element (defaults to "x-color-palette") + * The CSS class to apply to the containing element (defaults to 'x-color-palette') */ - itemCls : "x-color-palette", + itemCls : 'x-color-palette', /** * @cfg {String} value * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that * the hex codes are case-sensitive. */ value : null, - clickEvent:'click', + /** + * @cfg {String} clickEvent + * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu). + * Defaults to 'click'. + */ + clickEvent :'click', // private - ctype: "Ext.ColorPalette", + ctype : 'Ext.ColorPalette', /** * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event @@ -9627,35 +10198,67 @@ Ext.extend(Ext.ColorPalette, Ext.Component, { *

    You can override individual colors if needed:

    *
    
     var cp = new Ext.ColorPalette();
    -cp.colors[0] = "FF0000";  // change the first box to red
    +cp.colors[0] = 'FF0000';  // change the first box to red
     
    Or you can provide a custom array of your own for complete control:
    
     var cp = new Ext.ColorPalette();
    -cp.colors = ["000000", "993300", "333300"];
    +cp.colors = ['000000', '993300', '333300'];
     
    * @type Array */ colors : [ - "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333", - "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080", - "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696", - "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0", - "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF" + '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333', + '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080', + 'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696', + 'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0', + 'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF' ], + /** + * @cfg {Function} handler + * Optional. A function that will handle the select event of this palette. + * The handler is passed the following parameters:
      + *
    • palette : ColorPalette
      The {@link #palette Ext.ColorPalette}.
    • + *
    • color : String
      The 6-digit color hex code (without the # symbol).
    • + *
    + */ + /** + * @cfg {Object} scope + * The scope (this reference) in which the {@link #handler} + * function will be called. Defaults to this ColorPalette instance. + */ + + // private + initComponent : function(){ + Ext.ColorPalette.superclass.initComponent.call(this); + this.addEvents( + /** + * @event select + * Fires when a color is selected + * @param {ColorPalette} this + * @param {String} color The 6-digit color hex code (without the # symbol) + */ + 'select' + ); + + if(this.handler){ + this.on('select', this.handler, this.scope, true); + } + }, + // private onRender : function(container, position){ + this.autoEl = { + tag: 'div', + cls: this.itemCls + }; + Ext.ColorPalette.superclass.onRender.call(this, container, position); var t = this.tpl || new Ext.XTemplate( ' ' ); - var el = document.createElement("div"); - el.id = this.getId(); - el.className = this.itemCls; - t.overwrite(el, this.colors); - container.dom.insertBefore(el, position); - this.el = Ext.get(el); + t.overwrite(this.el, this.colors); this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'}); if(this.clickEvent != 'click'){ this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true}); @@ -9686,15 +10289,15 @@ cp.colors = ["000000", "993300", "333300"]; * @param {String} color A valid 6-digit color hex code (# will be stripped if included) */ select : function(color){ - color = color.replace("#", ""); + color = color.replace('#', ''); if(color != this.value || this.allowReselect){ var el = this.el; if(this.value){ - el.child("a.color-"+this.value).removeClass("x-color-palette-sel"); + el.child('a.color-'+this.value).removeClass('x-color-palette-sel'); } - el.child("a.color-"+color).addClass("x-color-palette-sel"); + el.child('a.color-'+color).addClass('x-color-palette-sel'); this.value = color; - this.fireEvent("select", this, color); + this.fireEvent('select', this, color); } } @@ -9702,771 +10305,793 @@ cp.colors = ["000000", "993300", "333300"]; * @cfg {String} autoEl @hide */ }); -Ext.reg('colorpalette', Ext.ColorPalette);/** - * @class Ext.DatePicker - * @extends Ext.Component - * Simple date picker class. - * @constructor - * Create a new DatePicker - * @param {Object} config The config object - * @xtype datepicker - */ -Ext.DatePicker = Ext.extend(Ext.BoxComponent, { - /** - * @cfg {String} todayText - * The text to display on the button that selects the current date (defaults to 'Today') - */ - todayText : 'Today', - /** - * @cfg {String} okText - * The text to display on the ok button (defaults to ' OK ' to give the user extra clicking room) - */ - okText : ' OK ', - /** - * @cfg {String} cancelText - * The text to display on the cancel button (defaults to 'Cancel') - */ - cancelText : 'Cancel', - /** - * @cfg {String} todayTip - * The tooltip to display for the button that selects the current date (defaults to '{current date} (Spacebar)') - */ - todayTip : '{0} (Spacebar)', - /** - * @cfg {String} minText - * The error text to display if the minDate validation fails (defaults to 'This date is before the minimum date') - */ - minText : 'This date is before the minimum date', - /** - * @cfg {String} maxText - * The error text to display if the maxDate validation fails (defaults to 'This date is after the maximum date') - */ - maxText : 'This date is after the maximum date', - /** - * @cfg {String} format - * The default date format string which can be overriden for localization support. The format must be - * valid according to {@link Date#parseDate} (defaults to 'm/d/y'). - */ - format : 'm/d/y', - /** - * @cfg {String} disabledDaysText - * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled') - */ - disabledDaysText : 'Disabled', - /** - * @cfg {String} disabledDatesText - * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled') - */ - disabledDatesText : 'Disabled', - /** - * @cfg {Array} monthNames - * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames) - */ - monthNames : Date.monthNames, - /** - * @cfg {Array} dayNames - * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames) - */ - dayNames : Date.dayNames, - /** - * @cfg {String} nextText - * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)') - */ - nextText : 'Next Month (Control+Right)', - /** - * @cfg {String} prevText - * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)') - */ - prevText : 'Previous Month (Control+Left)', - /** - * @cfg {String} monthYearText - * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)') - */ - monthYearText : 'Choose a month (Control+Up/Down to move years)', - /** - * @cfg {Number} startDay - * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday) - */ - startDay : 0, - /** - * @cfg {Boolean} showToday - * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar - * that selects the current date (defaults to true). - */ - showToday : true, - /** - * @cfg {Date} minDate - * Minimum allowable date (JavaScript date object, defaults to null) - */ - /** - * @cfg {Date} maxDate - * Maximum allowable date (JavaScript date object, defaults to null) - */ - /** - * @cfg {Array} disabledDays - * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null). - */ - /** - * @cfg {RegExp} disabledDatesRE - * JavaScript regular expression used to disable a pattern of dates (defaults to null). The {@link #disabledDates} - * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the - * disabledDates value. - */ - /** - * @cfg {Array} disabledDates - * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular - * expression so they are very powerful. Some examples: - *
      - *
    • ['03/08/2003', '09/16/2003'] would disable those exact dates
    • - *
    • ['03/08', '09/16'] would disable those days for every year
    • - *
    • ['^03/08'] would only match the beginning (useful if you are using short years)
    • - *
    • ['03/../2006'] would disable every day in March 2006
    • - *
    • ['^03'] would disable every day in every March
    • - *
    - * Note that the format of the dates included in the array should exactly match the {@link #format} config. - * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to - * escape the dot when restricting dates. For example: ['03\\.08\\.03']. - */ - - // private - initComponent : function(){ - Ext.DatePicker.superclass.initComponent.call(this); - - this.value = this.value ? - this.value.clearTime() : new Date().clearTime(); - - this.addEvents( - /** - * @event select - * Fires when a date is selected - * @param {DatePicker} this - * @param {Date} date The selected date - */ - 'select' - ); - - if(this.handler){ - this.on('select', this.handler, this.scope || this); - } - - this.initDisabledDays(); - }, - - // private - initDisabledDays : function(){ - if(!this.disabledDatesRE && this.disabledDates){ - var dd = this.disabledDates, - len = dd.length - 1, - re = '(?:'; - - Ext.each(dd, function(d, i){ - re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i]; - if(i != len){ - re += '|'; - } - }, this); - this.disabledDatesRE = new RegExp(re + ')'); - } - }, - - /** - * Replaces any existing disabled dates with new values and refreshes the DatePicker. - * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config - * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates. - */ - setDisabledDates : function(dd){ - if(Ext.isArray(dd)){ - this.disabledDates = dd; - this.disabledDatesRE = null; - }else{ - this.disabledDatesRE = dd; - } - this.initDisabledDays(); - this.update(this.value, true); - }, - - /** - * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker. - * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config - * for details on supported values. - */ - setDisabledDays : function(dd){ - this.disabledDays = dd; - this.update(this.value, true); - }, - - /** - * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker. - * @param {Date} value The minimum date that can be selected - */ - setMinDate : function(dt){ - this.minDate = dt; - this.update(this.value, true); - }, - - /** - * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker. - * @param {Date} value The maximum date that can be selected - */ - setMaxDate : function(dt){ - this.maxDate = dt; - this.update(this.value, true); - }, - - /** - * Sets the value of the date field - * @param {Date} value The date to set - */ - setValue : function(value){ - var old = this.value; - this.value = value.clearTime(true); - if(this.el){ - this.update(this.value); - } - }, - - /** - * Gets the current selected value of the date field - * @return {Date} The selected date - */ - getValue : function(){ - return this.value; - }, - - // private - focus : function(){ - if(this.el){ - this.update(this.activeDate); - } - }, - - // private - onEnable: function(initial){ - Ext.DatePicker.superclass.onEnable.call(this); - this.doDisabled(false); - this.update(initial ? this.value : this.activeDate); - if(Ext.isIE){ - this.el.repaint(); - } - - }, - - // private - onDisable: function(){ - Ext.DatePicker.superclass.onDisable.call(this); - this.doDisabled(true); - if(Ext.isIE && !Ext.isIE8){ - /* Really strange problem in IE6/7, when disabled, have to explicitly - * repaint each of the nodes to get them to display correctly, simply - * calling repaint on the main element doesn't appear to be enough. - */ - Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){ - Ext.fly(el).repaint(); - }); - } - }, - - // private - doDisabled: function(disabled){ - this.keyNav.setDisabled(disabled); - this.prevRepeater.setDisabled(disabled); - this.nextRepeater.setDisabled(disabled); - if(this.showToday){ - this.todayKeyListener.setDisabled(disabled); - this.todayBtn.setDisabled(disabled); - } - }, - - // private - onRender : function(container, position){ - var m = [ - '', - '', - '', - this.showToday ? '' : '', - '
      
    '], - dn = this.dayNames, - i; - for(i = 0; i < 7; i++){ - var d = this.startDay+i; - if(d > 6){ - d = d-7; - } - m.push(''); - } - m[m.length] = ''; - for(i = 0; i < 42; i++) { - if(i % 7 === 0 && i !== 0){ - m[m.length] = ''; - } - m[m.length] = ''; - } - m.push('
    ', dn[d].substr(0,1), '
    '); - - var el = document.createElement('div'); - el.className = 'x-date-picker'; - el.innerHTML = m.join(''); - - container.dom.insertBefore(el, position); - - this.el = Ext.get(el); - this.eventEl = Ext.get(el.firstChild); - - this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), { - handler: this.showPrevMonth, - scope: this, - preventDefault:true, - stopDefault:true - }); - - this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), { - handler: this.showNextMonth, - scope: this, - preventDefault:true, - stopDefault:true - }); - - this.monthPicker = this.el.down('div.x-date-mp'); - this.monthPicker.enableDisplayMode('block'); - - this.keyNav = new Ext.KeyNav(this.eventEl, { - 'left' : function(e){ - if(e.ctrlKey){ - this.showPrevMonth(); - }else{ - this.update(this.activeDate.add('d', -1)); - } - }, - - 'right' : function(e){ - if(e.ctrlKey){ - this.showNextMonth(); - }else{ - this.update(this.activeDate.add('d', 1)); - } - }, - - 'up' : function(e){ - if(e.ctrlKey){ - this.showNextYear(); - }else{ - this.update(this.activeDate.add('d', -7)); - } - }, - - 'down' : function(e){ - if(e.ctrlKey){ - this.showPrevYear(); - }else{ - this.update(this.activeDate.add('d', 7)); - } - }, - - 'pageUp' : function(e){ - this.showNextMonth(); - }, - - 'pageDown' : function(e){ - this.showPrevMonth(); - }, - - 'enter' : function(e){ - e.stopPropagation(); - return true; - }, - - scope : this - }); - - this.el.unselectable(); - - this.cells = this.el.select('table.x-date-inner tbody td'); - this.textNodes = this.el.query('table.x-date-inner tbody span'); - - this.mbtn = new Ext.Button({ - text: ' ', - tooltip: this.monthYearText, - renderTo: this.el.child('td.x-date-middle', true) - }); - this.mbtn.el.child('em').addClass('x-btn-arrow'); - - if(this.showToday){ - this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday, this); - var today = (new Date()).dateFormat(this.format); - this.todayBtn = new Ext.Button({ - renderTo: this.el.child('td.x-date-bottom', true), - text: String.format(this.todayText, today), - tooltip: String.format(this.todayTip, today), - handler: this.selectToday, - scope: this - }); - } - this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this); - this.mon(this.eventEl, 'click', this.handleDateClick, this, {delegate: 'a.x-date-date'}); - this.mon(this.mbtn, 'click', this.showMonthPicker, this); - this.onEnable(true); - }, - - // private - createMonthPicker : function(){ - if(!this.monthPicker.dom.firstChild){ - var buf = ['']; - for(var i = 0; i < 6; i++){ - buf.push( - '', - '', - i === 0 ? - '' : - '' - ); - } - buf.push( - '', - '
    ', Date.getShortMonthName(i), '', Date.getShortMonthName(i + 6), '
    ' - ); - this.monthPicker.update(buf.join('')); - - this.mon(this.monthPicker, 'click', this.onMonthClick, this); - this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this); - - this.mpMonths = this.monthPicker.select('td.x-date-mp-month'); - this.mpYears = this.monthPicker.select('td.x-date-mp-year'); - - this.mpMonths.each(function(m, a, i){ - i += 1; - if((i%2) === 0){ - m.dom.xmonth = 5 + Math.round(i * 0.5); - }else{ - m.dom.xmonth = Math.round((i-1) * 0.5); - } - }); - } - }, - - // private - showMonthPicker : function(){ - if(!this.disabled){ - this.createMonthPicker(); - var size = this.el.getSize(); - this.monthPicker.setSize(size); - this.monthPicker.child('table').setSize(size); - - this.mpSelMonth = (this.activeDate || this.value).getMonth(); - this.updateMPMonth(this.mpSelMonth); - this.mpSelYear = (this.activeDate || this.value).getFullYear(); - this.updateMPYear(this.mpSelYear); - - this.monthPicker.slideIn('t', {duration:0.2}); - } - }, - - // private - updateMPYear : function(y){ - this.mpyear = y; - var ys = this.mpYears.elements; - for(var i = 1; i <= 10; i++){ - var td = ys[i-1], y2; - if((i%2) === 0){ - y2 = y + Math.round(i * 0.5); - td.firstChild.innerHTML = y2; - td.xyear = y2; - }else{ - y2 = y - (5-Math.round(i * 0.5)); - td.firstChild.innerHTML = y2; - td.xyear = y2; - } - this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel'); - } - }, - - // private - updateMPMonth : function(sm){ - this.mpMonths.each(function(m, a, i){ - m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel'); - }); - }, - - // private - selectMPMonth : function(m){ - - }, - - // private - onMonthClick : function(e, t){ - e.stopEvent(); - var el = new Ext.Element(t), pn; - if(el.is('button.x-date-mp-cancel')){ - this.hideMonthPicker(); - } - else if(el.is('button.x-date-mp-ok')){ - var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()); - if(d.getMonth() != this.mpSelMonth){ - // 'fix' the JS rolling date conversion if needed - d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth(); - } - this.update(d); - this.hideMonthPicker(); - } - else if((pn = el.up('td.x-date-mp-month', 2))){ - this.mpMonths.removeClass('x-date-mp-sel'); - pn.addClass('x-date-mp-sel'); - this.mpSelMonth = pn.dom.xmonth; - } - else if((pn = el.up('td.x-date-mp-year', 2))){ - this.mpYears.removeClass('x-date-mp-sel'); - pn.addClass('x-date-mp-sel'); - this.mpSelYear = pn.dom.xyear; - } - else if(el.is('a.x-date-mp-prev')){ - this.updateMPYear(this.mpyear-10); - } - else if(el.is('a.x-date-mp-next')){ - this.updateMPYear(this.mpyear+10); - } - }, - - // private - onMonthDblClick : function(e, t){ - e.stopEvent(); - var el = new Ext.Element(t), pn; - if((pn = el.up('td.x-date-mp-month', 2))){ - this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate())); - this.hideMonthPicker(); - } - else if((pn = el.up('td.x-date-mp-year', 2))){ - this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate())); - this.hideMonthPicker(); - } - }, - - // private - hideMonthPicker : function(disableAnim){ - if(this.monthPicker){ - if(disableAnim === true){ - this.monthPicker.hide(); - }else{ - this.monthPicker.slideOut('t', {duration:0.2}); - } - } - }, - - // private - showPrevMonth : function(e){ - this.update(this.activeDate.add('mo', -1)); - }, - - // private - showNextMonth : function(e){ - this.update(this.activeDate.add('mo', 1)); - }, - - // private - showPrevYear : function(){ - this.update(this.activeDate.add('y', -1)); - }, - - // private - showNextYear : function(){ - this.update(this.activeDate.add('y', 1)); - }, - - // private - handleMouseWheel : function(e){ - e.stopEvent(); - if(!this.disabled){ - var delta = e.getWheelDelta(); - if(delta > 0){ - this.showPrevMonth(); - } else if(delta < 0){ - this.showNextMonth(); - } - } - }, - - // private - handleDateClick : function(e, t){ - e.stopEvent(); - if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){ - this.setValue(new Date(t.dateValue)); - this.fireEvent('select', this, this.value); - } - }, - - // private - selectToday : function(){ - if(this.todayBtn && !this.todayBtn.disabled){ - this.setValue(new Date().clearTime()); - this.fireEvent('select', this, this.value); - } - }, - - // private - update : function(date, forceRefresh){ - var vd = this.activeDate, vis = this.isVisible(); - this.activeDate = date; - if(!forceRefresh && vd && this.el){ - var t = date.getTime(); - if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){ - this.cells.removeClass('x-date-selected'); - this.cells.each(function(c){ - if(c.dom.firstChild.dateValue == t){ - c.addClass('x-date-selected'); - if(vis){ - Ext.fly(c.dom.firstChild).focus(50); - } - return false; - } - }); - return; - } - } - var days = date.getDaysInMonth(); - var firstOfMonth = date.getFirstDateOfMonth(); - var startingPos = firstOfMonth.getDay()-this.startDay; - - if(startingPos <= this.startDay){ - startingPos += 7; - } - - var pm = date.add('mo', -1); - var prevStart = pm.getDaysInMonth()-startingPos; - - var cells = this.cells.elements; - var textEls = this.textNodes; - days += startingPos; - - // convert everything to numbers so it's fast - var day = 86400000; - var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime(); - var today = new Date().clearTime().getTime(); - var sel = date.clearTime().getTime(); - var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY; - var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY; - var ddMatch = this.disabledDatesRE; - var ddText = this.disabledDatesText; - var ddays = this.disabledDays ? this.disabledDays.join('') : false; - var ddaysText = this.disabledDaysText; - var format = this.format; - - if(this.showToday){ - var td = new Date().clearTime(); - var disable = (td < min || td > max || - (ddMatch && format && ddMatch.test(td.dateFormat(format))) || - (ddays && ddays.indexOf(td.getDay()) != -1)); - - if(!this.disabled){ - this.todayBtn.setDisabled(disable); - this.todayKeyListener[disable ? 'disable' : 'enable'](); - } - } - - var setCellClass = function(cal, cell){ - cell.title = ''; - var t = d.getTime(); - cell.firstChild.dateValue = t; - if(t == today){ - cell.className += ' x-date-today'; - cell.title = cal.todayText; - } - if(t == sel){ - cell.className += ' x-date-selected'; - if(vis){ - Ext.fly(cell.firstChild).focus(50); - } - } - // disabling - if(t < min) { - cell.className = ' x-date-disabled'; - cell.title = cal.minText; - return; - } - if(t > max) { - cell.className = ' x-date-disabled'; - cell.title = cal.maxText; - return; - } - if(ddays){ - if(ddays.indexOf(d.getDay()) != -1){ - cell.title = ddaysText; - cell.className = ' x-date-disabled'; - } - } - if(ddMatch && format){ - var fvalue = d.dateFormat(format); - if(ddMatch.test(fvalue)){ - cell.title = ddText.replace('%0', fvalue); - cell.className = ' x-date-disabled'; - } - } - }; - - var i = 0; - for(; i < startingPos; i++) { - textEls[i].innerHTML = (++prevStart); - d.setDate(d.getDate()+1); - cells[i].className = 'x-date-prevday'; - setCellClass(this, cells[i]); - } - for(; i < days; i++){ - var intDay = i - startingPos + 1; - textEls[i].innerHTML = (intDay); - d.setDate(d.getDate()+1); - cells[i].className = 'x-date-active'; - setCellClass(this, cells[i]); - } - var extraDays = 0; - for(; i < 42; i++) { - textEls[i].innerHTML = (++extraDays); - d.setDate(d.getDate()+1); - cells[i].className = 'x-date-nextday'; - setCellClass(this, cells[i]); - } - - this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear()); - - if(!this.internalRender){ - var main = this.el.dom.firstChild; - var w = main.offsetWidth; - this.el.setWidth(w + this.el.getBorderWidth('lr')); - Ext.fly(main).setWidth(w); - this.internalRender = true; - // opera does not respect the auto grow header center column - // then, after it gets a width opera refuses to recalculate - // without a second pass - if(Ext.isOpera && !this.secondPass){ - main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px'; - this.secondPass = true; - this.update.defer(10, this, [date]); - } - } - }, - - // private - beforeDestroy : function() { - if(this.rendered){ - this.keyNav.disable(); - this.keyNav = null; - Ext.destroy( - this.leftClickRpt, - this.rightClickRpt, - this.monthPicker, - this.eventEl, - this.mbtn, - this.todayBtn - ); - } - } - - /** - * @cfg {String} autoEl @hide - */ -}); - -Ext.reg('datepicker', Ext.DatePicker); +Ext.reg('colorpalette', Ext.ColorPalette); +/** + * @class Ext.DatePicker + * @extends Ext.Component + *

    A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class + * to allow browsing and selection of valid dates.

    + *

    All the string values documented below may be overridden by including an Ext locale file in + * your page.

    + * @constructor + * Create a new DatePicker + * @param {Object} config The config object + * @xtype datepicker + */ +Ext.DatePicker = Ext.extend(Ext.BoxComponent, { + /** + * @cfg {String} todayText + * The text to display on the button that selects the current date (defaults to 'Today') + */ + todayText : 'Today', + /** + * @cfg {String} okText + * The text to display on the ok button (defaults to ' OK ' to give the user extra clicking room) + */ + okText : ' OK ', + /** + * @cfg {String} cancelText + * The text to display on the cancel button (defaults to 'Cancel') + */ + cancelText : 'Cancel', + /** + * @cfg {Function} handler + * Optional. A function that will handle the select event of this picker. + * The handler is passed the following parameters:
      + *
    • picker : DatePicker
      This DatePicker.
    • + *
    • date : Date
      The selected date.
    • + *
    + */ + /** + * @cfg {Object} scope + * The scope (this reference) in which the {@link #handler} + * function will be called. Defaults to this DatePicker instance. + */ + /** + * @cfg {String} todayTip + * A string used to format the message for displaying in a tooltip over the button that + * selects the current date. Defaults to '{0} (Spacebar)' where + * the {0} token is replaced by today's date. + */ + todayTip : '{0} (Spacebar)', + /** + * @cfg {String} minText + * The error text to display if the minDate validation fails (defaults to 'This date is before the minimum date') + */ + minText : 'This date is before the minimum date', + /** + * @cfg {String} maxText + * The error text to display if the maxDate validation fails (defaults to 'This date is after the maximum date') + */ + maxText : 'This date is after the maximum date', + /** + * @cfg {String} format + * The default date format string which can be overriden for localization support. The format must be + * valid according to {@link Date#parseDate} (defaults to 'm/d/y'). + */ + format : 'm/d/y', + /** + * @cfg {String} disabledDaysText + * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled') + */ + disabledDaysText : 'Disabled', + /** + * @cfg {String} disabledDatesText + * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled') + */ + disabledDatesText : 'Disabled', + /** + * @cfg {Array} monthNames + * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames) + */ + monthNames : Date.monthNames, + /** + * @cfg {Array} dayNames + * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames) + */ + dayNames : Date.dayNames, + /** + * @cfg {String} nextText + * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)') + */ + nextText : 'Next Month (Control+Right)', + /** + * @cfg {String} prevText + * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)') + */ + prevText : 'Previous Month (Control+Left)', + /** + * @cfg {String} monthYearText + * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)') + */ + monthYearText : 'Choose a month (Control+Up/Down to move years)', + /** + * @cfg {Number} startDay + * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday) + */ + startDay : 0, + /** + * @cfg {Boolean} showToday + * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar + * that selects the current date (defaults to true). + */ + showToday : true, + /** + * @cfg {Date} minDate + * Minimum allowable date (JavaScript date object, defaults to null) + */ + /** + * @cfg {Date} maxDate + * Maximum allowable date (JavaScript date object, defaults to null) + */ + /** + * @cfg {Array} disabledDays + * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null). + */ + /** + * @cfg {RegExp} disabledDatesRE + * JavaScript regular expression used to disable a pattern of dates (defaults to null). The {@link #disabledDates} + * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the + * disabledDates value. + */ + /** + * @cfg {Array} disabledDates + * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular + * expression so they are very powerful. Some examples: + *
      + *
    • ['03/08/2003', '09/16/2003'] would disable those exact dates
    • + *
    • ['03/08', '09/16'] would disable those days for every year
    • + *
    • ['^03/08'] would only match the beginning (useful if you are using short years)
    • + *
    • ['03/../2006'] would disable every day in March 2006
    • + *
    • ['^03'] would disable every day in every March
    • + *
    + * Note that the format of the dates included in the array should exactly match the {@link #format} config. + * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to + * escape the dot when restricting dates. For example: ['03\\.08\\.03']. + */ + + // private + // Set by other components to stop the picker focus being updated when the value changes. + focusOnSelect: true, + + // private + initComponent : function(){ + Ext.DatePicker.superclass.initComponent.call(this); + + this.value = this.value ? + this.value.clearTime(true) : new Date().clearTime(); + + this.addEvents( + /** + * @event select + * Fires when a date is selected + * @param {DatePicker} this DatePicker + * @param {Date} date The selected date + */ + 'select' + ); + + if(this.handler){ + this.on('select', this.handler, this.scope || this); + } + + this.initDisabledDays(); + }, + + // private + initDisabledDays : function(){ + if(!this.disabledDatesRE && this.disabledDates){ + var dd = this.disabledDates, + len = dd.length - 1, + re = '(?:'; + + Ext.each(dd, function(d, i){ + re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i]; + if(i != len){ + re += '|'; + } + }, this); + this.disabledDatesRE = new RegExp(re + ')'); + } + }, + + /** + * Replaces any existing disabled dates with new values and refreshes the DatePicker. + * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config + * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates. + */ + setDisabledDates : function(dd){ + if(Ext.isArray(dd)){ + this.disabledDates = dd; + this.disabledDatesRE = null; + }else{ + this.disabledDatesRE = dd; + } + this.initDisabledDays(); + this.update(this.value, true); + }, + + /** + * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker. + * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config + * for details on supported values. + */ + setDisabledDays : function(dd){ + this.disabledDays = dd; + this.update(this.value, true); + }, + + /** + * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker. + * @param {Date} value The minimum date that can be selected + */ + setMinDate : function(dt){ + this.minDate = dt; + this.update(this.value, true); + }, + + /** + * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker. + * @param {Date} value The maximum date that can be selected + */ + setMaxDate : function(dt){ + this.maxDate = dt; + this.update(this.value, true); + }, + + /** + * Sets the value of the date field + * @param {Date} value The date to set + */ + setValue : function(value){ + this.value = value.clearTime(true); + this.update(this.value); + }, + + /** + * Gets the current selected value of the date field + * @return {Date} The selected date + */ + getValue : function(){ + return this.value; + }, + + // private + focus : function(){ + this.update(this.activeDate); + }, + + // private + onEnable: function(initial){ + Ext.DatePicker.superclass.onEnable.call(this); + this.doDisabled(false); + this.update(initial ? this.value : this.activeDate); + if(Ext.isIE){ + this.el.repaint(); + } + + }, + + // private + onDisable : function(){ + Ext.DatePicker.superclass.onDisable.call(this); + this.doDisabled(true); + if(Ext.isIE && !Ext.isIE8){ + /* Really strange problem in IE6/7, when disabled, have to explicitly + * repaint each of the nodes to get them to display correctly, simply + * calling repaint on the main element doesn't appear to be enough. + */ + Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){ + Ext.fly(el).repaint(); + }); + } + }, + + // private + doDisabled : function(disabled){ + this.keyNav.setDisabled(disabled); + this.prevRepeater.setDisabled(disabled); + this.nextRepeater.setDisabled(disabled); + if(this.showToday){ + this.todayKeyListener.setDisabled(disabled); + this.todayBtn.setDisabled(disabled); + } + }, + + // private + onRender : function(container, position){ + var m = [ + '', + '', + '', + this.showToday ? '' : '', + '
      
    '], + dn = this.dayNames, + i; + for(i = 0; i < 7; i++){ + var d = this.startDay+i; + if(d > 6){ + d = d-7; + } + m.push(''); + } + m[m.length] = ''; + for(i = 0; i < 42; i++) { + if(i % 7 === 0 && i !== 0){ + m[m.length] = ''; + } + m[m.length] = ''; + } + m.push('
    ', dn[d].substr(0,1), '
    '); + + var el = document.createElement('div'); + el.className = 'x-date-picker'; + el.innerHTML = m.join(''); + + container.dom.insertBefore(el, position); + + this.el = Ext.get(el); + this.eventEl = Ext.get(el.firstChild); + + this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), { + handler: this.showPrevMonth, + scope: this, + preventDefault:true, + stopDefault:true + }); + + this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), { + handler: this.showNextMonth, + scope: this, + preventDefault:true, + stopDefault:true + }); + + this.monthPicker = this.el.down('div.x-date-mp'); + this.monthPicker.enableDisplayMode('block'); + + this.keyNav = new Ext.KeyNav(this.eventEl, { + 'left' : function(e){ + if(e.ctrlKey){ + this.showPrevMonth(); + }else{ + this.update(this.activeDate.add('d', -1)); + } + }, + + 'right' : function(e){ + if(e.ctrlKey){ + this.showNextMonth(); + }else{ + this.update(this.activeDate.add('d', 1)); + } + }, + + 'up' : function(e){ + if(e.ctrlKey){ + this.showNextYear(); + }else{ + this.update(this.activeDate.add('d', -7)); + } + }, + + 'down' : function(e){ + if(e.ctrlKey){ + this.showPrevYear(); + }else{ + this.update(this.activeDate.add('d', 7)); + } + }, + + 'pageUp' : function(e){ + this.showNextMonth(); + }, + + 'pageDown' : function(e){ + this.showPrevMonth(); + }, + + 'enter' : function(e){ + e.stopPropagation(); + return true; + }, + + scope : this + }); + + this.el.unselectable(); + + this.cells = this.el.select('table.x-date-inner tbody td'); + this.textNodes = this.el.query('table.x-date-inner tbody span'); + + this.mbtn = new Ext.Button({ + text: ' ', + tooltip: this.monthYearText, + renderTo: this.el.child('td.x-date-middle', true) + }); + this.mbtn.el.child('em').addClass('x-btn-arrow'); + + if(this.showToday){ + this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday, this); + var today = (new Date()).dateFormat(this.format); + this.todayBtn = new Ext.Button({ + renderTo: this.el.child('td.x-date-bottom', true), + text: String.format(this.todayText, today), + tooltip: String.format(this.todayTip, today), + handler: this.selectToday, + scope: this + }); + } + this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this); + this.mon(this.eventEl, 'click', this.handleDateClick, this, {delegate: 'a.x-date-date'}); + this.mon(this.mbtn, 'click', this.showMonthPicker, this); + this.onEnable(true); + }, + + // private + createMonthPicker : function(){ + if(!this.monthPicker.dom.firstChild){ + var buf = ['']; + for(var i = 0; i < 6; i++){ + buf.push( + '', + '', + i === 0 ? + '' : + '' + ); + } + buf.push( + '', + '
    ', Date.getShortMonthName(i), '', Date.getShortMonthName(i + 6), '
    ' + ); + this.monthPicker.update(buf.join('')); + + this.mon(this.monthPicker, 'click', this.onMonthClick, this); + this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this); + + this.mpMonths = this.monthPicker.select('td.x-date-mp-month'); + this.mpYears = this.monthPicker.select('td.x-date-mp-year'); + + this.mpMonths.each(function(m, a, i){ + i += 1; + if((i%2) === 0){ + m.dom.xmonth = 5 + Math.round(i * 0.5); + }else{ + m.dom.xmonth = Math.round((i-1) * 0.5); + } + }); + } + }, + + // private + showMonthPicker : function(){ + if(!this.disabled){ + this.createMonthPicker(); + var size = this.el.getSize(); + this.monthPicker.setSize(size); + this.monthPicker.child('table').setSize(size); + + this.mpSelMonth = (this.activeDate || this.value).getMonth(); + this.updateMPMonth(this.mpSelMonth); + this.mpSelYear = (this.activeDate || this.value).getFullYear(); + this.updateMPYear(this.mpSelYear); + + this.monthPicker.slideIn('t', {duration:0.2}); + } + }, + + // private + updateMPYear : function(y){ + this.mpyear = y; + var ys = this.mpYears.elements; + for(var i = 1; i <= 10; i++){ + var td = ys[i-1], y2; + if((i%2) === 0){ + y2 = y + Math.round(i * 0.5); + td.firstChild.innerHTML = y2; + td.xyear = y2; + }else{ + y2 = y - (5-Math.round(i * 0.5)); + td.firstChild.innerHTML = y2; + td.xyear = y2; + } + this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel'); + } + }, + + // private + updateMPMonth : function(sm){ + this.mpMonths.each(function(m, a, i){ + m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel'); + }); + }, + + // private + selectMPMonth : function(m){ + + }, + + // private + onMonthClick : function(e, t){ + e.stopEvent(); + var el = new Ext.Element(t), pn; + if(el.is('button.x-date-mp-cancel')){ + this.hideMonthPicker(); + } + else if(el.is('button.x-date-mp-ok')){ + var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()); + if(d.getMonth() != this.mpSelMonth){ + // 'fix' the JS rolling date conversion if needed + d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth(); + } + this.update(d); + this.hideMonthPicker(); + } + else if((pn = el.up('td.x-date-mp-month', 2))){ + this.mpMonths.removeClass('x-date-mp-sel'); + pn.addClass('x-date-mp-sel'); + this.mpSelMonth = pn.dom.xmonth; + } + else if((pn = el.up('td.x-date-mp-year', 2))){ + this.mpYears.removeClass('x-date-mp-sel'); + pn.addClass('x-date-mp-sel'); + this.mpSelYear = pn.dom.xyear; + } + else if(el.is('a.x-date-mp-prev')){ + this.updateMPYear(this.mpyear-10); + } + else if(el.is('a.x-date-mp-next')){ + this.updateMPYear(this.mpyear+10); + } + }, + + // private + onMonthDblClick : function(e, t){ + e.stopEvent(); + var el = new Ext.Element(t), pn; + if((pn = el.up('td.x-date-mp-month', 2))){ + this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate())); + this.hideMonthPicker(); + } + else if((pn = el.up('td.x-date-mp-year', 2))){ + this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate())); + this.hideMonthPicker(); + } + }, + + // private + hideMonthPicker : function(disableAnim){ + if(this.monthPicker){ + if(disableAnim === true){ + this.monthPicker.hide(); + }else{ + this.monthPicker.slideOut('t', {duration:0.2}); + } + } + }, + + // private + showPrevMonth : function(e){ + this.update(this.activeDate.add('mo', -1)); + }, + + // private + showNextMonth : function(e){ + this.update(this.activeDate.add('mo', 1)); + }, + + // private + showPrevYear : function(){ + this.update(this.activeDate.add('y', -1)); + }, + + // private + showNextYear : function(){ + this.update(this.activeDate.add('y', 1)); + }, + + // private + handleMouseWheel : function(e){ + e.stopEvent(); + if(!this.disabled){ + var delta = e.getWheelDelta(); + if(delta > 0){ + this.showPrevMonth(); + } else if(delta < 0){ + this.showNextMonth(); + } + } + }, + + // private + handleDateClick : function(e, t){ + e.stopEvent(); + if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){ + this.cancelFocus = this.focusOnSelect === false; + this.setValue(new Date(t.dateValue)); + delete this.cancelFocus; + this.fireEvent('select', this, this.value); + } + }, + + // private + selectToday : function(){ + if(this.todayBtn && !this.todayBtn.disabled){ + this.setValue(new Date().clearTime()); + this.fireEvent('select', this, this.value); + } + }, + + // private + update : function(date, forceRefresh){ + if(this.rendered){ + var vd = this.activeDate, vis = this.isVisible(); + this.activeDate = date; + if(!forceRefresh && vd && this.el){ + var t = date.getTime(); + if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){ + this.cells.removeClass('x-date-selected'); + this.cells.each(function(c){ + if(c.dom.firstChild.dateValue == t){ + c.addClass('x-date-selected'); + if(vis && !this.cancelFocus){ + Ext.fly(c.dom.firstChild).focus(50); + } + return false; + } + }, this); + return; + } + } + var days = date.getDaysInMonth(), + firstOfMonth = date.getFirstDateOfMonth(), + startingPos = firstOfMonth.getDay()-this.startDay; + + if(startingPos < 0){ + startingPos += 7; + } + days += startingPos; + + var pm = date.add('mo', -1), + prevStart = pm.getDaysInMonth()-startingPos, + cells = this.cells.elements, + textEls = this.textNodes, + // convert everything to numbers so it's fast + day = 86400000, + d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime(), + today = new Date().clearTime().getTime(), + sel = date.clearTime(true).getTime(), + min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY, + max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY, + ddMatch = this.disabledDatesRE, + ddText = this.disabledDatesText, + ddays = this.disabledDays ? this.disabledDays.join('') : false, + ddaysText = this.disabledDaysText, + format = this.format; + + if(this.showToday){ + var td = new Date().clearTime(), + disable = (td < min || td > max || + (ddMatch && format && ddMatch.test(td.dateFormat(format))) || + (ddays && ddays.indexOf(td.getDay()) != -1)); + + if(!this.disabled){ + this.todayBtn.setDisabled(disable); + this.todayKeyListener[disable ? 'disable' : 'enable'](); + } + } + + var setCellClass = function(cal, cell){ + cell.title = ''; + var t = d.getTime(); + cell.firstChild.dateValue = t; + if(t == today){ + cell.className += ' x-date-today'; + cell.title = cal.todayText; + } + if(t == sel){ + cell.className += ' x-date-selected'; + if(vis){ + Ext.fly(cell.firstChild).focus(50); + } + } + // disabling + if(t < min) { + cell.className = ' x-date-disabled'; + cell.title = cal.minText; + return; + } + if(t > max) { + cell.className = ' x-date-disabled'; + cell.title = cal.maxText; + return; + } + if(ddays){ + if(ddays.indexOf(d.getDay()) != -1){ + cell.title = ddaysText; + cell.className = ' x-date-disabled'; + } + } + if(ddMatch && format){ + var fvalue = d.dateFormat(format); + if(ddMatch.test(fvalue)){ + cell.title = ddText.replace('%0', fvalue); + cell.className = ' x-date-disabled'; + } + } + }; + + var i = 0; + for(; i < startingPos; i++) { + textEls[i].innerHTML = (++prevStart); + d.setDate(d.getDate()+1); + cells[i].className = 'x-date-prevday'; + setCellClass(this, cells[i]); + } + for(; i < days; i++){ + var intDay = i - startingPos + 1; + textEls[i].innerHTML = (intDay); + d.setDate(d.getDate()+1); + cells[i].className = 'x-date-active'; + setCellClass(this, cells[i]); + } + var extraDays = 0; + for(; i < 42; i++) { + textEls[i].innerHTML = (++extraDays); + d.setDate(d.getDate()+1); + cells[i].className = 'x-date-nextday'; + setCellClass(this, cells[i]); + } + + this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear()); + + if(!this.internalRender){ + var main = this.el.dom.firstChild, + w = main.offsetWidth; + this.el.setWidth(w + this.el.getBorderWidth('lr')); + Ext.fly(main).setWidth(w); + this.internalRender = true; + // opera does not respect the auto grow header center column + // then, after it gets a width opera refuses to recalculate + // without a second pass + if(Ext.isOpera && !this.secondPass){ + main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px'; + this.secondPass = true; + this.update.defer(10, this, [date]); + } + } + } + }, + + // private + beforeDestroy : function() { + if(this.rendered){ + Ext.destroy( + this.keyNav, + this.monthPicker, + this.eventEl, + this.mbtn, + this.nextRepeater, + this.prevRepeater, + this.cells.el, + this.todayBtn + ); + delete this.textNodes; + delete this.cells.elements; + } + } + + /** + * @cfg {String} autoEl @hide + */ +}); + +Ext.reg('datepicker', Ext.DatePicker); /** * @class Ext.LoadMask * A simple utility class for generically masking elements while loading data. If the {@link #store} @@ -10488,16 +11113,22 @@ Ext.LoadMask = function(el, config){ this.el = Ext.get(el); Ext.apply(this, config); if(this.store){ - this.store.on('beforeload', this.onBeforeLoad, this); - this.store.on('load', this.onLoad, this); - this.store.on('exception', this.onLoad, this); + this.store.on({ + scope: this, + beforeload: this.onBeforeLoad, + load: this.onLoad, + exception: this.onLoad + }); this.removeMask = Ext.value(this.removeMask, false); }else{ var um = this.el.getUpdater(); um.showLoadIndicator = false; // disable the default indicator - um.on('beforeupdate', this.onBeforeLoad, this); - um.on('update', this.onLoad, this); - um.on('failure', this.onLoad, this); + um.on({ + scope: this, + beforeupdate: this.onBeforeLoad, + update: this.onLoad, + failure: this.onLoad + }); this.removeMask = Ext.value(this.removeMask, true); } }; @@ -10741,12 +11372,13 @@ Ext.Slider = Ext.extend(Ext.BoxComponent, { autoStart: 300 }); this.tracker.initEl(this.thumb); - this.on('beforedestroy', this.tracker.destroy, this.tracker); }, // private override onMouseDown : function(e){ - if(this.disabled) {return;} + if(this.disabled){ + return; + } if(this.clickToChange && e.target != this.thumb.dom){ var local = this.innerEl.translatePoints(e.getXY()); this.onClickChange(local); @@ -10791,11 +11423,12 @@ Ext.Slider = Ext.extend(Ext.BoxComponent, { // private doSnap : function(value){ - if(!this.increment || this.increment == 1 || !value) { + if(!(this.increment && value)){ return value; } - var newValue = value, inc = this.increment; - var m = value % inc; + var newValue = value, + inc = this.increment, + m = value % inc; if(m != 0){ newValue -= m; if(m * 2 > inc){ @@ -10823,8 +11456,8 @@ Ext.Slider = Ext.extend(Ext.BoxComponent, { // private getRatio : function(){ - var w = this.innerEl.getWidth(); - var v = this.maxValue - this.minValue; + var w = this.innerEl.getWidth(), + v = this.maxValue - this.minValue; return v == 0 ? w : (w/v); }, @@ -10857,12 +11490,12 @@ Ext.Slider = Ext.extend(Ext.BoxComponent, { // private translateValue : function(v){ var ratio = this.getRatio(); - return (v * ratio)-(this.minValue * ratio)-this.halfThumb; + return (v * ratio) - (this.minValue * ratio) - this.halfThumb; }, reverseValue : function(pos){ var ratio = this.getRatio(); - return (pos+this.halfThumb+(this.minValue * ratio))/ratio; + return (pos + this.halfThumb + (this.minValue * ratio)) / ratio; }, // private @@ -10938,7 +11571,7 @@ Ext.Slider = Ext.extend(Ext.BoxComponent, { this.thumb.removeClass(this.disabledClass); if(Ext.isIE){ this.innerEl.removeClass(this.disabledClass).dom.disabled = false; - if (this.thumbHolder){ + if(this.thumbHolder){ this.thumbHolder.hide(); } this.thumb.show(); @@ -10964,6 +11597,12 @@ Ext.Slider = Ext.extend(Ext.BoxComponent, { */ getValue : function(){ return this.value; + }, + + // private + beforeDestroy : function(){ + Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder'); + Ext.Slider.superclass.beforeDestroy.call(this); } }); Ext.reg('slider', Ext.Slider); @@ -10976,8 +11615,8 @@ Ext.Slider.Vertical = { }, getRatio : function(){ - var h = this.innerEl.getHeight(); - var v = this.maxValue - this.minValue; + var h = this.innerEl.getHeight(), + v = this.maxValue - this.minValue; return h/v; }, @@ -10990,15 +11629,15 @@ Ext.Slider.Vertical = { }, onDrag: function(e){ - var pos = this.innerEl.translatePoints(this.tracker.getXY()); - var bottom = this.innerEl.getHeight()-pos.top; + var pos = this.innerEl.translatePoints(this.tracker.getXY()), + bottom = this.innerEl.getHeight()-pos.top; this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false); this.fireEvent('drag', this, e); }, onClickChange : function(local){ if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){ - var bottom = this.innerEl.getHeight()-local.top; + var bottom = this.innerEl.getHeight() - local.top; this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true); } } @@ -11195,6 +11834,7 @@ myAction.on('complete', function(){ this.waitTimer = Ext.TaskMgr.start({ run: function(i){ var inc = o.increment || 10; + i -= 1; this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate); }, interval: o.interval || 1000, @@ -11282,6 +11922,16 @@ myAction.on('complete', function(){ this.hide(); } return this; + }, + + onDestroy: function(){ + if(this.rendered){ + if(this.textEl.isComposite){ + this.textEl.clear(); + } + Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl'); + } + Ext.ProgressBar.superclass.onDestroy.call(this); } }); Ext.reg('progress', Ext.ProgressBar); \ No newline at end of file