X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/25ef3491bd9ae007ff1fc2b0d7943e6eaaccf775..6e39d509471fe9b4e2660e0d1631b350d0c66f40:/pkgs/cmp-foundation-debug.js?ds=sidebyside diff --git a/pkgs/cmp-foundation-debug.js b/pkgs/cmp-foundation-debug.js index 22f6c94c..b791522b 100644 --- a/pkgs/cmp-foundation-debug.js +++ b/pkgs/cmp-foundation-debug.js @@ -1,5 +1,5 @@ /*! - * Ext JS Library 3.0.3 + * Ext JS Library 3.1.0 * Copyright(c) 2006-2009 Ext JS, LLC * licensing@extjs.com * http://www.extjs.com/license @@ -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){ @@ -133,7 +133,12 @@ Ext.ComponentMgr = function(){ * @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); + } } }; }(); @@ -236,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} @@ -305,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. @@ -344,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 @@ -439,7 +459,7 @@ Ext.Component = function(config){ } if(this.stateful !== false){ - this.initState(config); + this.initState(); } if(this.applyTo){ @@ -950,6 +970,53 @@ new Ext.Panel({ */ 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', @@ -1065,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(); @@ -1078,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({
@@ -1109,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); } } @@ -1240,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; + } } } }, @@ -1267,9 +1433,12 @@ var myGrid = new Ext.grid.EditorGridPanel({ destroy : function(){ 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.removeAllListeners(); this.el.remove(); if(this.actionMode == 'container' || this.removeMode == 'container'){ this.container.remove(); @@ -1279,11 +1448,19 @@ var myGrid = new Ext.grid.EditorGridPanel({ Ext.ComponentMgr.unregister(this); this.fireEvent('destroy', this); this.purgeListeners(); + this.destroying = false; this.isDestroyed = true; } } }, + deleteMembers : function(){ + var args = arguments; + for(var i = 0, len = args.length; i < len; ++i){ + delete this[args[i]]; + } + }, + // private beforeDestroy : Ext.emptyFn, @@ -1315,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:

@@ -1596,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
@@ -1615,7 +1798,7 @@ alert(t.getXTypes());  // alerts 'component/box/field/textfield'
         }, this);
         this.mons = [];
     },
-    
+
     // private
     createMons: function(){
         if(!this.mons){
@@ -1624,7 +1807,26 @@ alert(t.getXTypes());  // alerts 'component/box/field/textfield'
         }
     },
 
-    // internal function for auto removal of assigned event handlers on destruction
+    /**
+     * 

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)){ @@ -1658,7 +1860,15 @@ alert(t.getXTypes()); // alerts 'component/box/field/textfield' 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(); @@ -1711,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 @@ -1765,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 ''). */ @@ -1804,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, @@ -1906,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; @@ -1918,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); @@ -1957,7 +2167,7 @@ Ext.Action.prototype = { execute : function(){ this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments); } -}; +}); /** * @class Ext.Layer * @extends Ext.Element @@ -2128,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(){ @@ -2417,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 @@ -2700,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 @@ -2774,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 @@ -2829,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; } @@ -2846,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){ @@ -2858,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; }, @@ -2968,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; }, /** @@ -3048,6 +3305,7 @@ var myPanel = new Ext.Panel({ this.positionEl = Ext.get(this.positionEl); } this.boxReady = true; + this.setAutoScroll(this.autoScroll); this.setSize(this.width, this.height); if(this.x || this.y){ this.setPosition(this.x, this.y); @@ -3075,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 @@ -3137,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"; @@ -3156,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 @@ -3167,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); @@ -3197,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); @@ -3222,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 @@ -3267,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 ); @@ -3276,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 ); @@ -3285,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){ @@ -3298,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] @@ -3319,7 +3577,7 @@ Ext.extend(Ext.SplitBar, Ext.util.Observable, { } } }, - + /** * Get the adapter this SplitBar uses * @return The adapter object @@ -3327,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 @@ -3336,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 @@ -3344,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 @@ -3352,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 @@ -3360,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 @@ -3368,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 @@ -3379,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(); } }); @@ -3399,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. @@ -3417,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){ @@ -3430,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 @@ -3448,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){ @@ -3461,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. @@ -3478,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){ @@ -3547,1180 +3805,1255 @@ Ext.SplitBar.TOP = 3; * @type Number */ Ext.SplitBar.BOTTOM = 4; -/** - * @class Ext.Container - * @extends Ext.BoxComponent - *

Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the - * basic behavior of containing items, namely adding, inserting and removing items.

- * - *

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 - * 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:


-// explicitly create a Container
-var embeddedColumns = new Ext.Container({
-    autoEl: 'div',  // This is the default
-    layout: 'column',
-    defaults: {
-        // implicitly create Container by specifying xtype
-        xtype: 'container',
-        autoEl: 'div', // This is the default.
-        layout: 'form',
-        columnWidth: 0.5,
-        style: {
-            padding: '10px'
-        }
-    },
-//  The two items below will be Ext.Containers, each encapsulated by a <DIV> element.
-    items: [{
-        items: {
-            xtype: 'datefield',
-            name: 'startDate',
-            fieldLabel: 'Start date'
-        }
-    }, {
-        items: {
-            xtype: 'datefield',
-            name: 'endDate',
-            fieldLabel: 'End date'
-        }
-    }]
-});

- * - *

Layout

- *

Container classes delegate the rendering of child Components to a layout - * manager class which must be configured into the Container using the - * {@link #layout} configuration property.

- *

When either specifying child {@link #items} of a Container, - * or dynamically {@link #add adding} Components to a Container, remember to - * consider how you wish the Container to arrange those child elements, and - * whether those child elements need to be sized using one of Ext's built-in - * {@link #layout} schemes. By default, Containers use the - * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only - * renders child components, appending them one after the other inside the - * 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} - * 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 - * is resized.

- *

Certain layout managers allow dynamic addition of child components. - * Those that do include {@link Ext.layout.CardLayout}, - * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and - * {@link Ext.layout.TableLayout}. For example:


-//  Create the GridPanel.
-var myNewGrid = new Ext.grid.GridPanel({
-    store: myStore,
-    columns: myColumnModel,
-    title: 'Results', // the title becomes the title of the tab
-});
-
-myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
-myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
- * 

- *

The example above adds a newly created GridPanel to a TabPanel. Note that - * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which - * means all its child items are sized to {@link Ext.layout.FitLayout fit} - * exactly into its client area. - *

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 - * 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 - * GridPanel will not be sized as expected.

- * - *

Adding via remote configuration

- * - *

A server side script can be used to add Components which are generated dynamically on the server. - * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server - * based on certain parameters: - *


-// execute an Ajax request to invoke server side script:
-Ext.Ajax.request({
-    url: 'gen-invoice-grid.php',
-    // send additional parameters to instruct server script
-    params: {
-        startDate: Ext.getCmp('start-date').getValue(),
-        endDate: Ext.getCmp('end-date').getValue()
-    },
-    // process the response object to add it to the TabPanel:
-    success: function(xhr) {
-        var newComponent = eval(xhr.responseText); // see discussion below
-        myTabPanel.add(newComponent); // add the component to the TabPanel
-        myTabPanel.setActiveTab(newComponent);
-    },
-    failure: function() {
-        Ext.Msg.alert("Grid create failed", "Server communication failure");
-    }
-});
-
- *

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}, - * or an instantiated Component. The server might return this for example:


-(function() {
-    function formatDate(value){
-        return value ? value.dateFormat('M d, Y') : '';
-    };
-
-    var store = new Ext.data.Store({
-        url: 'get-invoice-data.php',
-        baseParams: {
-            startDate: '01/01/2008',
-            endDate: '01/31/2008'
-        },
-        reader: new Ext.data.JsonReader({
-            record: 'transaction',
-            idProperty: 'id',
-            totalRecords: 'total'
-        }, [
-           'customer',
-           'invNo',
-           {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
-           {name: 'value', type: 'float'}
-        ])
-    });
-
-    var grid = new Ext.grid.GridPanel({
-        title: 'Invoice Report',
-        bbar: new Ext.PagingToolbar(store),
-        store: store,
-        columns: [
-            {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
-            {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
-            {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
-            {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
-        ],
-    });
-    store.load();
-    return grid;  // return instantiated component
-})();
-
- *

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 - * 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.

- * - * @xtype container - */ -Ext.Container = Ext.extend(Ext.BoxComponent, { - /** - * @cfg {Boolean} monitorResize - * True to automatically monitor window resize events to handle anything that is sensitive to the current size - * of the viewport. This value is typically managed by the chosen {@link #layout} and should not need - * to be set manually. - */ - /** - * @cfg {String/Object} layout - *

*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')
-    items: [{
-        title: 'Panel inside a Window'
-    }]
-}).show();
-     * 
- *

If the {@link #layout} configuration is not explicitly specified for - * a general purpose container (e.g. Container or Panel) 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). - * Some container classes implicitly specify a default layout - * (e.g. FormPanel specifies layout:'form'). Other specific - * purpose classes internally specify/manage their internal layout (e.g. - * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).

- *

layout may be specified as either as an Object or - * as a String:

- */ - /** - * @cfg {Object} layoutConfig - * This is a config object containing properties specific to the chosen - * {@link #layout} if {@link #layout} - * has been specified as a string.

- */ - /** - * @cfg {Boolean/Number} bufferResize - * 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. Defaults to 50. - */ - bufferResize: 50, - - /** - * @cfg {String/Number} activeItem - * A string component id or the numeric index of the component that should be initially activated within the - * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first - * item in the container's collection). activeItem only applies to layout styles that can display - * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and - * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}. - */ - /** - * @cfg {Object/Array} items - *
** 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:

- *

-// specifying a single item
-items: {...},
-layout: 'fit',    // specify a layout!
-
-// specifying multiple items
-items: [{...}, {...}],
-layout: 'anchor', // specify a layout!
-     * 
- *

Each item may be:

- *
- *

Notes:

- *
- */ - /** - * @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:


-defaults: {               // defaults are applied to items, not the container
-    autoScroll:true
-},
-items: [
-    {
-        xtype: 'panel',   // defaults do not have precedence over
-        id: 'panel1',     // options in config objects, so the defaults
-        autoScroll: false // will not be applied here, panel1 will be autoScroll:false
-    },
-    new Ext.Panel({       // defaults do have precedence over options
-        id: 'panel2',     // options in components, so the defaults
-        autoScroll: false // will be applied here, panel2 will be autoScroll:true.
-    })
-]
-     * 
- */ - - - /** @cfg {Boolean} autoDestroy - * If true the container will automatically destroy any contained component that is removed from it, else - * destruction must be handled manually (defaults to true). - */ - autoDestroy : true, - - /** @cfg {Boolean} forceLayout - * If true the container will force a layout initially even if hidden or collapsed. This option - * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false). - */ - forceLayout: false, - - /** @cfg {Boolean} hideBorders - * True to hide the borders of each contained component, false to defer to the component's existing - * border settings (defaults to false). - */ - /** @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'.

- */ - 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. - * Defaults to ['add', 'remove']. - */ - bubbleEvents: ['add', 'remove'], - - // private - initComponent : function(){ - Ext.Container.superclass.initComponent.call(this); - - this.addEvents( - /** - * @event afterlayout - * Fires when the components in this container are arranged by the associated layout manager. - * @param {Ext.Container} this - * @param {ContainerLayout} layout The ContainerLayout implementation for this container - */ - 'afterlayout', - /** - * @event beforeadd - * Fires before any {@link Ext.Component} is added or inserted into the container. - * A handler can return false to cancel the add. - * @param {Ext.Container} this - * @param {Ext.Component} component The component being added - * @param {Number} index The index at which the component will be added to the container's items collection - */ - 'beforeadd', - /** - * @event beforeremove - * Fires before any {@link Ext.Component} is removed from the container. A handler can return - * false to cancel the remove. - * @param {Ext.Container} this - * @param {Ext.Component} component The component being removed - */ - 'beforeremove', - /** - * @event add - * @bubbles - * Fires after any {@link Ext.Component} is added or inserted into the container. - * @param {Ext.Container} this - * @param {Ext.Component} component The component that was added - * @param {Number} index The index at which the component was added to the container's items collection - */ - 'add', - /** - * @event remove - * @bubbles - * Fires after any {@link Ext.Component} is removed from the container. - * @param {Ext.Container} this - * @param {Ext.Component} component The component that was removed - */ - 'remove' - ); - - this.enableBubble(this.bubbleEvents); - - /** - * The collection of components in this container as a {@link Ext.util.MixedCollection} - * @type MixedCollection - * @property items - */ - var items = this.items; - if(items){ - delete this.items; - this.add(items); - } - }, - - // private - initItems : function(){ - if(!this.items){ - this.items = new Ext.util.MixedCollection(false, this.getComponentId); - this.getLayout(); // initialize the layout - } - }, - - // private - setLayout : function(layout){ - if(this.layout && this.layout != layout){ - this.layout.setContainer(null); - } - this.initItems(); - this.layout = layout; - layout.setContainer(this); - }, - - afterRender: function(){ - Ext.Container.superclass.afterRender.call(this); - 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); - } - if(!this.ownerCt){ - // force a layout if no ownerCt is set - this.doLayout(false, true); - } - if(this.monitorResize === true){ - Ext.EventManager.onWindowResize(this.doLayout, this, [false]); - } - }, - - /** - *

Returns the Element to be used to contain the child Components of this Container.

- *

An implementation is provided which returns the Container's {@link #getEl Element}, but - * if there is a more complex structure to a Container, this may be overridden to return - * the element into which the {@link #layout layout} renders child Components.

- * @return {Ext.Element} The Element to render child Components into. - */ - getLayoutTarget : function(){ - return this.el; - }, - - // private - used as the key lookup function for the items collection - getComponentId : function(comp){ - return comp.getItemId(); - }, - - /** - *

Adds {@link Ext.Component Component}(s) to this Container.

- *

Description : - *

- *

Notes : - *

- * @param {Object/Array} component - *

Either a single component or an Array of components to add. See - * {@link #items} for additional information.

- * @param {Object} (Optional) component_2 - * @param {Object} (Optional) component_n - * @return {Ext.Component} component The Component (or config object) that was added. - */ - add : function(comp){ - this.initItems(); - var args = arguments.length > 1; - if(args || Ext.isArray(comp)){ - Ext.each(args ? arguments : comp, function(c){ - this.add(c); - }, this); - return; - } - var c = this.lookupComponent(this.applyDefaults(comp)); - var pos = this.items.length; - if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){ - this.items.add(c); - c.ownerCt = this; - this.onAdd(c); - this.fireEvent('add', this, c, pos); - } - return c; - }, - - onAdd : function(c){ - // Empty template method - }, - - /** - * 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 - * Component has been inserted. - * @param {Number} index The index at which the Component will be inserted - * into the Container's items collection - * @param {Ext.Component} component The child Component to insert.

- * Ext uses lazy rendering, and will only render the inserted Component should - * it become necessary.

- * A Component config object may be passed in order to avoid the overhead of - * constructing a real Component object if lazy rendering might mean that the - * inserted Component will not be rendered immediately. To take advantage of - * this 'lazy instantiation', set the {@link Ext.Component#xtype} config - * property to the registered type of the Component wanted.

- * For a list of all available xtypes, see {@link Ext.Component}. - * @return {Ext.Component} component The Component (or config object) that was - * inserted with the Container's default config values applied. - */ - insert : function(index, comp){ - this.initItems(); - var a = arguments, len = a.length; - if(len > 2){ - for(var i = len-1; i >= 1; --i) { - this.insert(index, a[i]); - } - return; - } - var c = this.lookupComponent(this.applyDefaults(comp)); - 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; - this.onAdd(c); - this.fireEvent('add', this, c, index); - } - return c; - }, - - // private - applyDefaults : function(c){ - if(this.defaults){ - if(Ext.isString(c)){ - c = Ext.ComponentMgr.get(c); - Ext.apply(c, this.defaults); - }else if(!c.events){ - Ext.applyIf(c, this.defaults); - }else{ - Ext.apply(c, this.defaults); - } - } - return c; - }, - - // private - onBeforeAdd : function(item){ - if(item.ownerCt){ - item.ownerCt.remove(item, false); - } - if(this.hideBorders === true){ - item.border = (item.border === true); - } - }, - - /** - * Removes a component from this container. Fires the {@link #beforeremove} event before removing, then fires - * the {@link #remove} event after the component has been removed. - * @param {Component/String} component The component reference or id to remove. - * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. - * Defaults to the value of this Container's {@link #autoDestroy} config. - * @return {Ext.Component} component The Component that was removed. - */ - remove : function(comp, autoDestroy){ - this.initItems(); - var c = this.getComponent(comp); - if(c && this.fireEvent('beforeremove', this, c) !== false){ - delete c.ownerCt; - if(this.layout && this.rendered){ - this.layout.onRemove(c); - } - this.onRemove(c); - this.items.remove(c); - if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){ - c.destroy(); - } - this.fireEvent('remove', this, c); - } - return c; - }, - - onRemove: function(c){ - // Empty template method - }, - - /** - * Removes all components from this container. - * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. - * Defaults to the value of this Container's {@link #autoDestroy} config. - * @return {Array} Array of the destroyed components - */ - removeAll: function(autoDestroy){ - this.initItems(); - var item, rem = [], items = []; - this.items.each(function(i){ - rem.push(i); - }); - for (var i = 0, len = rem.length; i < len; ++i){ - item = rem[i]; - this.remove(item, autoDestroy); - if(item.ownerCt !== this){ - items.push(item); - } - } - return items; - }, - - /** - * Examines this container's {@link #items} property - * and gets a direct child component of this container. - * @param {String/Number} comp This parameter may be any of the following: - *
- *

For additional information see {@link Ext.util.MixedCollection#get}. - * @return Ext.Component The component (if found). - */ - getComponent : function(comp){ - if(Ext.isObject(comp)){ - comp = comp.getItemId(); - } - return this.items.get(comp); - }, - - // private - lookupComponent : function(comp){ - if(Ext.isString(comp)){ - return Ext.ComponentMgr.get(comp); - }else if(!comp.events){ - return this.createComponent(comp); - } - return comp; - }, - - // private - createComponent : function(config){ - return Ext.create(config, this.defaultType); - }, - - // private - canLayout: function() { - var el = this.getVisibilityEl(); - return el && !el.isStyle("display", "none"); - }, - - - /** - * 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. - * @return {Ext.Container} this - */ - doLayout: function(shallow, force){ - var rendered = this.rendered; - forceLayout = force || this.forceLayout; - - if(!this.canLayout() || this.collapsed){ - this.deferLayout = this.deferLayout || !shallow; - if(!forceLayout){ - return; - } - shallow = shallow && !this.deferLayout; - } else { - delete this.deferLayout; - } - 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.doLayout(false, forceLayout); - } - } - } - if(rendered){ - this.onLayout(shallow, forceLayout); - } - // Initial layout completed - this.hasLayout = true; - delete this.forceLayout; - }, - - //private - onLayout : Ext.emptyFn, - - // private - shouldBufferLayout: function(){ - /* - * Returns true if the container should buffer a layout. - * This is true only if the container has previously been laid out - * and has a parent container that is pending a layout. - */ - var hl = this.hasLayout; - if(this.ownerCt){ - // Only ever buffer if we've laid out the first time and we have one pending. - return hl ? !this.hasLayoutPending() : false; - } - // Never buffer initial layout - return hl; - }, - - // private - hasLayoutPending: function(){ - // Traverse hierarchy to see if any parent container has a pending layout. - var pending = false; - this.ownerCt.bubble(function(c){ - if(c.layoutPending){ - pending = true; - return false; - } - }); - return pending; - }, - - onShow : function(){ - Ext.Container.superclass.onShow.call(this); - if(this.deferLayout !== undefined){ - this.doLayout(true); - } - }, - - /** - * Returns the layout currently in use by the container. If the container does not currently have a layout - * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout. - * @return {ContainerLayout} layout The container's layout - */ - getLayout : function(){ - if(!this.layout){ - var layout = new Ext.layout.ContainerLayout(this.layoutConfig); - this.setLayout(layout); - } - return this.layout; - }, - - // private - beforeDestroy : function(){ - if(this.items){ - Ext.destroy.apply(Ext, this.items.items); - } - if(this.monitorResize){ - Ext.EventManager.removeResizeListener(this.doLayout, this); - } - Ext.destroy(this.layout); - Ext.Container.superclass.beforeDestroy.call(this); - }, - - /** - * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (this) of - * function call will be the scope provided or the current component. The arguments to the function - * will be the args provided or the current component. If the function returns false at any point, - * the bubble is stopped. - * @param {Function} fn The function to call - * @param {Object} scope (optional) The scope of the function (defaults to current node) - * @param {Array} args (optional) The args to call the function with (default to passing the current component) - * @return {Ext.Container} this - */ - bubble : function(fn, scope, args){ - var p = this; - while(p){ - if(fn.apply(scope || p, args || [p]) === false){ - break; - } - p = p.ownerCt; - } - return this; - }, - - /** - * Cascades down the component/container heirarchy from this component (called first), calling the specified function with - * each component. The scope (this) of - * function call will be the scope provided or the current component. The arguments to the function - * will be the args provided or the current component. If the function returns false at any point, - * the cascade is stopped on that branch. - * @param {Function} fn The function to call - * @param {Object} scope (optional) The scope of the function (defaults to current component) - * @param {Array} args (optional) The args to call the function with (defaults to passing the current component) - * @return {Ext.Container} this - */ - cascade : function(fn, scope, args){ - if(fn.apply(scope || this, args || [this]) !== false){ - if(this.items){ - var cs = this.items.items; - for(var i = 0, len = cs.length; i < len; i++){ - if(cs[i].cascade){ - cs[i].cascade(fn, scope, args); - }else{ - fn.apply(scope || cs[i], args || [cs[i]]); - } - } - } - } - return this; - }, - - /** - * Find a component under this container at any level by id - * @param {String} id - * @return Ext.Component - */ - findById : function(id){ - var m, ct = this; - this.cascade(function(c){ - if(ct != c && c.id === id){ - m = c; - return false; - } - }); - return m || null; - }, - - /** - * Find a component under this container at any level by xtype or class - * @param {String/Class} xtype The xtype string for a component, or the class of the component directly - * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is - * the default), or true to check whether this Component is directly of the specified xtype. - * @return {Array} Array of Ext.Components - */ - findByType : function(xtype, shallow){ - return this.findBy(function(c){ - return c.isXType(xtype, shallow); - }); - }, - - /** - * Find a component under this container at any level by property - * @param {String} prop - * @param {String} value - * @return {Array} Array of Ext.Components - */ - find : function(prop, value){ - return this.findBy(function(c){ - return c[prop] === value; - }); - }, - - /** - * Find a component under this container at any level by a custom function. If the passed function returns - * true, the component will be included in the results. The passed function is called with the arguments (component, this container). - * @param {Function} fn The function to call - * @param {Object} scope (optional) - * @return {Array} Array of Ext.Components - */ - findBy : function(fn, scope){ - var m = [], ct = this; - this.cascade(function(c){ - if(ct != c && fn.call(scope || c, c, ct) === true){ - m.push(c); - } - }); - return m; - }, - - /** - * Get a component contained by this container (alias for items.get(key)) - * @param {String/Number} key The index or id of the component - * @return {Ext.Component} Ext.Component - */ - get : function(key){ - return this.items.get(key); - } -}); - -Ext.Container.LAYOUTS = {}; -Ext.reg('container', Ext.Container); /** - * @class Ext.layout.ContainerLayout - *

The ContainerLayout class is the default layout manager delegated by {@link Ext.Container} to - * render any child Components when no {@link Ext.Container#layout layout} is configured into - * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout - * classes in Ext. It simply renders all child Components into the Container, performing no sizing or - * positioning services. To utilize a layout that provides sizing and positioning of child Components, - * specify an appropriate {@link Ext.Container#layout layout}.

- *

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); -}; + * @class Ext.Container + * @extends Ext.BoxComponent + *

Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the + * basic behavior of containing items, namely adding, inserting and removing items.

+ * + *

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 + * 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:


+// explicitly create a Container
+var embeddedColumns = new Ext.Container({
+    autoEl: 'div',  // This is the default
+    layout: 'column',
+    defaults: {
+        // implicitly create Container by specifying xtype
+        xtype: 'container',
+        autoEl: 'div', // This is the default.
+        layout: 'form',
+        columnWidth: 0.5,
+        style: {
+            padding: '10px'
+        }
+    },
+//  The two items below will be Ext.Containers, each encapsulated by a <DIV> element.
+    items: [{
+        items: {
+            xtype: 'datefield',
+            name: 'startDate',
+            fieldLabel: 'Start date'
+        }
+    }, {
+        items: {
+            xtype: 'datefield',
+            name: 'endDate',
+            fieldLabel: 'End date'
+        }
+    }]
+});

+ * + *

Layout

+ *

Container classes delegate the rendering of child Components to a layout + * manager class which must be configured into the Container using the + * {@link #layout} configuration property.

+ *

When either specifying child {@link #items} of a Container, + * or dynamically {@link #add adding} Components to a Container, remember to + * consider how you wish the Container to arrange those child elements, and + * whether those child elements need to be sized using one of Ext's built-in + * {@link #layout} schemes. By default, Containers use the + * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only + * renders child components, appending them one after the other inside the + * 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} + * 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 + * is resized.

+ *

Certain layout managers allow dynamic addition of child components. + * Those that do include {@link Ext.layout.CardLayout}, + * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and + * {@link Ext.layout.TableLayout}. For example:


+//  Create the GridPanel.
+var myNewGrid = new Ext.grid.GridPanel({
+    store: myStore,
+    columns: myColumnModel,
+    title: 'Results', // the title becomes the title of the tab
+});
 
-Ext.layout.ContainerLayout.prototype = {
-    /**
-     * @cfg {String} extraCls
-     * 

An optional extra CSS class that will be added to the container. This can be useful for adding - * customized styles to the container or any of its children using standard CSS rules. See - * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.

- *

Note: extraCls defaults to '' except for the following classes - * which assign a value by default: - *

    - *
  • {@link Ext.layout.AbsoluteLayout Absolute Layout} : 'x-abs-layout-item'
  • - *
  • {@link Ext.layout.Box Box Layout} : 'x-box-item'
  • - *
  • {@link Ext.layout.ColumnLayout Column Layout} : 'x-column'
  • - *
- * To configure the above Classes with an extra CSS class append to the default. For example, - * for ColumnLayout:

-     * extraCls: 'x-column custom-class'
+myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
+myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
+ * 

+ *

The example above adds a newly created GridPanel to a TabPanel. Note that + * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which + * means all its child items are sized to {@link Ext.layout.FitLayout fit} + * exactly into its client area. + *

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 + * 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 + * GridPanel will not be sized as expected.

+ * + *

Adding via remote configuration

+ * + *

A server side script can be used to add Components which are generated dynamically on the server. + * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server + * based on certain parameters: + *


+// execute an Ajax request to invoke server side script:
+Ext.Ajax.request({
+    url: 'gen-invoice-grid.php',
+    // send additional parameters to instruct server script
+    params: {
+        startDate: Ext.getCmp('start-date').getValue(),
+        endDate: Ext.getCmp('end-date').getValue()
+    },
+    // process the response object to add it to the TabPanel:
+    success: function(xhr) {
+        var newComponent = eval(xhr.responseText); // see discussion below
+        myTabPanel.add(newComponent); // add the component to the TabPanel
+        myTabPanel.setActiveTab(newComponent);
+    },
+    failure: function() {
+        Ext.Msg.alert("Grid create failed", "Server communication failure");
+    }
+});
+
+ *

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}, + * or an instantiated Component. The server might return this for example:


+(function() {
+    function formatDate(value){
+        return value ? value.dateFormat('M d, Y') : '';
+    };
+
+    var store = new Ext.data.Store({
+        url: 'get-invoice-data.php',
+        baseParams: {
+            startDate: '01/01/2008',
+            endDate: '01/31/2008'
+        },
+        reader: new Ext.data.JsonReader({
+            record: 'transaction',
+            idProperty: 'id',
+            totalRecords: 'total'
+        }, [
+           'customer',
+           'invNo',
+           {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
+           {name: 'value', type: 'float'}
+        ])
+    });
+
+    var grid = new Ext.grid.GridPanel({
+        title: 'Invoice Report',
+        bbar: new Ext.PagingToolbar(store),
+        store: store,
+        columns: [
+            {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
+            {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
+            {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
+            {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
+        ],
+    });
+    store.load();
+    return grid;  // return instantiated component
+})();
+
+ *

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 + * 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.

+ * + * @xtype container + */ +Ext.Container = Ext.extend(Ext.BoxComponent, { + /** + * @cfg {Boolean} monitorResize + * True to automatically monitor window resize events to handle anything that is sensitive to the current size + * of the viewport. This value is typically managed by the chosen {@link #layout} and should not need + * to be set manually. + */ + /** + * @cfg {String/Object} layout + *

*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')
+    items: [{
+        title: 'Panel inside a Window'
+    }]
+}).show();
      * 
- *

+ *

If the {@link #layout} configuration is not explicitly specified for + * a general purpose container (e.g. Container or Panel) 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). + * Some container classes implicitly specify a default layout + * (e.g. FormPanel specifies layout:'form'). Other specific + * purpose classes internally specify/manage their internal layout (e.g. + * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).

+ *

layout may be specified as either as an Object or + * as a String:

    + * + *
  • Specify as an Object
  • + *
      + *
    • Example usage:
    • +
      
      +layout: {
      +    type: 'vbox',
      +    padding: '5',
      +    align: 'left'
      +}
      +
      + * + *
    • 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:

      + *
        + *
      • {@link Ext.layout.AbsoluteLayout absolute}
      • + *
      • {@link Ext.layout.AccordionLayout accordion}
      • + *
      • {@link Ext.layout.AnchorLayout anchor}
      • + *
      • {@link Ext.layout.ContainerLayout auto}     Default
      • + *
      • {@link Ext.layout.BorderLayout border}
      • + *
      • {@link Ext.layout.CardLayout card}
      • + *
      • {@link Ext.layout.ColumnLayout column}
      • + *
      • {@link Ext.layout.FitLayout fit}
      • + *
      • {@link Ext.layout.FormLayout form}
      • + *
      • {@link Ext.layout.HBoxLayout hbox}
      • + *
      • {@link Ext.layout.MenuLayout menu}
      • + *
      • {@link Ext.layout.TableLayout table}
      • + *
      • {@link Ext.layout.ToolbarLayout toolbar}
      • + *
      • {@link Ext.layout.VBoxLayout vbox}
      • + *
      + * + *
    • 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 + * specified.

      + * + *
    + * + *
  • Specify as a String
  • + *
      + *
    • Example usage:
    • +
      
      +layout: 'vbox',
      +layoutConfig: {
      +    padding: '5',
      +    align: 'left'
      +}
      +
      + *
    • layout
    • + *

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


      + *
    • {@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.

      + *
*/ /** - * @cfg {Boolean} renderHidden - * True to hide each contained item on render (defaults to false). + * @cfg {Object} layoutConfig + * This is a config object containing properties specific to the chosen + * {@link #layout} if {@link #layout} + * has been specified as a string.

+ */ + /** + * @cfg {Boolean/Number} bufferResize + * 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. Defaults to 50. */ + bufferResize: 50, /** - * A reference to the {@link Ext.Component} that is active. For example,

-     * if(myPanel.layout.activeItem.id == 'item-1') { ... }
-     * 
- * activeItem only applies to layout styles that can display items one at a time - * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} - * and {@link Ext.layout.FitLayout}). Read-only. Related to {@link Ext.Container#activeItem}. - * @type {Ext.Component} - * @property activeItem + * @cfg {String/Number} activeItem + * A string component id or the numeric index of the component that should be initially activated within the + * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first + * item in the container's collection). activeItem only applies to layout styles that can display + * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and + * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}. */ + /** + * @cfg {Object/Array} items + *
** 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:

+ *

+// specifying a single item
+items: {...},
+layout: 'fit',    // specify a layout!
 
-    // private
-    monitorResize:false,
-    // private
-    activeItem : null,
+// specifying multiple items
+items: [{...}, {...}],
+layout: 'anchor', // specify a layout!
+     * 
+ *

Each item may be:

+ *
    + *
  • any type of object based on {@link Ext.Component}
  • + *
  • a fully instanciated object or
  • + *
  • an object literal that:
  • + *
      + *
    • has a specified {@link Ext.Component#xtype xtype}
    • + *
    • the {@link Ext.Component#xtype} specified is associated with the Component + * desired and should be chosen from one of the available xtypes as listed + * in {@link Ext.Component}.
    • + *
    • If an {@link Ext.Component#xtype xtype} is not explicitly + * specified, the {@link #defaultType} for that Container is used.
    • + *
    • will be "lazily instanciated", avoiding the overhead of constructing a fully + * instanciated Component object
    • + *
+ *

Notes:

+ *
    + *
  • Ext uses lazy rendering. Child Components will only be rendered + * should it become necessary. Items are automatically laid out when they are first + * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.
  • + *
  • Do not specify {@link Ext.Panel#contentEl contentEl}/ + * {@link Ext.Panel#html html} with items.
  • + *
+ */ + /** + * @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
+},
+items: [
+    {
+        xtype: 'panel',   // defaults do not have precedence over
+        id: 'panel1',     // options in config objects, so the defaults
+        autoScroll: false // will not be applied here, panel1 will be autoScroll:false
+    },
+    new Ext.Panel({       // defaults do have precedence over options
+        id: 'panel2',     // options in components, so the defaults
+        autoScroll: false // will be applied here, panel2 will be autoScroll:true.
+    })
+]
+     * 
+ */ + + + /** @cfg {Boolean} autoDestroy + * If true the container will automatically destroy any contained component that is removed from it, else + * destruction must be handled manually (defaults to true). + */ + autoDestroy : true, + + /** @cfg {Boolean} forceLayout + * If true the container will force a layout initially even if hidden or collapsed. This option + * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false). + */ + forceLayout: false, + + /** @cfg {Boolean} hideBorders + * True to hide the borders of each contained component, false to defer to the component's existing + * border settings (defaults to false). + */ + /** @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'.

+ */ + 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 - layout : function(){ - var target = this.container.getLayoutTarget(); - this.onLayout(this.container, target); - this.container.fireEvent('afterlayout', this.container, this); + initComponent : function(){ + Ext.Container.superclass.initComponent.call(this); + + this.addEvents( + /** + * @event afterlayout + * Fires when the components in this container are arranged by the associated layout manager. + * @param {Ext.Container} this + * @param {ContainerLayout} layout The ContainerLayout implementation for this container + */ + 'afterlayout', + /** + * @event beforeadd + * Fires before any {@link Ext.Component} is added or inserted into the container. + * A handler can return false to cancel the add. + * @param {Ext.Container} this + * @param {Ext.Component} component The component being added + * @param {Number} index The index at which the component will be added to the container's items collection + */ + 'beforeadd', + /** + * @event beforeremove + * Fires before any {@link Ext.Component} is removed from the container. A handler can return + * false to cancel the remove. + * @param {Ext.Container} this + * @param {Ext.Component} component The component being removed + */ + 'beforeremove', + /** + * @event add + * @bubbles + * Fires after any {@link Ext.Component} is added or inserted into the container. + * @param {Ext.Container} this + * @param {Ext.Component} component The component that was added + * @param {Number} index The index at which the component was added to the container's items collection + */ + 'add', + /** + * @event remove + * @bubbles + * Fires after any {@link Ext.Component} is removed from the container. + * @param {Ext.Container} this + * @param {Ext.Component} component The component that was removed + */ + 'remove' + ); + + this.enableBubble(this.bubbleEvents); + + /** + * The collection of components in this container as a {@link Ext.util.MixedCollection} + * @type MixedCollection + * @property items + */ + var items = this.items; + if(items){ + delete this.items; + this.add(items); + } }, // private - onLayout : function(ct, target){ - this.renderAll(ct, target); + initItems : function(){ + if(!this.items){ + this.items = new Ext.util.MixedCollection(false, this.getComponentId); + this.getLayout(); // initialize the layout + } }, // private - isValidParent : function(c, target){ - return target && c.getDomPositionEl().dom.parentNode == (target.dom || target); + setLayout : function(layout){ + if(this.layout && this.layout != layout){ + this.layout.setContainer(null); + } + this.initItems(); + this.layout = layout; + layout.setContainer(this); + }, + + 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); + + // 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 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]); + } + }, + + /** + *

Returns the Element to be used to contain the child Components of this Container.

+ *

An implementation is provided which returns the Container's {@link #getEl Element}, but + * if there is a more complex structure to a Container, this may be overridden to return + * the element into which the {@link #layout layout} renders child Components.

+ * @return {Ext.Element} The Element to render child Components into. + */ + getLayoutTarget : function(){ + return this.el; + }, + + // private - used as the key lookup function for the items collection + getComponentId : function(comp){ + return comp.getItemId(); + }, + + /** + *

Adds {@link Ext.Component Component}(s) to this Container.

+ *

Description : + *

    + *
  • Fires the {@link #beforeadd} event before adding
  • + *
  • The Container's {@link #defaults default config values} will be applied + * accordingly (see {@link #defaults} for details).
  • + *
  • Fires the {@link #add} event after the component has been added.
  • + *
+ *

Notes : + *

    + *
  • 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 + * once. For example:
    
    +var tb = new {@link Ext.Toolbar}();
    +tb.render(document.body);  // toolbar is rendered
    +tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
    +tb.add({text:'Button 2'});
    +tb.{@link #doLayout}();             // refresh the layout
    +     * 
  • + *
  • Warning: Containers directly managed by the BorderLayout layout manager + * may not be removed or added. See the Notes for {@link Ext.layout.BorderLayout BorderLayout} + * for more details.
  • + *
+ * @param {Object/Array} component + *

Either a single component or an Array of components to add. See + * {@link #items} for additional information.

+ * @param {Object} (Optional) component_2 + * @param {Object} (Optional) component_n + * @return {Ext.Component} component The Component (or config object) that was added. + */ + add : function(comp){ + this.initItems(); + var args = arguments.length > 1; + if(args || Ext.isArray(comp)){ + var result = []; + Ext.each(args ? arguments : comp, function(c){ + result.push(this.add(c)); + }, this); + return result; + } + var c = this.lookupComponent(this.applyDefaults(comp)); + var index = this.items.length; + if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ + this.items.add(c); + // *onAdded + c.onAdded(this, index); + this.onAdd(c); + this.fireEvent('add', this, c, index); + } + return c; }, + onAdd : function(c){ + // Empty template method + }, + // private - renderAll : function(ct, target){ - var items = ct.items.items; - for(var i = 0, len = items.length; i < len; i++) { - var c = items[i]; - if(c && (!c.rendered || !this.isValidParent(c, target))){ - this.renderItem(c, i, target); + 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 + * Component has been inserted. + * @param {Number} index The index at which the Component will be inserted + * into the Container's items collection + * @param {Ext.Component} component The child Component to insert.

+ * Ext uses lazy rendering, and will only render the inserted Component should + * it become necessary.

+ * A Component config object may be passed in order to avoid the overhead of + * constructing a real Component object if lazy rendering might mean that the + * inserted Component will not be rendered immediately. To take advantage of + * this 'lazy instantiation', set the {@link Ext.Component#xtype} config + * property to the registered type of the Component wanted.

+ * For a list of all available xtypes, see {@link Ext.Component}. + * @return {Ext.Component} component The Component (or config object) that was + * inserted with the Container's default config values applied. + */ + insert : function(index, comp){ + this.initItems(); + var a = arguments, len = a.length; + if(len > 2){ + var result = []; + for(var i = len-1; i >= 1; --i) { + result.push(this.insert(index, a[i])); + } + return result; + } + var c = this.lookupComponent(this.applyDefaults(comp)); + 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.onAdded(this, index); + this.onAdd(c); + this.fireEvent('add', this, c, index); } + return c; }, // private - renderItem : function(c, position, target){ - if(c && !c.rendered){ - c.render(target, position); - this.configureItem(c, position); - }else if(c && !this.isValidParent(c, target)){ - if(Ext.isNumber(position)){ - position = target.dom.childNodes[position]; + applyDefaults : function(c){ + 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, d); + }else if(!c.events){ + Ext.applyIf(c, d); + }else{ + Ext.apply(c, d); } - target.dom.insertBefore(c.getDomPositionEl().dom, position || null); - c.container = target; - this.configureItem(c, position); } + return c; }, - + // private - configureItem: function(c, position){ - if(this.extraCls){ - var t = c.getPositionEl ? c.getPositionEl() : c; - t.addClass(this.extraCls); + onBeforeAdd : function(item){ + if(item.ownerCt){ + item.ownerCt.remove(item, false); } - if (this.renderHidden && c != this.activeItem) { - c.hide(); + if(this.hideBorders === true){ + item.border = (item.border === true); } - if(c.doLayout && this.forceLayout){ - c.doLayout(false, true); + }, + + /** + * Removes a component from this container. Fires the {@link #beforeremove} event before removing, then fires + * the {@link #remove} event after the component has been removed. + * @param {Component/String} component The component reference or id to remove. + * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Ext.Component} component The Component that was removed. + */ + remove : function(comp, autoDestroy){ + this.initItems(); + var c = this.getComponent(comp); + if(c && this.fireEvent('beforeremove', this, c) !== false){ + this.doRemove(c, autoDestroy); + this.fireEvent('remove', this, c); } + return c; }, - + 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); - } + // Empty template method }, // private - onResize: function(){ - var ct = this.container, - b; - - if(ct.collapsed){ - return; + doRemove: function(c, autoDestroy){ + if(this.layout && this.rendered){ + this.layout.onRemove(c); } - if(b = ct.bufferResize){ - // Only allow if we should buffer the layout - if(ct.shouldBufferLayout()){ - 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.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. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Array} Array of the destroyed components + */ + removeAll: function(autoDestroy){ + this.initItems(); + var item, rem = [], items = []; + this.items.each(function(i){ + rem.push(i); + }); + for (var i = 0, len = rem.length; i < len; ++i){ + item = rem[i]; + this.remove(item, autoDestroy); + if(item.ownerCt !== this){ + items.push(item); } - }else{ - ct.doLayout(); } + return items; }, - + + /** + * Examines this container's {@link #items} property + * 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} + * or {@link Ext.Component#id id} 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}. + * @return Ext.Component The component (if found). + */ + getComponent : function(comp){ + if(Ext.isObject(comp)){ + comp = comp.getItemId(); + } + return this.items.get(comp); + }, + // private - runLayout: function(){ - var ct = this.container; - ct.doLayout(); - delete ct.layoutPending; + lookupComponent : function(comp){ + if(Ext.isString(comp)){ + return Ext.ComponentMgr.get(comp); + }else if(!comp.events){ + return this.createComponent(comp); + } + return comp; }, // private - setContainer : function(ct){ - if(this.monitorResize && ct != this.container){ - var old = this.container; - if(old){ - old.un(old.resizeEvent, this.onResize, this); + 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; + }, + + /** + * 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. + * @return {Ext.Container} this + */ + doLayout: function(shallow, force){ + var rendered = this.rendered, + forceLayout = force || this.forceLayout, + cs, i, len, c; + + this.layoutDone = true; + if(!this.canLayout() || this.collapsed){ + this.deferLayout = this.deferLayout || !shallow; + if(!forceLayout){ + return; } - if(ct){ - ct.on(ct.resizeEvent, this.onResize, this); + 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; } } - this.container = ct; + +// 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(); + } + +// 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, 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 - parseMargins : function(v){ - if(Ext.isNumber(v)){ - v = v.toString(); + 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(Ext.isDefined(this.deferLayout)){ + this.doLayout(true); } - var ms = v.split(' '); - var len = ms.length; - if(len == 1){ - ms[1] = ms[0]; - ms[2] = ms[0]; - ms[3] = ms[0]; + }, + + /** + * Returns the layout currently in use by the container. If the container does not currently have a layout + * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout. + * @return {ContainerLayout} layout The container's layout + */ + getLayout : function(){ + if(!this.layout){ + var layout = new Ext.layout.ContainerLayout(this.layoutConfig); + this.setLayout(layout); } - if(len == 2){ - ms[2] = ms[0]; - ms[3] = ms[1]; + return this.layout; + }, + + // private + beforeDestroy : function(){ + var c; + if(this.items){ + while(c = this.items.first()){ + this.doRemove(c, true); + } } - if(len == 3){ - ms[3] = ms[1]; + if(this.monitorResize){ + Ext.EventManager.removeResizeListener(this.doLayout, this); } - return { - top:parseInt(ms[0], 10) || 0, - right:parseInt(ms[1], 10) || 0, - bottom:parseInt(ms[2], 10) || 0, - left:parseInt(ms[3], 10) || 0 - }; + Ext.destroy(this.layout); + Ext.Container.superclass.beforeDestroy.call(this); }, /** - * 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 - * {@link Ext.layout.FormLayout#getTemplateArgs}. - * @property fieldTpl - * @type Ext.Template - */ - fieldTpl: (function() { - var t = new Ext.Template( - '

', - '', - '
', - '
', - '
' - ); - 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 + * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (this) of + * function call will be the scope provided or the current component. The arguments to the function + * will be the args provided or the current component. If the function returns false at any point, + * the bubble is stopped. + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope of the function (defaults to current node) + * @param {Array} args (optional) The args to call the function with (default to passing the current component) + * @return {Ext.Container} this */ - destroy : Ext.emptyFn -}; -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 - * container. This class is intended to be extended or created via the layout:'fit' {@link Ext.Container#layout} - * config, and should generally not need to be created directly via the new keyword.

+ bubble : function(fn, scope, args){ + var p = this; + while(p){ + if(fn.apply(scope || p, args || [p]) === false){ + break; + } + p = p.ownerCt; + } + return this; + }, + + /** + * Cascades down the component/container heirarchy from this component (called first), calling the specified function with + * each component. The scope (this) of + * function call will be the scope provided or the current component. The arguments to the function + * will be the args provided or the current component. If the function returns false at any point, + * the cascade is stopped on that branch. + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope of the function (defaults to current component) + * @param {Array} args (optional) The args to call the function with (defaults to passing the current component) + * @return {Ext.Container} this + */ + cascade : function(fn, scope, args){ + if(fn.apply(scope || this, args || [this]) !== false){ + if(this.items){ + var cs = this.items.items; + for(var i = 0, len = cs.length; i < len; i++){ + if(cs[i].cascade){ + cs[i].cascade(fn, scope, args); + }else{ + fn.apply(scope || cs[i], args || [cs[i]]); + } + } + } + } + return this; + }, + + /** + * Find a component under this container at any level by id + * @param {String} id + * @return Ext.Component + */ + findById : function(id){ + var m, ct = this; + this.cascade(function(c){ + if(ct != c && c.id === id){ + m = c; + return false; + } + }); + return m || null; + }, + + /** + * Find a component under this container at any level by xtype or class + * @param {String/Class} xtype The xtype string for a component, or the class of the component directly + * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is + * the default), or true to check whether this Component is directly of the specified xtype. + * @return {Array} Array of Ext.Components + */ + findByType : function(xtype, shallow){ + return this.findBy(function(c){ + return c.isXType(xtype, shallow); + }); + }, + + /** + * Find a component under this container at any level by property + * @param {String} prop + * @param {String} value + * @return {Array} Array of Ext.Components + */ + find : function(prop, value){ + return this.findBy(function(c){ + return c[prop] === value; + }); + }, + + /** + * Find a component under this container at any level by a custom function. If the passed function returns + * true, the component will be included in the results. The passed function is called with the arguments (component, this container). + * @param {Function} fn The function to call + * @param {Object} scope (optional) + * @return {Array} Array of Ext.Components + */ + findBy : function(fn, scope){ + var m = [], ct = this; + this.cascade(function(c){ + if(ct != c && fn.call(scope || c, c, ct) === true){ + m.push(c); + } + }); + return m; + }, + + /** + * Get a component contained by this container (alias for items.get(key)) + * @param {String/Number} key The index or id of the component + * @return {Ext.Component} Ext.Component + */ + get : function(key){ + return this.items.get(key); + } +}); + +Ext.Container.LAYOUTS = {}; +Ext.reg('container', Ext.Container); +/** + * @class Ext.layout.ContainerLayout + *

The ContainerLayout class is the default layout manager delegated by {@link Ext.Container} to + * render any child Components when no {@link Ext.Container#layout layout} is configured into + * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout + * classes in Ext. It simply renders all child Components into the Container, performing no sizing or + * positioning services. To utilize a layout that provides sizing and positioning of child Components, + * specify an appropriate {@link Ext.Container#layout layout}.

+ *

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 = Ext.extend(Object, { + /** + * @cfg {String} extraCls + *

An optional extra CSS class that will be added to the container. This can be useful for adding + * customized styles to the container or any of its children using standard CSS rules. See + * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.

+ *

Note: extraCls defaults to '' except for the following classes + * which assign a value by default: + *

    + *
  • {@link Ext.layout.AbsoluteLayout Absolute Layout} : 'x-abs-layout-item'
  • + *
  • {@link Ext.layout.Box Box Layout} : 'x-box-item'
  • + *
  • {@link Ext.layout.ColumnLayout Column Layout} : 'x-column'
  • + *
+ * To configure the above Classes with an extra CSS class append to the default. For example, + * for ColumnLayout:

+     * extraCls: 'x-column custom-class'
+     * 
+ *

+ */ + /** + * @cfg {Boolean} renderHidden + * True to hide each contained item on render (defaults to false). + */ + + /** + * A reference to the {@link Ext.Component} that is active. For example,

+     * if(myPanel.layout.activeItem.id == 'item-1') { ... }
+     * 
+ * activeItem only applies to layout styles that can display items one at a time + * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} + * and {@link Ext.layout.FitLayout}). Read-only. Related to {@link Ext.Container#activeItem}. + * @type {Ext.Component} + * @property activeItem + */ + + // private + monitorResize:false, + // 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 + onLayout : function(ct, target){ + this.renderAll(ct, target); + }, + + // private + isValidParent : function(c, target){ + return target && c.getPositionEl().dom.parentNode == (target.dom || target); + }, + + // private + renderAll : function(ct, target){ + var items = ct.items.items; + for(var i = 0, len = items.length; i < len; i++) { + var c = items[i]; + if(c && (!c.rendered || !this.isValidParent(c, target))){ + this.renderItem(c, i, target); + } + } + }, + + // private + renderItem : function(c, position, target){ + if(c && !c.rendered){ + c.render(target, position); + this.configureItem(c, position); + }else if(c && !this.isValidParent(c, target)){ + if(Ext.isNumber(position)){ + position = target.dom.childNodes[position]; + } + 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(); + } + }, + + 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(){ + var ct = this.container, + b = ct.bufferResize; + + if (ct.collapsed){ + return; + } + + // 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); + } + }else{ + ct.doLayout(false, this.forceLayout); + } + }, + + // private + runLayout: function(){ + 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){ + var old = this.container; + if(old){ + old.un(old.resizeEvent, this.onResize, this); + } + if(ct){ + ct.on(ct.resizeEvent, this.onResize, this); + } + } + */ + this.container = ct; + }, + + // private + parseMargins : function(v){ + if(Ext.isNumber(v)){ + v = v.toString(); + } + var ms = v.split(' '); + var len = ms.length; + if(len == 1){ + ms[1] = ms[0]; + ms[2] = ms[0]; + ms[3] = ms[0]; + } + if(len == 2){ + ms[2] = ms[0]; + ms[3] = ms[1]; + } + if(len == 3){ + ms[3] = ms[1]; + } + return { + top:parseInt(ms[0], 10) || 0, + right:parseInt(ms[1], 10) || 0, + bottom:parseInt(ms[2], 10) || 0, + left:parseInt(ms[3], 10) || 0 + }; + }, + + /** + * 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 + * {@link Ext.layout.FormLayout#getTemplateArgs}. + * @property fieldTpl + * @type Ext.Template + */ + fieldTpl: (function() { + var t = new Ext.Template( + '
', + '', + '
', + '
', + '
' + ); + 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 : 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 + * container. This class is intended to be extended or created via the layout:'fit' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.

*

FitLayout does not have any direct config options (other than inherited ones). To fit a panel to a container * using FitLayout, simply set layout:'fit' on the container and add a single panel to it. If the container has * multiple panels, only the first one will be displayed. Example usage:

@@ -4744,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)); } }, @@ -4845,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); }, /** @@ -4853,18 +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(); + if(item){ + item.show(); + } this.layout(); - if(layout){ + if(item && layout){ item.doLayout(); } + item.fireEvent('activate', item); } }, @@ -4877,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 @@ -5099,7 +5439,7 @@ var p = new Ext.Panel({ layout:'column', items: [{ title: 'Column 1', - columnWidth: .25 + columnWidth: .25 },{ title: 'Column 2', columnWidth: .6 @@ -5131,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 @@ -5146,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'}); @@ -5155,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')); } } @@ -5182,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 @@ -5220,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' },{ @@ -5278,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++) { @@ -5294,7 +5636,7 @@ Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, { c.collapsed = false; if(!c.rendered){ c.render(target, i); - c.getDomPositionEl().addClass('x-border-panel'); + c.getPositionEl().addClass('x-border-panel'); } this[pos] = pos != 'center' && c.split ? new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) : @@ -5304,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; @@ -5531,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, /** @@ -5557,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, @@ -5566,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, @@ -5682,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(); @@ -5701,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(); }, @@ -5720,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'){ @@ -5739,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(); }, @@ -5871,6 +6217,7 @@ Ext.layout.BorderLayout.Region.prototype = { }; } this.el.on(this.autoHideHd); + this.collapsedEl.on(this.autoHideHd); } }, @@ -5879,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); } }, @@ -6054,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); } }; @@ -6286,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); } }); @@ -6404,17 +6754,17 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { * @type String * @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 && !this.isHide(c)){ + if(this.trackLabels){ c.un('show', this.onFieldShow, this); c.un('hide', this.onFieldHide, this); } @@ -6422,7 +6772,9 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { var el = c.getPositionEl(), ct = c.getItemCt && c.getItemCt(); if(c.rendered && ct){ - el.insertAfter(ct); + if (el && el.dom) { + el.insertAfter(ct); + } Ext.destroy(ct); Ext.destroyMembers(c, 'label', 'itemCt'); if(c.customItemCt){ @@ -6430,7 +6782,7 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { } } }, - + // private setContainer : function(ct){ Ext.layout.FormLayout.superclass.setContainer.call(this, ct); @@ -6464,17 +6816,18 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { } } }, - + + // 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); + c.getItemCt().addClass('x-hide-' + c.hideMode); }, //private @@ -6537,11 +6890,6 @@ new Ext.Template( }else{ c.itemCt = this.fieldTpl.append(target, args, true); } - 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(!c.getItemCt){ // Non form fields don't have getItemCt, apply it here // This will get cleaned up in onRemove @@ -6553,7 +6901,12 @@ new Ext.Template( }); } c.label = c.getItemCt().child('label.x-form-item-label'); - if(this.trackLabels && !this.isHide(c)){ + 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); } @@ -6591,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) { @@ -6615,7 +6968,7 @@ new Ext.Template( } return value; }, - + adjustHeightAnchor : function(value, c){ if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){ return value - c.label.getHeight(); @@ -6625,7 +6978,7 @@ new Ext.Template( // private isValidParent : function(c, target){ - return target && this.container.getEl().contains(c.getDomPositionEl()); + return target && this.container.getEl().contains(c.getPositionEl()); } /** @@ -6634,7 +6987,8 @@ 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 manages multiple Panels in an expandable accordion style such that only @@ -6740,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; } @@ -6773,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); } @@ -6799,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; @@ -6881,6 +7244,8 @@ Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { // private monitorResize:false, + + targetCls: 'x-table-layout-ct', /** * @cfg {Object} tableAttrs @@ -6915,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); } @@ -6985,7 +7348,7 @@ Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { this.configureItem(c, position); }else if(c && !this.isValidParent(c, target)){ var container = this.getNextCell(c); - container.insertBefore(c.getDomPositionEl().dom, null); + container.insertBefore(c.getPositionEl().dom, null); c.container = Ext.get(container); this.configureItem(c, position); } @@ -6993,7 +7356,7 @@ Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { // private isValidParent : function(c, target){ - return c.getDomPositionEl().up('table', 5).dom.parentNode === (target.dom || target); + return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target); } /** @@ -7078,439 +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 - *

    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', - ctCls : '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.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(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); - }, - +/** + * @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 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, - size = this.getTargetSize(target), - w = size.width - target.getPadding('lr'), - h = size.height - target.getPadding('tb') - this.scrollOffset, - 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 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 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 @@ -7537,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', @@ -7572,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'; @@ -7626,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 @@ -7984,7 +8351,7 @@ var w = new Ext.Window({ *

      • 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.
    • @@ -8069,11 +8436,7 @@ var win = new Ext.Window({ * {@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). - */ + /** * @cfg {Mixed} floating *

      This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this @@ -8109,33 +8472,6 @@ var win = new Ext.Window({ * 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 - *

      Optional. Specify an existing HTML element, or the id of an existing HTML element to use as this Panel's - * {@link #body} content.

      - *
        - *
      • 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 - * after 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 afterRender method after any configured {@link #html HTML} has - * been inserted, and so the document will not contain this element at the time the - * {@link #render} event is fired.
        - *
        The specified HTML element used will not participate in any {@link Ext.Container#layout layout} - * scheme that the Panel may use. It is just HTML. Layouts operate on child {@link Ext.Container#items 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.
      • - *
      - */ /** * @cfg {Object/Array} keys * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding} @@ -8305,7 +8641,16 @@ new Ext.Panel({ * 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'. */ @@ -8424,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; } @@ -8453,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 = []; - Ext.each(btns, function(btn){ - if(btn.render){ // button instance - this.buttons.push(btn); - }else if(btn.xtype){ - this.buttons.push(Ext.create(btn, 'button')); - }else{ - this.addButton(btn); - } - }, this); + 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]){ @@ -8513,23 +8885,23 @@ new Ext.Panel({ d = el.dom, 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); @@ -8582,7 +8954,7 @@ new Ext.Panel({ * b) The last element is reported incorrectly when using a loadmask */ this.ft = Ext.get(this.bwrap.dom.lastChild); - this.mc = Ext.get(this.bwrap.dom.firstChild.firstChild.firstChild); + this.mc = Ext.get(mc); }else{ this.createElement('header', d); this.createElement('bwrap', d); @@ -8654,50 +9026,18 @@ new Ext.Panel({ if(ts){ this.addTool.apply(this, ts); } - - 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); + } }, @@ -8731,14 +9071,12 @@ new Ext.Panel({ // private makeFloating : function(cfg){ this.floating = true; - this.el = new Ext.Layer( - Ext.isObject(cfg) ? cfg : { - shadow: Ext.isDefined(this.shadow) ? 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); }, /** @@ -8763,27 +9101,23 @@ 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(Ext.isString(config)){ - 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 @@ -8850,13 +9184,14 @@ new Ext.Panel({ syncHeight : function(){ var h = this.toolbarHeight, bd = this.body, - lsh = this.lastSize.height; - + 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); @@ -8895,7 +9230,7 @@ new Ext.Panel({ }; }, - // private + // private afterRender : function(){ if(this.floating && !this.hidden){ this.el.show(); @@ -8903,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(){ @@ -8962,6 +9275,7 @@ new Ext.Panel({ this.syncHeight(); } } + }, // private @@ -9123,49 +9437,43 @@ new Ext.Panel({ onResize : function(w, h){ if(Ext.isDefined(w) || Ext.isDefined(h)){ if(!this.collapsed){ + // 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)){ - w = this.adjustBodyWidth(w - this.getFrameWidth()); - if(this.tbar){ - this.tbar.setWidth(w); - if(this.topToolbar){ - this.topToolbar.setSize(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); } + // 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); @@ -9183,20 +9491,20 @@ new Ext.Panel({ this.on('expand', function(){ delete this.queuedExpand; this.onResize(this.queuedBodySize.width, this.queuedBodySize.height); - this.doLayout(); }, this, {single:true}); } } 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; @@ -9288,6 +9596,11 @@ 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 @@ -9345,38 +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.ft, - this.mc, - 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]); } } if(this.rendered){ - Ext.destroy(this.toolbars); + 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); + Ext.destroy( + this.topToolbar, + this.bottomToolbar + ); } - Ext.Panel.superclass.beforeDestroy.call(this); }, // private @@ -9496,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") @@ -9652,9 +9976,9 @@ Ext.extend(Ext.Editor, Ext.Component, { } if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){ this.startValue = v; + this.field.reset(); this.field.setValue(v); - this.doAutoSize(); - this.el.alignTo(this.boundEl, this.alignment); + this.realign(true); this.editing = true; this.show(); } @@ -9701,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); }, /** @@ -9808,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.
      @@ -9830,23 +10161,7 @@ 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. */ @@ -9861,6 +10176,11 @@ Ext.extend(Ext.ColorPalette, Ext.Component, { * the hex codes are case-sensitive. */ value : null, + /** + * @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', @@ -9909,18 +10229,36 @@ cp.colors = ['000000', '993300', '333300']; * 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}); @@ -9964,783 +10302,796 @@ cp.colors = ['000000', '993300', '333300']; } /** - * @cfg {String} autoEl @hide + * @cfg {String} autoEl @hide + */ +}); +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)') */ -}); -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 {Function} handler - * Optional. A function that will handle the select event of this picker. - * The handler is passed the following parameters:
        - *
      • picker : DatePicker
        The Ext.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 - * 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(true) : 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){ - 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.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){ - 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){ - Ext.fly(c.dom.firstChild).focus(50); - } - return false; - } - }); - 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){ - 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); + 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} @@ -10762,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); } }; @@ -11015,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); @@ -11065,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){ @@ -11097,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); }, @@ -11131,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 @@ -11212,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(); @@ -11238,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); @@ -11250,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; }, @@ -11264,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); } } @@ -11557,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