+
+/**
+ * @class Ext.layout.BorderLayout.Region
+ * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
+ * within the layout. Each region has its own {@link Ext.layout.ContainerLayout layout} that is
+ * independent of other regions and the containing BorderLayout, and can be any of the
+ * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
+ * <p>Region size is managed automatically and cannot be changed by the user -- for
+ * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
+ * @constructor
+ * Create a new Region.
+ * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
+ * @param {Object} config The configuration options
+ * @param {String} position The region position. Valid values are: <tt>north</tt>, <tt>south</tt>,
+ * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>. Every {@link Ext.layout.BorderLayout BorderLayout}
+ * <b>must have a center region</b> for the primary content -- all other regions are optional.
+ */
+Ext.layout.BorderLayout.Region = function(layout, config, pos){
+ Ext.apply(this, config);
+ this.layout = layout;
+ this.position = pos;
+ this.state = {};
+ if(typeof this.margins == 'string'){
+ this.margins = this.layout.parseMargins(this.margins);
+ }
+ this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
+ if(this.collapsible){
+ if(typeof this.cmargins == 'string'){
+ this.cmargins = this.layout.parseMargins(this.cmargins);
+ }
+ if(this.collapseMode == 'mini' && !this.cmargins){
+ this.cmargins = {left:0,top:0,right:0,bottom:0};
+ }else{
+ this.cmargins = Ext.applyIf(this.cmargins || {},
+ pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
+ }
+ }
+};
+
+Ext.layout.BorderLayout.Region.prototype = {
+ /**
+ * @cfg {Boolean} animFloat
+ * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
+ * panel that will close again once the user mouses out of that panel (or clicks out if
+ * <tt>{@link #autoHide} = false</tt>). Setting <tt>{@link #animFloat} = false</tt> will
+ * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
+ */
+ /**
+ * @cfg {Boolean} autoHide
+ * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
+ * panel. If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
+ * out of the panel. If <tt>autoHide = false</tt>, the panel will continue to display until the
+ * user clicks outside of the panel (defaults to <tt>true</tt>).
+ */
+ /**
+ * @cfg {String} collapseMode
+ * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
+ * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
+ * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
+ * title bar.</div></li>
+ * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
+ * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
+ * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
+ * </div></li>
+ * </ul></div></p>
+ * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
+ * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
+ * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
+ * <p>See also <tt>{@link #cmargins}</tt>.</p>
+ */
+ /**
+ * @cfg {Object} margins
+ * An object containing margins to apply to the region when in the expanded state in the
+ * format:<pre><code>
+{
+ top: (top margin),
+ right: (right margin),
+ bottom: (bottom margin),
+ left: (left margin)
+}</code></pre>
+ * <p>May also be a string containing space-separated, numeric margin values. The order of the
+ * sides associated with each value matches the way CSS processes margin values:</p>
+ * <p><div class="mdetail-params"><ul>
+ * <li>If there is only one value, it applies to all sides.</li>
+ * <li>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.</li>
+ * <li>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.</li>
+ * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
+ * </ul></div></p>
+ * <p>Defaults to:</p><pre><code>
+ * {top:0, right:0, bottom:0, left:0}
+ * </code></pre>
+ */
+ /**
+ * @cfg {Object} cmargins
+ * An object containing margins to apply to the region when in the collapsed state in the
+ * format:<pre><code>
+{
+ top: (top margin),
+ right: (right margin),
+ bottom: (bottom margin),
+ left: (left margin)
+}</code></pre>
+ * <p>May also be a string containing space-separated, numeric margin values. The order of the
+ * sides associated with each value matches the way CSS processes margin values.</p>
+ * <p><ul>
+ * <li>If there is only one value, it applies to all sides.</li>
+ * <li>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.</li>
+ * <li>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.</li>
+ * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
+ * </ul></p>
+ */
+ /**
+ * @cfg {Boolean} collapsible
+ * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>). If
+ * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
+ * bar of the region, otherwise the button will not be shown.</p>
+ * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
+ * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
+ * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
+ */
+ collapsible : false,
+ /**
+ * @cfg {Boolean} split
+ * <p><tt>true</tt> 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 <tt>false</tt> creating a
+ * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
+ * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
+ * <li>this configuration option is ignored if <tt>region='center'</tt></li>
+ * <li>when <tt>split == true</tt>, it is common to specify a
+ * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
+ * 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.</li>
+ * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
+ * for the collapse tool</tt></li>
+ * </ul></div>
+ */
+ split:false,
+ /**
+ * @cfg {Boolean} floatable
+ * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
+ * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
+ * clicking the expand button to see it again (defaults to <tt>true</tt>).
+ */
+ floatable: true,
+ /**
+ * @cfg {Number} minWidth
+ * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
+ * <tt>maxWidth</tt> may also be specified.</p><br>
+ * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
+ * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
+ * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
+ */
+ minWidth:50,
+ /**
+ * @cfg {Number} minHeight
+ * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
+ * <tt>maxHeight</tt> may also be specified.</p><br>
+ * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
+ * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
+ * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
+ */
+ minHeight:50,
+
+ // private
+ defaultMargins : {left:0,top:0,right:0,bottom:0},
+ // private
+ defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
+ // private
+ defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
+ floatingZIndex: 100,
+
+ /**
+ * True if this region is collapsed. Read-only.
+ * @type Boolean
+ * @property
+ */
+ isCollapsed : false,
+
+ /**
+ * This region's panel. Read-only.
+ * @type Ext.Panel
+ * @property panel
+ */
+ /**
+ * This region's layout. Read-only.
+ * @type Layout
+ * @property layout
+ */
+ /**
+ * This region's layout position (north, south, east, west or center). Read-only.
+ * @type String
+ * @property position
+ */
+
+ // private
+ render : function(ct, p){
+ this.panel = p;
+ p.el.enableDisplayMode();
+ this.targetEl = ct;
+ this.el = p.el;
+
+ var gs = p.getState, ps = this.position;
+ p.getState = function(){
+ return Ext.apply(gs.call(p) || {}, this.state);
+ }.createDelegate(this);
+
+ if(ps != 'center'){
+ p.allowQueuedExpand = false;
+ p.on({
+ beforecollapse: this.beforeCollapse,
+ collapse: this.onCollapse,
+ beforeexpand: this.beforeExpand,
+ expand: this.onExpand,
+ hide: this.onHide,
+ show: this.onShow,
+ scope: this
+ });
+ if(this.collapsible || this.floatable){
+ p.collapseEl = 'el';
+ p.slideAnchor = this.getSlideAnchor();
+ }
+ if(p.tools && p.tools.toggle){
+ p.tools.toggle.addClass('x-tool-collapse-'+ps);
+ p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
+ }
+ }
+ },
+
+ // private
+ getCollapsedEl : function(){
+ if(!this.collapsedEl){
+ if(!this.toolTemplate){
+ var tt = new Ext.Template(
+ '<div class="x-tool x-tool-{id}"> </div>'
+ );
+ tt.disableFormats = true;
+ tt.compile();
+ Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
+ }
+ this.collapsedEl = this.targetEl.createChild({
+ cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
+ id: this.panel.id + '-xcollapsed'
+ });
+ this.collapsedEl.enableDisplayMode('block');
+
+ if(this.collapseMode == 'mini'){
+ this.collapsedEl.addClass('x-layout-cmini-'+this.position);
+ this.miniCollapsedEl = this.collapsedEl.createChild({
+ cls: "x-layout-mini x-layout-mini-"+this.position, html: " "
+ });
+ this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
+ this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
+ this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
+ }else {
+ if(this.collapsible !== false && !this.hideCollapseTool) {
+ var t = this.expandToolEl = this.toolTemplate.append(
+ this.collapsedEl.dom,
+ {id:'expand-'+this.position}, true);
+ t.addClassOnOver('x-tool-expand-'+this.position+'-over');
+ t.on('click', this.onExpandClick, this, {stopEvent:true});
+ }
+ if(this.floatable !== false || this.titleCollapse){
+ this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
+ this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
+ }
+ }
+ }
+ return this.collapsedEl;
+ },
+
+ // private
+ onExpandClick : function(e){
+ if(this.isSlid){
+ this.panel.expand(false);
+ }else{
+ this.panel.expand();
+ }
+ },
+
+ // private
+ onCollapseClick : function(e){
+ this.panel.collapse();
+ },
+
+ // private
+ beforeCollapse : function(p, animate){
+ this.lastAnim = animate;
+ if(this.splitEl){
+ this.splitEl.hide();
+ }
+ this.getCollapsedEl().show();
+ var el = this.panel.getEl();
+ this.originalZIndex = el.getStyle('z-index');
+ el.setStyle('z-index', 100);
+ this.isCollapsed = true;
+ this.layout.layout();
+ },
+
+ // private
+ onCollapse : function(animate){
+ this.panel.el.setStyle('z-index', 1);
+ if(this.lastAnim === false || this.panel.animCollapse === false){
+ this.getCollapsedEl().dom.style.visibility = 'visible';
+ }else{
+ this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
+ }
+ this.state.collapsed = true;
+ this.panel.saveState();
+ },
+
+ // private
+ beforeExpand : function(animate){
+ if(this.isSlid){
+ this.afterSlideIn();
+ }
+ var c = this.getCollapsedEl();
+ this.el.show();
+ if(this.position == 'east' || this.position == 'west'){
+ this.panel.setSize(undefined, c.getHeight());
+ }else{
+ this.panel.setSize(c.getWidth(), undefined);
+ }
+ c.hide();
+ c.dom.style.visibility = 'hidden';
+ this.panel.el.setStyle('z-index', this.floatingZIndex);
+ },
+
+ // private
+ onExpand : function(){
+ this.isCollapsed = false;
+ if(this.splitEl){
+ this.splitEl.show();
+ }
+ this.layout.layout();
+ this.panel.el.setStyle('z-index', this.originalZIndex);
+ this.state.collapsed = false;
+ this.panel.saveState();
+ },
+
+ // private
+ collapseClick : function(e){
+ if(this.isSlid){
+ e.stopPropagation();
+ this.slideIn();
+ }else{
+ e.stopPropagation();
+ this.slideOut();
+ }
+ },
+
+ // private
+ onHide : function(){
+ if(this.isCollapsed){
+ this.getCollapsedEl().hide();
+ }else if(this.splitEl){
+ this.splitEl.hide();
+ }
+ },
+
+ // private
+ onShow : function(){
+ if(this.isCollapsed){
+ this.getCollapsedEl().show();
+ }else if(this.splitEl){
+ this.splitEl.show();
+ }
+ },
+
+ /**
+ * True if this region is currently visible, else false.
+ * @return {Boolean}
+ */
+ isVisible : function(){
+ return !this.panel.hidden;
+ },
+
+ /**
+ * Returns the current margins for this region. If the region is collapsed, the
+ * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
+ * {@link #margins} value will be returned.
+ * @return {Object} An object containing the element's margins: <tt>{left: (left
+ * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
+ */
+ getMargins : function(){
+ return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
+ },
+
+ /**
+ * Returns the current size of this region. If the region is collapsed, the size of the
+ * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
+ * @return {Object} An object containing the element's size: <tt>{width: (element width),
+ * height: (element height)}</tt>
+ */
+ getSize : function(){
+ return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
+ },
+
+ /**
+ * Sets the specified panel as the container element for this region.
+ * @param {Ext.Panel} panel The new panel
+ */
+ setPanel : function(panel){
+ this.panel = panel;
+ },
+
+ /**
+ * Returns the minimum allowable width for this region.
+ * @return {Number} The minimum width
+ */
+ getMinWidth: function(){
+ return this.minWidth;
+ },
+
+ /**
+ * Returns the minimum allowable height for this region.
+ * @return {Number} The minimum height
+ */
+ getMinHeight: function(){
+ return this.minHeight;
+ },
+
+ // private
+ applyLayoutCollapsed : function(box){
+ var ce = this.getCollapsedEl();
+ ce.setLeftTop(box.x, box.y);
+ ce.setSize(box.width, box.height);
+ },
+
+ // private
+ applyLayout : function(box){
+ if(this.isCollapsed){
+ this.applyLayoutCollapsed(box);
+ }else{
+ this.panel.setPosition(box.x, box.y);
+ this.panel.setSize(box.width, box.height);
+ }
+ },
+
+ // private
+ beforeSlide: function(){
+ this.panel.beforeEffect();
+ },
+
+ // private
+ afterSlide : function(){
+ this.panel.afterEffect();
+ },
+
+ // private
+ initAutoHide : function(){
+ if(this.autoHide !== false){
+ if(!this.autoHideHd){
+ this.autoHideSlideTask = new Ext.util.DelayedTask(this.slideIn, this);
+ this.autoHideHd = {
+ "mouseout": function(e){
+ if(!e.within(this.el, true)){
+ this.autoHideSlideTask.delay(500);
+ }
+ },
+ "mouseover" : function(e){
+ this.autoHideSlideTask.cancel();
+ },
+ scope : this
+ };
+ }
+ this.el.on(this.autoHideHd);
+ this.collapsedEl.on(this.autoHideHd);
+ }
+ },
+
+ // private
+ clearAutoHide : function(){
+ 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);
+ }
+ },
+
+ // private
+ clearMonitor : function(){
+ Ext.getDoc().un("click", this.slideInIf, this);
+ },
+
+ /**
+ * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
+ * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
+ * are clicked, or the mouse exits the Region.
+ */
+ slideOut : function(){
+ if(this.isSlid || this.el.hasActiveFx()){
+ return;
+ }
+ this.isSlid = true;
+ var ts = this.panel.tools, dh, pc;
+ if(ts && ts.toggle){
+ ts.toggle.hide();
+ }
+ this.el.show();
+
+ // Temporarily clear the collapsed flag so we can onResize the panel on the slide
+ pc = this.panel.collapsed;
+ this.panel.collapsed = false;
+
+ if(this.position == 'east' || this.position == 'west'){
+ // Temporarily clear the deferHeight flag so we can size the height on the slide
+ dh = this.panel.deferHeight;
+ this.panel.deferHeight = false;
+
+ this.panel.setSize(undefined, this.collapsedEl.getHeight());
+
+ // Put the deferHeight flag back after setSize
+ this.panel.deferHeight = dh;
+ }else{
+ this.panel.setSize(this.collapsedEl.getWidth(), undefined);
+ }
+
+ // Put the collapsed flag back after onResize
+ this.panel.collapsed = pc;
+
+ this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
+ this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
+ this.el.setStyle("z-index", this.floatingZIndex+2);
+ this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
+ if(this.animFloat !== false){
+ this.beforeSlide();
+ this.el.slideIn(this.getSlideAnchor(), {
+ callback: function(){
+ this.afterSlide();
+ this.initAutoHide();
+ Ext.getDoc().on("click", this.slideInIf, this);
+ },
+ scope: this,
+ block: true
+ });
+ }else{
+ this.initAutoHide();
+ Ext.getDoc().on("click", this.slideInIf, this);
+ }
+ },
+
+ // private
+ afterSlideIn : function(){
+ this.clearAutoHide();
+ this.isSlid = false;
+ this.clearMonitor();
+ this.el.setStyle("z-index", "");
+ this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
+ this.el.dom.style.left = this.restoreLT[0];
+ this.el.dom.style.top = this.restoreLT[1];
+
+ var ts = this.panel.tools;
+ if(ts && ts.toggle){
+ ts.toggle.show();
+ }
+ },
+
+ /**
+ * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
+ * this region back into its collapsed state.
+ */
+ slideIn : function(cb){
+ if(!this.isSlid || this.el.hasActiveFx()){
+ Ext.callback(cb);
+ return;
+ }
+ this.isSlid = false;
+ if(this.animFloat !== false){
+ this.beforeSlide();
+ this.el.slideOut(this.getSlideAnchor(), {
+ callback: function(){
+ this.el.hide();
+ this.afterSlide();
+ this.afterSlideIn();
+ Ext.callback(cb);
+ },
+ scope: this,
+ block: true
+ });
+ }else{
+ this.el.hide();
+ this.afterSlideIn();
+ }
+ },
+
+ // private
+ slideInIf : function(e){
+ if(!e.within(this.el)){
+ this.slideIn();
+ }
+ },
+
+ // private
+ anchors : {
+ "west" : "left",
+ "east" : "right",
+ "north" : "top",
+ "south" : "bottom"
+ },
+
+ // private
+ sanchors : {
+ "west" : "l",
+ "east" : "r",
+ "north" : "t",
+ "south" : "b"
+ },
+
+ // private
+ canchors : {
+ "west" : "tl-tr",
+ "east" : "tr-tl",
+ "north" : "tl-bl",
+ "south" : "bl-tl"
+ },
+
+ // private
+ getAnchor : function(){
+ return this.anchors[this.position];
+ },
+
+ // private
+ getCollapseAnchor : function(){
+ return this.canchors[this.position];
+ },
+
+ // private
+ getSlideAnchor : function(){
+ return this.sanchors[this.position];
+ },
+
+ // private
+ getAlignAdj : function(){
+ var cm = this.cmargins;
+ switch(this.position){
+ case "west":
+ return [0, 0];
+ break;
+ case "east":
+ return [0, 0];
+ break;
+ case "north":
+ return [0, 0];
+ break;
+ case "south":
+ return [0, 0];
+ break;
+ }
+ },
+
+ // private
+ getExpandAdj : function(){
+ var c = this.collapsedEl, cm = this.cmargins;
+ switch(this.position){
+ case "west":
+ return [-(cm.right+c.getWidth()+cm.left), 0];
+ break;
+ case "east":
+ return [cm.right+c.getWidth()+cm.left, 0];
+ break;
+ case "north":
+ return [0, -(cm.top+cm.bottom+c.getHeight())];
+ break;
+ case "south":
+ return [0, cm.top+cm.bottom+c.getHeight()];
+ break;
+ }
+ },
+
+ destroy : function(){
+ if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
+ this.autoHideSlideTask.cancel();
+ }
+ Ext.destroyMembers(this, 'miniCollapsedEl', 'collapsedEl', 'expandToolEl');
+ }
+};
+
+/**
+ * @class Ext.layout.BorderLayout.SplitRegion
+ * @extends Ext.layout.BorderLayout.Region
+ * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
+ * has a built-in {@link Ext.SplitBar} for user resizing of regions. The movement of the split bar
+ * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
+ * @constructor
+ * Create a new SplitRegion.
+ * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
+ * @param {Object} config The configuration options
+ * @param {String} position The region position. Valid values are: north, south, east, west and center. Every
+ * BorderLayout must have a center region for the primary content -- all other regions are optional.
+ */
+Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
+ Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
+ // prevent switch
+ this.applyLayout = this.applyFns[pos];
+};
+
+Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
+ /**
+ * @cfg {Number} tickSize
+ * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
+ * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
+ */
+ /**
+ * @cfg {String} splitTip
+ * The tooltip to display when the user hovers over a
+ * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
+ * (defaults to <tt>"Drag to resize."</tt>). Only applies if
+ * <tt>{@link #useSplitTips} = true</tt>.
+ */
+ splitTip : "Drag to resize.",
+ /**
+ * @cfg {String} collapsibleSplitTip
+ * The tooltip to display when the user hovers over a
+ * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
+ * (defaults to "Drag to resize. Double click to hide."). Only applies if
+ * <tt>{@link #useSplitTips} = true</tt>.
+ */
+ collapsibleSplitTip : "Drag to resize. Double click to hide.",
+ /**
+ * @cfg {Boolean} useSplitTips
+ * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
+ * (defaults to <tt>false</tt>). The tooltip text will be the value of either
+ * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
+ */
+ useSplitTips : false,
+
+ // private
+ splitSettings : {
+ north : {
+ orientation: Ext.SplitBar.VERTICAL,
+ placement: Ext.SplitBar.TOP,
+ maxFn : 'getVMaxSize',
+ minProp: 'minHeight',
+ maxProp: 'maxHeight'
+ },
+ south : {
+ orientation: Ext.SplitBar.VERTICAL,
+ placement: Ext.SplitBar.BOTTOM,
+ maxFn : 'getVMaxSize',
+ minProp: 'minHeight',
+ maxProp: 'maxHeight'
+ },
+ east : {
+ orientation: Ext.SplitBar.HORIZONTAL,
+ placement: Ext.SplitBar.RIGHT,
+ maxFn : 'getHMaxSize',
+ minProp: 'minWidth',
+ maxProp: 'maxWidth'
+ },
+ west : {
+ orientation: Ext.SplitBar.HORIZONTAL,
+ placement: Ext.SplitBar.LEFT,
+ maxFn : 'getHMaxSize',
+ minProp: 'minWidth',
+ maxProp: 'maxWidth'
+ }
+ },
+
+ // private
+ applyFns : {
+ west : function(box){
+ if(this.isCollapsed){
+ return this.applyLayoutCollapsed(box);
+ }
+ var sd = this.splitEl.dom, s = sd.style;
+ this.panel.setPosition(box.x, box.y);
+ var sw = sd.offsetWidth;
+ s.left = (box.x+box.width-sw)+'px';
+ s.top = (box.y)+'px';
+ s.height = Math.max(0, box.height)+'px';
+ this.panel.setSize(box.width-sw, box.height);
+ },
+ east : function(box){
+ if(this.isCollapsed){
+ return this.applyLayoutCollapsed(box);
+ }
+ var sd = this.splitEl.dom, s = sd.style;
+ var sw = sd.offsetWidth;
+ this.panel.setPosition(box.x+sw, box.y);
+ s.left = (box.x)+'px';
+ s.top = (box.y)+'px';
+ s.height = Math.max(0, box.height)+'px';
+ this.panel.setSize(box.width-sw, box.height);
+ },
+ north : function(box){
+ if(this.isCollapsed){
+ return this.applyLayoutCollapsed(box);
+ }
+ var sd = this.splitEl.dom, s = sd.style;
+ var sh = sd.offsetHeight;
+ this.panel.setPosition(box.x, box.y);
+ s.left = (box.x)+'px';
+ s.top = (box.y+box.height-sh)+'px';
+ s.width = Math.max(0, box.width)+'px';
+ this.panel.setSize(box.width, box.height-sh);
+ },
+ south : function(box){
+ if(this.isCollapsed){
+ return this.applyLayoutCollapsed(box);
+ }
+ var sd = this.splitEl.dom, s = sd.style;
+ var sh = sd.offsetHeight;
+ this.panel.setPosition(box.x, box.y+sh);
+ s.left = (box.x)+'px';
+ s.top = (box.y)+'px';
+ s.width = Math.max(0, box.width)+'px';
+ this.panel.setSize(box.width, box.height-sh);
+ }
+ },
+
+ // private
+ render : function(ct, p){
+ Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
+
+ var ps = this.position;
+
+ this.splitEl = ct.createChild({
+ cls: "x-layout-split x-layout-split-"+ps, html: " ",
+ id: this.panel.id + '-xsplit'
+ });
+
+ if(this.collapseMode == 'mini'){
+ this.miniSplitEl = this.splitEl.createChild({
+ cls: "x-layout-mini x-layout-mini-"+ps, html: " "
+ });
+ this.miniSplitEl.addClassOnOver('x-layout-mini-over');
+ this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
+ }
+
+ var s = this.splitSettings[ps];
+
+ this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
+ this.split.tickSize = this.tickSize;
+ this.split.placement = s.placement;
+ this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
+ this.split.minSize = this.minSize || this[s.minProp];
+ this.split.on("beforeapply", this.onSplitMove, this);
+ this.split.useShim = this.useShim === true;
+ this.maxSize = this.maxSize || this[s.maxProp];
+
+ if(p.hidden){
+ this.splitEl.hide();
+ }
+
+ if(this.useSplitTips){
+ this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
+ }
+ if(this.collapsible){
+ this.splitEl.on("dblclick", this.onCollapseClick, this);
+ }
+ },
+
+ //docs inherit from superclass
+ getSize : function(){
+ if(this.isCollapsed){
+ return this.collapsedEl.getSize();
+ }
+ var s = this.panel.getSize();
+ if(this.position == 'north' || this.position == 'south'){
+ s.height += this.splitEl.dom.offsetHeight;
+ }else{
+ s.width += this.splitEl.dom.offsetWidth;
+ }
+ return s;
+ },
+
+ // private
+ getHMaxSize : function(){
+ var cmax = this.maxSize || 10000;
+ var center = this.layout.center;
+ return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
+ },
+
+ // private
+ getVMaxSize : function(){
+ var cmax = this.maxSize || 10000;
+ var center = this.layout.center;
+ return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
+ },
+
+ // private
+ onSplitMove : function(split, newSize){
+ var s = this.panel.getSize();
+ this.lastSplitSize = newSize;
+ if(this.position == 'north' || this.position == 'south'){
+ this.panel.setSize(s.width, newSize);
+ this.state.height = newSize;
+ }else{
+ this.panel.setSize(newSize, s.height);
+ this.state.width = newSize;
+ }
+ this.layout.layout();
+ this.panel.saveState();
+ return false;
+ },
+
+ /**
+ * Returns a reference to the split bar in use by this region.
+ * @return {Ext.SplitBar} The split bar
+ */
+ getSplitBar : function(){
+ return this.split;
+ },
+
+ // inherit docs
+ destroy : function() {
+ Ext.destroy(this.miniSplitEl, this.split, this.splitEl);
+ Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this);
+ }
+});
+
+Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;
+/**
+ * @class Ext.layout.FormLayout
+ * @extends Ext.layout.AnchorLayout
+ * <p>This layout manager is specifically designed for rendering and managing child Components of
+ * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
+ * {@link Ext.form.Field Field}s.</p>
+ *
+ * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
+ * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
+ * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
+ *
+ * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
+ * (which is configured with FormLayout as its layout class by default) since it also provides built-in
+ * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
+ *
+ * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
+ * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
+ * layout-specific config properties:<div class="mdetail-params"><ul>
+ * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
+ * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
+ * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
+ * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
+ * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
+ * </ul></div></p>
+ *
+ * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
+ * <div class="mdetail-params"><ul>
+ * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
+ * </ul></div></p>
+ *
+ * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
+ * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
+ * in this way may be configured with the following options which affect the way the FormLayout renders them:
+ * <div class="mdetail-params"><ul>
+ * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
+ * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
+ * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
+ * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
+ * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
+ * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
+ * </ul></div></p>
+ *
+ * <p>Example usage:</p>
+ * <pre><code>
+// Required if showing validation messages
+Ext.QuickTips.init();
+
+// While you can create a basic Panel with layout:'form', practically
+// you should usually use a FormPanel to also get its form functionality
+// since it already creates a FormLayout internally.
+var form = new Ext.form.FormPanel({
+ title: 'Form Layout',
+ bodyStyle: 'padding:15px',
+ width: 350,
+ defaultType: 'textfield',
+ defaults: {
+ // applied to each contained item
+ width: 230,
+ msgTarget: 'side'
+ },
+ items: [{
+ fieldLabel: 'First Name',
+ name: 'first',
+ allowBlank: false,
+ {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
+ },{
+ fieldLabel: 'Last Name',
+ name: 'last'
+ },{
+ fieldLabel: 'Email',
+ name: 'email',
+ vtype:'email'
+ }, {
+ xtype: 'textarea',
+ hideLabel: true, // override hideLabels layout config
+ name: 'msg',
+ anchor: '100% -53'
+ }
+ ],
+ buttons: [
+ {text: 'Save'},
+ {text: 'Cancel'}
+ ],
+ layoutConfig: {
+ {@link #labelSeparator}: '~' // superseded by assignment below
+ },
+ // config options applicable to container when layout='form':
+ hideLabels: false,
+ labelAlign: 'left', // or 'right' or 'top'
+ {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
+ labelWidth: 65, // defaults to 100
+ labelPad: 8 // defaults to 5, must specify labelWidth to be honored
+});
+</code></pre>
+ */
+Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
+
+ /**
+ * @cfg {String} labelSeparator
+ * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}. Configuration
+ * of this property at the <b>container</b> level takes precedence.
+ */
+ labelSeparator : ':',
+
+ /**
+ * Read only. The CSS style specification string added to field labels in this layout if not
+ * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
+ * @type String
+ * @property labelStyle
+ */
+
+ /**
+ * @cfg {Boolean} trackLabels
+ * True to show/hide the field label when the field is hidden. Defaults to <tt>true</tt>.
+ */
+ trackLabels: true,
+
+ type: 'form',
+
+ onRemove: function(c){
+ Ext.layout.FormLayout.superclass.onRemove.call(this, c);
+ if(this.trackLabels){
+ c.un('show', this.onFieldShow, this);
+ c.un('hide', this.onFieldHide, this);
+ }
+ // check for itemCt, since we may be removing a fieldset or something similar
+ var el = c.getPositionEl(),
+ ct = c.getItemCt && c.getItemCt();
+ if (c.rendered && ct) {
+ if (el && el.dom) {
+ el.insertAfter(ct);
+ }
+ Ext.destroy(ct);
+ Ext.destroyMembers(c, 'label', 'itemCt');
+ if (c.customItemCt) {
+ Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
+ }
+ }
+ },
+
+ // private
+ setContainer : function(ct){
+ Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
+ if(ct.labelAlign){
+ ct.addClass('x-form-label-'+ct.labelAlign);
+ }
+
+ if(ct.hideLabels){
+ Ext.apply(this, {
+ labelStyle: 'display:none',
+ elementStyle: 'padding-left:0;',
+ labelAdjust: 0
+ });
+ }else{
+ this.labelSeparator = Ext.isDefined(ct.labelSeparator) ? ct.labelSeparator : this.labelSeparator;
+ ct.labelWidth = ct.labelWidth || 100;
+ if(Ext.isNumber(ct.labelWidth)){
+ var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
+ Ext.apply(this, {
+ labelAdjust: ct.labelWidth + pad,
+ labelStyle: 'width:' + ct.labelWidth + 'px;',
+ elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px'
+ });
+ }
+ if(ct.labelAlign == 'top'){
+ Ext.apply(this, {
+ labelStyle: 'width:auto;',
+ labelAdjust: 0,
+ elementStyle: 'padding-left:0;'
+ });
+ }
+ }
+ },
+
+ // private
+ isHide: function(c){
+ return c.hideLabel || this.container.hideLabels;
+ },
+
+ onFieldShow: function(c){
+ c.getItemCt().removeClass('x-hide-' + c.hideMode);
+
+ // Composite fields will need to layout after the container is made visible
+ if (c.isComposite) {
+ c.doLayout();
+ }
+ },
+
+ onFieldHide: function(c){
+ c.getItemCt().addClass('x-hide-' + c.hideMode);
+ },
+
+ //private
+ getLabelStyle: function(s){
+ var ls = '', items = [this.labelStyle, s];
+ for (var i = 0, len = items.length; i < len; ++i){
+ if (items[i]){
+ ls += items[i];
+ if (ls.substr(-1, 1) != ';'){
+ ls += ';';
+ }
+ }
+ }
+ return ls;
+ },
+
+ /**
+ * @cfg {Ext.Template} fieldTpl
+ * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
+ * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
+new Ext.Template(
+ '<div class="x-form-item {itemCls}" tabIndex="-1">',
+ '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
+ '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
+ '</div><div class="{clearCls}"></div>',
+ '</div>'
+);
+</code></pre>
+ * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
+ * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
+ * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
+ * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
+ * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
+ * supplied at the container level.</div></li>
+ * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
+ * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
+ * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
+ * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
+ * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
+ * field (defaults to <tt>''</tt>)</div></li>
+ * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
+ * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
+ * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
+ * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
+ * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
+ * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
+ * </ul></div>
+ * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
+ */
+
+ /**
+ * @private
+ *
+ */
+ renderItem : function(c, position, target){
+ if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
+ var args = this.getTemplateArgs(c);
+ if(Ext.isNumber(position)){
+ position = target.dom.childNodes[position] || null;
+ }
+ if(position){
+ c.itemCt = this.fieldTpl.insertBefore(position, args, true);
+ }else{
+ c.itemCt = this.fieldTpl.append(target, args, true);
+ }
+ if(!c.getItemCt){
+ // Non form fields don't have getItemCt, apply it here
+ // This will get cleaned up in onRemove
+ Ext.apply(c, {
+ getItemCt: function(){
+ return c.itemCt;
+ },
+ customItemCt: true
+ });
+ }
+ c.label = c.getItemCt().child('label.x-form-item-label');
+ if(!c.rendered){
+ c.render('x-form-el-' + c.id);
+ }else if(!this.isValidParent(c, target)){
+ Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl());
+ }
+ if(this.trackLabels){
+ if(c.hidden){
+ this.onFieldHide(c);
+ }
+ c.on({
+ scope: this,
+ show: this.onFieldShow,
+ hide: this.onFieldHide
+ });
+ }
+ this.configureItem(c);
+ }else {
+ Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
+ }
+ },
+
+ /**
+ * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
+ * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
+ * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
+ * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
+ * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
+ * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
+ * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
+ * supplied at the container level.</div></li>
+ * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
+ * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
+ * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
+ * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
+ * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
+ * field (defaults to the field's configured fieldLabel property)</div></li>
+ * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
+ * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
+ * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
+ * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
+ * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
+ * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
+ * </ul></div>
+ * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered.
+ * @return {Object} An object hash containing the properties required to render the Field.
+ */
+ getTemplateArgs: function(field) {
+ var noLabelSep = !field.fieldLabel || field.hideLabel;
+
+ return {
+ id : field.id,
+ label : field.fieldLabel,
+ itemCls : (field.itemCls || this.container.itemCls || '') + (field.hideLabel ? ' x-hide-label' : ''),
+ clearCls : field.clearCls || 'x-form-clear-left',
+ labelStyle : this.getLabelStyle(field.labelStyle),
+ elementStyle : this.elementStyle || '',
+ labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator)
+ };
+ },
+
+ // private
+ adjustWidthAnchor: function(value, c){
+ if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){
+ var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict);
+ return value - this.labelAdjust + (adjust ? -3 : 0);
+ }
+ return value;
+ },
+
+ adjustHeightAnchor : function(value, c){
+ if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){
+ return value - c.label.getHeight();
+ }
+ return value;
+ },
+
+ // private
+ isValidParent : function(c, target){
+ return target && this.container.getEl().contains(c.getPositionEl());
+ }
+
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+
+Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;
+/**
+ * @class Ext.layout.AccordionLayout
+ * @extends Ext.layout.FitLayout
+ * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only
+ * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>
+ * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>
+ * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
+ * configuration property. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
+ * <p>Example usage:</p>
+ * <pre><code>
+var accordion = new Ext.Panel({
+ title: 'Accordion Layout',
+ layout:'accordion',
+ defaults: {
+ // applied to each contained panel
+ bodyStyle: 'padding:15px'
+ },
+ layoutConfig: {
+ // layout-specific configs go here
+ titleCollapse: false,
+ animate: true,
+ activeOnTop: true
+ },
+ items: [{
+ title: 'Panel 1',
+ html: '<p>Panel content!</p>'
+ },{
+ title: 'Panel 2',
+ html: '<p>Panel content!</p>'
+ },{
+ title: 'Panel 3',
+ html: '<p>Panel content!</p>'
+ }]
+});
+</code></pre>
+ */
+Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {
+ /**
+ * @cfg {Boolean} fill
+ * True to adjust the active item's height to fill the available space in the container, false to use the
+ * item's current height, or auto height if not explicitly set (defaults to true).
+ */
+ fill : true,
+ /**
+ * @cfg {Boolean} autoWidth
+ * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).
+ * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within
+ * layouts if they have auto width, so in such cases this config should be set to false.
+ */
+ autoWidth : true,
+ /**
+ * @cfg {Boolean} titleCollapse
+ * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
+ * expand/collapse only when the toggle tool button is clicked (defaults to true). When set to false,
+ * {@link #hideCollapseTool} should be false also.
+ */
+ titleCollapse : true,
+ /**
+ * @cfg {Boolean} hideCollapseTool
+ * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).
+ * When set to true, {@link #titleCollapse} should be true also.
+ */
+ hideCollapseTool : false,
+ /**
+ * @cfg {Boolean} collapseFirst
+ * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
+ * in the contained panels' title bars, false to render it last (defaults to false).
+ */
+ collapseFirst : false,
+ /**
+ * @cfg {Boolean} animate
+ * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
+ * close directly with no animation (defaults to false). Note: to defer to the specific config setting of each
+ * contained panel for this property, set this to undefined at the layout level.
+ */
+ animate : false,
+ /**
+ * @cfg {Boolean} sequence
+ * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.
+ */
+ sequence : false,
+ /**
+ * @cfg {Boolean} activeOnTop
+ * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
+ * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).
+ */
+ activeOnTop : false,
+
+ type: 'accordion',
+
+ renderItem : function(c){
+ if(this.animate === false){
+ c.animCollapse = false;
+ }
+ c.collapsible = true;
+ if(this.autoWidth){
+ c.autoWidth = true;
+ }
+ if(this.titleCollapse){
+ c.titleCollapse = true;
+ }
+ if(this.hideCollapseTool){
+ c.hideCollapseTool = true;
+ }
+ if(this.collapseFirst !== undefined){
+ c.collapseFirst = this.collapseFirst;
+ }
+ if(!this.activeItem && !c.collapsed){
+ this.setActiveItem(c, true);
+ }else if(this.activeItem && this.activeItem != c){
+ c.collapsed = true;
+ }
+ Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);
+ c.header.addClass('x-accordion-hd');
+ c.on('beforeexpand', this.beforeExpand, this);
+ },
+
+ onRemove: function(c){
+ Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);
+ if(c.rendered){
+ c.header.removeClass('x-accordion-hd');
+ }
+ c.un('beforeexpand', this.beforeExpand, this);
+ },
+
+ // private
+ beforeExpand : function(p, anim){
+ var ai = this.activeItem;
+ if(ai){
+ if(this.sequence){
+ delete this.activeItem;
+ if (!ai.collapsed){
+ ai.collapse({callback:function(){
+ p.expand(anim || true);
+ }, scope: this});
+ return false;
+ }
+ }else{
+ ai.collapse(this.animate);
+ }
+ }
+ this.setActive(p);
+ if(this.activeOnTop){
+ p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);
+ }
+ // Items have been hidden an possibly rearranged, we need to get the container size again.
+ this.layout();
+ },
+
+ // private
+ setItemSize : function(item, size){
+ if(this.fill && item){
+ var hh = 0, i, ct = this.getRenderedItems(this.container), len = ct.length, p;
+ // Add up all the header heights
+ for (i = 0; i < len; i++) {
+ if((p = ct[i]) != item && !p.hidden){
+ hh += p.header.getHeight();
+ }
+ };
+ // Subtract the header heights from the container size
+ size.height -= hh;
+ // Call setSize on the container to set the correct height. For Panels, deferedHeight
+ // will simply store this size for when the expansion is done.
+ item.setSize(size);
+ }
+ },
+
+ /**
+ * Sets the active (expanded) item in the layout.
+ * @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(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;
+
+//backwards compat
+Ext.layout.Accordion = Ext.layout.AccordionLayout;/**
+ * @class Ext.layout.TableLayout
+ * @extends Ext.layout.ContainerLayout
+ * <p>This layout allows you to easily render content into an HTML table. The total number of columns can be
+ * specified, and rowspan and colspan can be used to create complex layouts within the table.
+ * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,
+ * and should generally not need to be created directly via the new keyword.</p>
+ * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via
+ * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout. In the
+ * case of TableLayout, the only valid layout config property is {@link #columns}. However, the items added to a
+ * TableLayout can supply the following table-specific config properties:</p>
+ * <ul>
+ * <li><b>rowspan</b> Applied to the table cell containing the item.</li>
+ * <li><b>colspan</b> Applied to the table cell containing the item.</li>
+ * <li><b>cellId</b> An id applied to the table cell containing the item.</li>
+ * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>
+ * </ul>
+ * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard
+ * HTML table. You simply add each panel (or "cell") that you want to include along with any span attributes
+ * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
+ * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
+ * total column count in the layoutConfig and start adding panels in their natural order from left to right,
+ * top to bottom. The layout will automatically figure out, based on the column count, rowspans and colspans,
+ * how to position each panel within the table. Just like with HTML tables, your rowspans and colspans must add
+ * up correctly in your overall layout or you'll end up with missing and/or extra cells! Example usage:</p>
+ * <pre><code>
+// This code will generate a layout table that is 3 columns by 2 rows
+// with some spanning included. The basic layout will be:
+// +--------+-----------------+
+// | A | B |
+// | |--------+--------|
+// | | C | D |
+// +--------+--------+--------+
+var table = new Ext.Panel({
+ title: 'Table Layout',
+ layout:'table',
+ defaults: {
+ // applied to each contained panel
+ bodyStyle:'padding:20px'
+ },
+ layoutConfig: {
+ // The total column count must be specified here
+ columns: 3
+ },
+ items: [{
+ html: '<p>Cell A content</p>',
+ rowspan: 2
+ },{
+ html: '<p>Cell B content</p>',
+ colspan: 2
+ },{
+ html: '<p>Cell C content</p>',
+ cellCls: 'highlight'
+ },{
+ html: '<p>Cell D content</p>'
+ }]
+});
+</code></pre>
+ */
+Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ /**
+ * @cfg {Number} columns
+ * The total number of columns to create in the table for this layout. If not specified, all Components added to
+ * this layout will be rendered into a single row using one column per Component.
+ */
+
+ // private
+ monitorResize:false,
+
+ type: 'table',
+
+ targetCls: 'x-table-layout-ct',
+
+ /**
+ * @cfg {Object} tableAttrs
+ * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification
+ * used to create the layout's <tt><table></tt> element. Example:</p><pre><code>
+{
+ xtype: 'panel',
+ layout: 'table',
+ layoutConfig: {
+ tableAttrs: {
+ style: {
+ width: '100%'
+ }
+ },
+ columns: 3
+ }
+}</code></pre>
+ */
+ tableAttrs:null,
+
+ // private
+ setContainer : function(ct){
+ Ext.layout.TableLayout.superclass.setContainer.call(this, ct);
+
+ this.currentRow = 0;
+ this.currentColumn = 0;
+ this.cells = [];
+ },
+
+ // private
+ onLayout : function(ct, target){
+ 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);
+ }
+ this.renderAll(ct, target);
+ },
+
+ // private
+ getRow : function(index){
+ var row = this.table.tBodies[0].childNodes[index];
+ if(!row){
+ row = document.createElement('tr');
+ this.table.tBodies[0].appendChild(row);
+ }
+ return row;
+ },
+
+ // private
+ getNextCell : function(c){
+ var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);
+ var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];
+ for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){
+ if(!this.cells[rowIndex]){
+ this.cells[rowIndex] = [];
+ }
+ for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){
+ this.cells[rowIndex][colIndex] = true;
+ }
+ }
+ var td = document.createElement('td');
+ if(c.cellId){
+ td.id = c.cellId;
+ }
+ var cls = 'x-table-layout-cell';
+ if(c.cellCls){
+ cls += ' ' + c.cellCls;
+ }
+ td.className = cls;
+ if(c.colspan){
+ td.colSpan = c.colspan;
+ }
+ if(c.rowspan){
+ td.rowSpan = c.rowspan;
+ }
+ this.getRow(curRow).appendChild(td);
+ return td;
+ },
+
+ // private
+ getNextNonSpan: function(colIndex, rowIndex){
+ var cols = this.columns;
+ while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {
+ if(cols && colIndex >= cols){
+ rowIndex++;
+ colIndex = 0;
+ }else{
+ colIndex++;
+ }
+ }
+ return [colIndex, rowIndex];
+ },
+
+ // private
+ renderItem : function(c, position, target){
+ // Ensure we have our inner table to get cells to render into.
+ if(!this.table){
+ this.table = target.createChild(
+ Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
+ }
+ if(c && !c.rendered){
+ c.render(this.getNextCell(c));
+ this.configureItem(c);
+ }else if(c && !this.isValidParent(c, target)){
+ var container = this.getNextCell(c);
+ container.insertBefore(c.getPositionEl().dom, null);
+ c.container = Ext.get(container);
+ this.configureItem(c);
+ }
+ },
+
+ // private
+ isValidParent : function(c, target){
+ return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);
+ },
+
+ destroy: function(){
+ delete this.table;
+ Ext.layout.TableLayout.superclass.destroy.call(this);
+ }
+
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+
+Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**
+ * @class Ext.layout.AbsoluteLayout
+ * @extends Ext.layout.AnchorLayout
+ * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the
+ * ability for x/y positioning using the standard x and y component config options.</p>
+ * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
+ * configuration property. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
+ * <p>Example usage:</p>
+ * <pre><code>
+var form = new Ext.form.FormPanel({
+ title: 'Absolute Layout',
+ layout:'absolute',
+ layoutConfig: {
+ // layout-specific configs go here
+ extraCls: 'x-abs-layout-item',
+ },
+ baseCls: 'x-plain',
+ url:'save-form.php',
+ defaultType: 'textfield',
+ items: [{
+ x: 0,
+ y: 5,
+ xtype:'label',
+ text: 'Send To:'
+ },{
+ x: 60,
+ y: 0,
+ name: 'to',
+ anchor:'100%' // anchor width by percentage
+ },{
+ x: 0,
+ y: 35,
+ xtype:'label',
+ text: 'Subject:'
+ },{
+ x: 60,
+ y: 30,
+ name: 'subject',
+ anchor: '100%' // anchor width by percentage
+ },{
+ x:0,
+ y: 60,
+ xtype: 'textarea',
+ name: 'msg',
+ anchor: '100% 100%' // anchor width and height
+ }]
+});
+</code></pre>
+ */
+Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
+
+ extraCls: 'x-abs-layout-item',
+
+ type: 'absolute',
+
+ onLayout : function(ct, target){
+ target.position();
+ this.paddingLeft = target.getPadding('l');
+ this.paddingTop = target.getPadding('t');
+ Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
+ },
+
+ // private
+ adjustWidthAnchor : function(value, comp){
+ return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
+ },
+
+ // private
+ adjustHeightAnchor : function(value, comp){
+ return value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
+ }
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
+/**
+ * @class Ext.layout.BoxLayout
+ * @extends Ext.layout.ContainerLayout
+ * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
+ */
+Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ /**
+ * @cfg {Object} defaultMargins
+ * <p>If the individual contained items do not have a <tt>margins</tt>
+ * property specified, the default margins from this property will be
+ * applied to each item.</p>
+ * <br><p>This property may be specified as an object containing margins
+ * to apply in the format:</p><pre><code>
+{
+ top: (top margin),
+ right: (right margin),
+ bottom: (bottom margin),
+ left: (left margin)
+}</code></pre>
+ * <p>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:</p>
+ * <div class="mdetail-params"><ul>
+ * <li>If there is only one value, it applies to all sides.</li>
+ * <li>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.</li>
+ * <li>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.</li>
+ * <li>If there are four values, they apply to the top, right, bottom, and
+ * left, respectively.</li>
+ * </ul></div>
+ * <p>Defaults to:</p><pre><code>
+ * {top:0, right:0, bottom:0, left:0}
+ * </code></pre>
+ */
+ defaultMargins : {left:0,top:0,right:0,bottom:0},
+ /**
+ * @cfg {String} padding
+ * <p>Sets the padding to be applied to all child items managed by this layout.</p>
+ * <p>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:</p>
+ * <div class="mdetail-params"><ul>
+ * <li>If there is only one value, it applies to all sides.</li>
+ * <li>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.</li>
+ * <li>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.</li>
+ * <li>If there are four values, they apply to the top, right, bottom, and
+ * left, respectively.</li>
+ * </ul></div>
+ * <p>Defaults to: <code>"0"</code></p>
+ */
+ padding : '0',
+ // documented in subclasses
+ pack : 'start',
+
+ // private
+ monitorResize : true,
+ type: 'box',
+ 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);
+ }
+
+ var handler = this.overflowHandler;
+
+ if (typeof handler == 'string') {
+ handler = {
+ type: handler
+ };
+ }
+
+ var handlerType = 'none';
+ if (handler && handler.type != undefined) {
+ handlerType = handler.type;
+ }
+
+ var constructor = Ext.layout.boxOverflow[handlerType];
+ if (constructor[this.type]) {
+ constructor = constructor[this.type];
+ }
+
+ this.overflowHandler = new constructor(this, handler);
+ },
+
+ /**
+ * @private
+ * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
+ * when laying out
+ */
+ onLayout: function(container, target) {
+ Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target);
+
+ var tSize = this.getLayoutTargetSize(),
+ items = this.getVisibleItems(container),
+ calcs = this.calculateChildBoxes(items, tSize),
+ boxes = calcs.boxes,
+ meta = calcs.meta;
+
+ //invoke the overflow handler, if one is configured
+ if (tSize.width > 0) {
+ var handler = this.overflowHandler,
+ method = meta.tooNarrow ? 'handleOverflow' : 'clearOverflow';
+
+ var results = handler[method](calcs, tSize);
+
+ if (results) {
+ if (results.targetSize) {
+ tSize = results.targetSize;
+ }
+
+ if (results.recalculate) {
+ items = this.getVisibleItems(container);
+ calcs = this.calculateChildBoxes(items, tSize);
+ boxes = calcs.boxes;
+ }
+ }
+ }
+
+ /**
+ * @private
+ * @property layoutTargetLastSize
+ * @type Object
+ * Private cache of the last measured size of the layout target. This should never be used except by
+ * BoxLayout subclasses during their onLayout run.
+ */
+ this.layoutTargetLastSize = tSize;
+
+ /**
+ * @private
+ * @property childBoxCache
+ * @type Array
+ * Array of the last calculated height, width, top and left positions of each visible rendered component
+ * within the Box layout.
+ */
+ this.childBoxCache = calcs;
+
+ this.updateInnerCtSize(tSize, calcs);
+ this.updateChildBoxes(boxes);
+
+ // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
+ this.handleTargetOverflow(tSize, container, target);
+ },
+
+ /**
+ * Resizes and repositions each child component
+ * @param {Array} boxes The box measurements
+ */
+ updateChildBoxes: function(boxes) {
+ for (var i = 0, length = boxes.length; i < length; i++) {
+ var box = boxes[i],
+ comp = box.component;
+
+ if (box.dirtySize) {
+ comp.setSize(box.width, box.height);
+ }
+ // Don't set positions to NaN
+ if (isNaN(box.left) || isNaN(box.top)) {
+ continue;
+ }
+
+ comp.setPosition(box.left, box.top);
+ }
+ },
+
+ /**
+ * @private
+ * Called by onRender just before the child components are sized and positioned. This resizes the innerCt
+ * to make sure all child items fit within it. We call this before sizing the children because if our child
+ * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them
+ * again immediately afterwards, giving a performance hit.
+ * Subclasses should provide an implementation.
+ * @param {Object} currentSize The current height and width of the innerCt
+ * @param {Array} calculations The new box calculations of all items to be laid out
+ */
+ updateInnerCtSize: function(tSize, calcs) {
+ var align = this.align,
+ padding = this.padding,
+ width = tSize.width,
+ height = tSize.height;
+
+ if (this.type == 'hbox') {
+ var innerCtWidth = width,
+ innerCtHeight = calcs.meta.maxHeight + padding.top + padding.bottom;
+
+ if (align == 'stretch') {
+ innerCtHeight = height;
+ } else if (align == 'middle') {
+ innerCtHeight = Math.max(height, innerCtHeight);
+ }
+ } else {
+ var innerCtHeight = height,
+ innerCtWidth = calcs.meta.maxWidth + padding.left + padding.right;
+
+ if (align == 'stretch') {
+ innerCtWidth = width;
+ } else if (align == 'center') {
+ innerCtWidth = Math.max(width, innerCtWidth);
+ }
+ }
+
+ this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
+ },
+
+ /**
+ * @private
+ * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden',
+ * we need to lay out a second time because the scrollbars may have modified the height and width of the layout
+ * target. Having a Box layout inside such a target is therefore not recommended.
+ * @param {Object} previousTargetSize The size and height of the layout target before we just laid out
+ * @param {Ext.Container} container The container
+ * @param {Ext.Element} target The target element
+ */
+ handleTargetOverflow: function(previousTargetSize, container, target) {
+ var overflow = target.getStyle('overflow');
+
+ if (overflow && overflow != 'hidden' &&!this.adjustmentPass) {
+ var newTargetSize = this.getLayoutTargetSize();
+ if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){
+ this.adjustmentPass = true;
+ this.onLayout(container, target);
+ }
+ }
+
+ delete this.adjustmentPass;
+ },
+
+ // private
+ isValidParent : function(c, target) {
+ return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
+ },
+
+ /**
+ * @private
+ * Returns all items that are both rendered and visible
+ * @return {Array} All matching items
+ */
+ getVisibleItems: function(ct) {
+ var ct = ct || this.container,
+ t = ct.getLayoutTarget(),
+ cti = ct.items.items,
+ len = cti.length,
+
+ i, c, items = [];
+
+ for (i = 0; i < len; i++) {
+ if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true && c.collapsed !== true && c.shouldLayout !== false){
+ items.push(c);
+ }
+ }
+
+ return items;
+ },
+
+ // private
+ renderAll : function(ct, target) {
+ 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);
+ }
+ Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
+ },
+
+ getLayoutTargetSize : function() {
+ var target = this.container.getLayoutTarget(), ret;
+
+ if (target) {
+ ret = target.getViewSize();
+
+ // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
+ // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
+ // with getViewSize
+ if (Ext.isIE && Ext.isStrict && ret.width == 0){
+ ret = target.getStyleSize();
+ }
+
+ ret.width -= target.getPadding('lr');
+ ret.height -= target.getPadding('tb');
+ }
+
+ return ret;
+ },
+
+ // 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);
+ },
+
+ /**
+ * @private
+ */
+ destroy: function() {
+ Ext.destroy(this.overflowHandler);
+
+ Ext.layout.BoxLayout.superclass.destroy.apply(this, arguments);
+ }
+});
+
+
+
+Ext.ns('Ext.layout.boxOverflow');
+
+/**
+ * @class Ext.layout.boxOverflow.None
+ * @extends Object
+ * Base class for Box Layout overflow handlers. These specialized classes are invoked when a Box Layout
+ * (either an HBox or a VBox) has child items that are either too wide (for HBox) or too tall (for VBox)
+ * for its container.
+ */
+
+Ext.layout.boxOverflow.None = Ext.extend(Object, {
+ constructor: function(layout, config) {
+ this.layout = layout;
+
+ Ext.apply(this, config || {});
+ },
+
+ handleOverflow: Ext.emptyFn,
+
+ clearOverflow: Ext.emptyFn
+});
+
+
+Ext.layout.boxOverflow.none = Ext.layout.boxOverflow.None;
+/**
+ * @class Ext.layout.boxOverflow.Menu
+ * @extends Ext.layout.boxOverflow.None
+ * Description
+ */
+Ext.layout.boxOverflow.Menu = Ext.extend(Ext.layout.boxOverflow.None, {
+ /**
+ * @cfg afterCls
+ * @type String
+ * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
+ * which must always be present at the rightmost edge of the Container
+ */
+ afterCls: 'x-strip-right',
+
+ /**
+ * @property noItemsMenuText
+ * @type String
+ * HTML fragment to render into the toolbar overflow menu if there are no items to display
+ */
+ noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
+
+ constructor: function(layout) {
+ Ext.layout.boxOverflow.Menu.superclass.constructor.apply(this, arguments);
+
+ /**
+ * @property menuItems
+ * @type Array
+ * Array of all items that are currently hidden and should go into the dropdown menu
+ */
+ this.menuItems = [];
+ },
+
+ /**
+ * @private
+ * Creates the beforeCt, innerCt and afterCt elements if they have not already been created
+ * @param {Ext.Container} container The Container attached to this Layout instance
+ * @param {Ext.Element} target The target Element
+ */
+ createInnerElements: function() {
+ if (!this.afterCt) {
+ this.afterCt = this.layout.innerCt.insertSibling({cls: this.afterCls}, 'before');
+ }
+ },
+
+ /**
+ * @private
+ */
+ clearOverflow: function(calculations, targetSize) {
+ var newWidth = targetSize.width + (this.afterCt ? this.afterCt.getWidth() : 0),
+ items = this.menuItems;
+
+ this.hideTrigger();
+
+ for (var index = 0, length = items.length; index < length; index++) {
+ items.pop().component.show();
+ }
+
+ return {
+ targetSize: {
+ height: targetSize.height,
+ width : newWidth
+ }
+ };
+ },
+
+ /**
+ * @private
+ */
+ showTrigger: function() {
+ this.createMenu();
+ this.menuTrigger.show();
+ },
+
+ /**
+ * @private
+ */
+ hideTrigger: function() {
+ if (this.menuTrigger != undefined) {
+ this.menuTrigger.hide();
+ }
+ },
+
+ /**
+ * @private
+ * Called before the overflow menu is shown. This constructs the menu's items, caching them for as long as it can.
+ */
+ beforeMenuShow: function(menu) {
+ var items = this.menuItems,
+ len = items.length,
+ item,
+ prev;
+
+ var needsSep = function(group, item){
+ return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
+ };
+
+ this.clearMenu();
+ menu.removeAll();
+
+ for (var i = 0; i < len; i++) {
+ item = items[i].component;
+
+ if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
+ menu.add('-');
+ }
+
+ this.addComponentToMenu(menu, item);
+ prev = item;
+ }
+
+ // put something so the menu isn't empty if no compatible items found
+ if (menu.items.length < 1) {
+ menu.add(this.noItemsMenuText);
+ }
+ },
+
+ /**
+ * @private
+ * Returns a menu config for a given component. This config is used to create a menu item
+ * to be added to the expander menu
+ * @param {Ext.Component} component The component to create the config for
+ * @param {Boolean} hideOnClick Passed through to the menu item
+ */
+ createMenuConfig : function(component, hideOnClick){
+ var config = Ext.apply({}, component.initialConfig),
+ group = component.toggleGroup;
+
+ Ext.copyTo(config, component, [
+ 'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
+ ]);
+
+ Ext.apply(config, {
+ text : component.overflowText || component.text,
+ hideOnClick: hideOnClick
+ });
+
+ if (group || component.enableToggle) {
+ Ext.apply(config, {
+ group : group,
+ checked: component.pressed,
+ listeners: {
+ checkchange: function(item, checked){
+ component.toggle(checked);
+ }
+ }
+ });
+ }
+
+ delete config.ownerCt;
+ delete config.xtype;
+ delete config.id;
+
+ return config;
+ },
+
+ /**
+ * @private
+ * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
+ * @param {Ext.menu.Menu} menu The menu to add to
+ * @param {Ext.Component} component The component to add
+ */
+ addComponentToMenu : function(menu, component) {
+ if (component instanceof Ext.Toolbar.Separator) {
+ menu.add('-');
+
+ } else if (Ext.isFunction(component.isXType)) {
+ if (component.isXType('splitbutton')) {
+ menu.add(this.createMenuConfig(component, true));
+
+ } else if (component.isXType('button')) {
+ menu.add(this.createMenuConfig(component, !component.menu));
+
+ } else if (component.isXType('buttongroup')) {
+ component.items.each(function(item){
+ this.addComponentToMenu(menu, item);
+ }, this);
+ }
+ }
+ },
+
+ /**
+ * @private
+ * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
+ * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
+ */
+ clearMenu : function(){
+ var menu = this.moreMenu;
+ if (menu && menu.items) {
+ menu.items.each(function(item){
+ delete item.menu;
+ });
+ }
+ },
+
+ /**
+ * @private
+ * Creates the overflow trigger and menu used when enableOverflow is set to true and the items
+ * in the layout are too wide to fit in the space available
+ */
+ createMenu: function() {
+ if (!this.menuTrigger) {
+ this.createInnerElements();
+
+ /**
+ * @private
+ * @property menu
+ * @type Ext.menu.Menu
+ * The expand menu - holds items for every item that cannot be shown
+ * because the container is currently not large enough.
+ */
+ this.menu = new Ext.menu.Menu({
+ ownerCt : this.layout.container,
+ listeners: {
+ scope: this,
+ beforeshow: this.beforeMenuShow
+ }
+ });
+
+ /**
+ * @private
+ * @property menuTrigger
+ * @type Ext.Button
+ * The expand button which triggers the overflow menu to be shown
+ */
+ this.menuTrigger = new Ext.Button({
+ iconCls : 'x-toolbar-more-icon',
+ cls : 'x-toolbar-more',
+ menu : this.menu,
+ renderTo: this.afterCt
+ });
+ }
+ },
+
+ /**
+ * @private
+ */
+ destroy: function() {
+ Ext.destroy(this.menu, this.menuTrigger);
+ }
+});
+
+Ext.layout.boxOverflow.menu = Ext.layout.boxOverflow.Menu;
+
+
+/**
+ * @class Ext.layout.boxOverflow.HorizontalMenu
+ * @extends Ext.layout.boxOverflow.Menu
+ * Description
+ */
+Ext.layout.boxOverflow.HorizontalMenu = Ext.extend(Ext.layout.boxOverflow.Menu, {
+
+ constructor: function() {
+ Ext.layout.boxOverflow.HorizontalMenu.superclass.constructor.apply(this, arguments);
+
+ var me = this,
+ layout = me.layout,
+ origFunction = layout.calculateChildBoxes;
+
+ layout.calculateChildBoxes = function(visibleItems, targetSize) {
+ var calcs = origFunction.apply(layout, arguments),
+ meta = calcs.meta,
+ items = me.menuItems;
+
+ //calculate the width of the items currently hidden solely because there is not enough space
+ //to display them
+ var hiddenWidth = 0;
+ for (var index = 0, length = items.length; index < length; index++) {
+ hiddenWidth += items[index].width;
+ }
+
+ meta.minimumWidth += hiddenWidth;
+ meta.tooNarrow = meta.minimumWidth > targetSize.width;
+
+ return calcs;
+ };
+ },
+
+ handleOverflow: function(calculations, targetSize) {
+ this.showTrigger();
+
+ var newWidth = targetSize.width - this.afterCt.getWidth(),
+ boxes = calculations.boxes,
+ usedWidth = 0,
+ recalculate = false;
+
+ //calculate the width of all visible items and any spare width
+ for (var index = 0, length = boxes.length; index < length; index++) {
+ usedWidth += boxes[index].width;
+ }
+
+ var spareWidth = newWidth - usedWidth,
+ showCount = 0;
+
+ //see if we can re-show any of the hidden components
+ for (var index = 0, length = this.menuItems.length; index < length; index++) {
+ var hidden = this.menuItems[index],
+ comp = hidden.component,
+ width = hidden.width;
+
+ if (width < spareWidth) {
+ comp.show();
+
+ spareWidth -= width;
+ showCount ++;
+ recalculate = true;
+ } else {
+ break;
+ }
+ }
+
+ if (recalculate) {
+ this.menuItems = this.menuItems.slice(showCount);
+ } else {
+ for (var i = boxes.length - 1; i >= 0; i--) {
+ var item = boxes[i].component,
+ right = boxes[i].left + boxes[i].width;
+
+ if (right >= newWidth) {
+ this.menuItems.unshift({
+ component: item,
+ width : boxes[i].width
+ });
+
+ item.hide();
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (this.menuItems.length == 0) {
+ this.hideTrigger();
+ }
+
+ return {
+ targetSize: {
+ height: targetSize.height,
+ width : newWidth
+ },
+ recalculate: recalculate
+ };
+ }
+});
+
+Ext.layout.boxOverflow.menu.hbox = Ext.layout.boxOverflow.HorizontalMenu;/**
+ * @class Ext.layout.boxOverflow.Scroller
+ * @extends Ext.layout.boxOverflow.None
+ * Description
+ */
+Ext.layout.boxOverflow.Scroller = Ext.extend(Ext.layout.boxOverflow.None, {
+ /**
+ * @cfg animateScroll
+ * @type Boolean
+ * True to animate the scrolling of items within the layout (defaults to true, ignored if enableScroll is false)
+ */
+ animateScroll: true,
+
+ /**
+ * @cfg scrollIncrement
+ * @type Number
+ * The number of pixels to scroll by on scroller click (defaults to 100)
+ */
+ scrollIncrement: 100,
+
+ /**
+ * @cfg wheelIncrement
+ * @type Number
+ * The number of pixels to increment on mouse wheel scrolling (defaults to <tt>3</tt>).
+ */
+ wheelIncrement: 3,
+
+ /**
+ * @cfg scrollRepeatInterval
+ * @type Number
+ * Number of milliseconds between each scroll while a scroller button is held down (defaults to 400)
+ */
+ scrollRepeatInterval: 400,
+
+ /**
+ * @cfg scrollDuration
+ * @type Number
+ * Number of seconds that each scroll animation lasts (defaults to 0.4)
+ */
+ scrollDuration: 0.4,
+
+ /**
+ * @cfg beforeCls
+ * @type String
+ * CSS class added to the beforeCt element. This is the element that holds any special items such as scrollers,
+ * which must always be present at the leftmost edge of the Container
+ */
+ beforeCls: 'x-strip-left',
+
+ /**
+ * @cfg afterCls
+ * @type String
+ * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
+ * which must always be present at the rightmost edge of the Container
+ */
+ afterCls: 'x-strip-right',
+
+ /**
+ * @cfg scrollerCls
+ * @type String
+ * CSS class added to both scroller elements if enableScroll is used
+ */
+ scrollerCls: 'x-strip-scroller',
+
+ /**
+ * @cfg beforeScrollerCls
+ * @type String
+ * CSS class added to the left scroller element if enableScroll is used
+ */
+ beforeScrollerCls: 'x-strip-scroller-left',
+
+ /**
+ * @cfg afterScrollerCls
+ * @type String
+ * CSS class added to the right scroller element if enableScroll is used
+ */
+ afterScrollerCls: 'x-strip-scroller-right',
+
+ /**
+ * @private
+ * Sets up an listener to scroll on the layout's innerCt mousewheel event
+ */
+ createWheelListener: function() {
+ this.layout.innerCt.on({
+ scope : this,
+ mousewheel: function(e) {
+ e.stopEvent();
+
+ this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false);
+ }
+ });
+ },
+
+ /**
+ * @private
+ * Most of the heavy lifting is done in the subclasses
+ */
+ handleOverflow: function(calculations, targetSize) {
+ this.createInnerElements();
+ this.showScrollers();
+ },
+
+ /**
+ * @private
+ */
+ clearOverflow: function() {
+ this.hideScrollers();
+ },
+
+ /**
+ * @private
+ * Shows the scroller elements in the beforeCt and afterCt. Creates the scrollers first if they are not already
+ * present.
+ */
+ showScrollers: function() {
+ this.createScrollers();
+
+ this.beforeScroller.show();
+ this.afterScroller.show();
+
+ this.updateScrollButtons();
+ },
+
+ /**
+ * @private
+ * Hides the scroller elements in the beforeCt and afterCt
+ */
+ hideScrollers: function() {
+ if (this.beforeScroller != undefined) {
+ this.beforeScroller.hide();
+ this.afterScroller.hide();
+ }
+ },
+
+ /**
+ * @private
+ * Creates the clickable scroller elements and places them into the beforeCt and afterCt
+ */
+ createScrollers: function() {
+ if (!this.beforeScroller && !this.afterScroller) {
+ var before = this.beforeCt.createChild({
+ cls: String.format("{0} {1} ", this.scrollerCls, this.beforeScrollerCls)
+ });
+
+ var after = this.afterCt.createChild({
+ cls: String.format("{0} {1}", this.scrollerCls, this.afterScrollerCls)
+ });
+
+ before.addClassOnOver(this.beforeScrollerCls + '-hover');
+ after.addClassOnOver(this.afterScrollerCls + '-hover');
+
+ before.setVisibilityMode(Ext.Element.DISPLAY);
+ after.setVisibilityMode(Ext.Element.DISPLAY);
+
+ this.beforeRepeater = new Ext.util.ClickRepeater(before, {
+ interval: this.scrollRepeatInterval,
+ handler : this.scrollLeft,
+ scope : this
+ });
+
+ this.afterRepeater = new Ext.util.ClickRepeater(after, {
+ interval: this.scrollRepeatInterval,
+ handler : this.scrollRight,
+ scope : this
+ });
+
+ /**
+ * @property beforeScroller
+ * @type Ext.Element
+ * The left scroller element. Only created when needed.
+ */
+ this.beforeScroller = before;
+
+ /**
+ * @property afterScroller
+ * @type Ext.Element
+ * The left scroller element. Only created when needed.
+ */
+ this.afterScroller = after;
+ }
+ },
+
+ /**
+ * @private
+ */
+ destroy: function() {
+ Ext.destroy(this.beforeScroller, this.afterScroller, this.beforeRepeater, this.afterRepeater, this.beforeCt, this.afterCt);
+ },
+
+ /**
+ * @private
+ * Scrolls left or right by the number of pixels specified
+ * @param {Number} delta Number of pixels to scroll to the right by. Use a negative number to scroll left
+ */
+ scrollBy: function(delta, animate) {
+ this.scrollTo(this.getScrollPosition() + delta, animate);
+ },
+
+ /**
+ * @private
+ * Normalizes an item reference, string id or numerical index into a reference to the item
+ * @param {Ext.Component|String|Number} item The item reference, id or index
+ * @return {Ext.Component} The item
+ */
+ getItem: function(item) {
+ if (Ext.isString(item)) {
+ item = Ext.getCmp(item);
+ } else if (Ext.isNumber(item)) {
+ item = this.items[item];
+ }
+
+ return item;
+ },
+
+ /**
+ * @private
+ * @return {Object} Object passed to scrollTo when scrolling
+ */
+ getScrollAnim: function() {
+ return {
+ duration: this.scrollDuration,
+ callback: this.updateScrollButtons,
+ scope : this
+ };
+ },
+
+ /**
+ * @private
+ * Enables or disables each scroller button based on the current scroll position
+ */
+ updateScrollButtons: function() {
+ if (this.beforeScroller == undefined || this.afterScroller == undefined) {
+ return;
+ }
+
+ var beforeMeth = this.atExtremeBefore() ? 'addClass' : 'removeClass',
+ afterMeth = this.atExtremeAfter() ? 'addClass' : 'removeClass',
+ beforeCls = this.beforeScrollerCls + '-disabled',
+ afterCls = this.afterScrollerCls + '-disabled';
+
+ this.beforeScroller[beforeMeth](beforeCls);
+ this.afterScroller[afterMeth](afterCls);
+ this.scrolling = false;
+ },
+
+ /**
+ * @private
+ * Returns true if the innerCt scroll is already at its left-most point
+ * @return {Boolean} True if already at furthest left point
+ */
+ atExtremeBefore: function() {
+ return this.getScrollPosition() === 0;
+ },
+
+ /**
+ * @private
+ * Scrolls to the left by the configured amount
+ */
+ scrollLeft: function(animate) {
+ this.scrollBy(-this.scrollIncrement, animate);
+ },
+
+ /**
+ * @private
+ * Scrolls to the right by the configured amount
+ */
+ scrollRight: function(animate) {
+ this.scrollBy(this.scrollIncrement, animate);
+ },
+
+ /**
+ * Scrolls to the given component.
+ * @param {String|Number|Ext.Component} item The item to scroll to. Can be a numerical index, component id
+ * or a reference to the component itself.
+ * @param {Boolean} animate True to animate the scrolling
+ */
+ scrollToItem: function(item, animate) {
+ item = this.getItem(item);
+
+ if (item != undefined) {
+ var visibility = this.getItemVisibility(item);
+
+ if (!visibility.fullyVisible) {
+ var box = item.getBox(true, true),
+ newX = box.x;
+
+ if (visibility.hiddenRight) {
+ newX -= (this.layout.innerCt.getWidth() - box.width);
+ }
+
+ this.scrollTo(newX, animate);
+ }
+ }
+ },
+
+ /**
+ * @private
+ * For a given item in the container, return an object with information on whether the item is visible
+ * with the current innerCt scroll value.
+ * @param {Ext.Component} item The item
+ * @return {Object} Values for fullyVisible, hiddenLeft and hiddenRight
+ */
+ getItemVisibility: function(item) {
+ var box = this.getItem(item).getBox(true, true),
+ itemLeft = box.x,
+ itemRight = box.x + box.width,
+ scrollLeft = this.getScrollPosition(),
+ scrollRight = this.layout.innerCt.getWidth() + scrollLeft;
+
+ return {
+ hiddenLeft : itemLeft < scrollLeft,
+ hiddenRight : itemRight > scrollRight,
+ fullyVisible: itemLeft > scrollLeft && itemRight < scrollRight
+ };
+ }
+});
+
+Ext.layout.boxOverflow.scroller = Ext.layout.boxOverflow.Scroller;
+
+
+/**\r
+ * @class Ext.layout.boxOverflow.VerticalScroller\r
+ * @extends Ext.layout.boxOverflow.Scroller\r
+ * Description\r
+ */\r
+Ext.layout.boxOverflow.VerticalScroller = Ext.extend(Ext.layout.boxOverflow.Scroller, {
+ scrollIncrement: 75,
+ wheelIncrement : 2,
+
+ handleOverflow: function(calculations, targetSize) {
+ Ext.layout.boxOverflow.VerticalScroller.superclass.handleOverflow.apply(this, arguments);
+
+ return {
+ targetSize: {
+ height: targetSize.height - (this.beforeCt.getHeight() + this.afterCt.getHeight()),
+ width : targetSize.width
+ }
+ };
+ },
+
+ /**
+ * @private
+ * Creates the beforeCt and afterCt elements if they have not already been created
+ */
+ createInnerElements: function() {
+ var target = this.layout.innerCt;
+
+ //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of
+ //special items such as scrollers or dropdown menu triggers
+ if (!this.beforeCt) {
+ this.beforeCt = target.insertSibling({cls: this.beforeCls}, 'before');
+ this.afterCt = target.insertSibling({cls: this.afterCls}, 'after');
+
+ this.createWheelListener();
+ }
+ },
+
+ /**
+ * @private
+ * Scrolls to the given position. Performs bounds checking.
+ * @param {Number} position The position to scroll to. This is constrained.
+ * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
+ */
+ scrollTo: function(position, animate) {
+ var oldPosition = this.getScrollPosition(),
+ newPosition = position.constrain(0, this.getMaxScrollBottom());
+
+ if (newPosition != oldPosition && !this.scrolling) {
+ if (animate == undefined) {
+ animate = this.animateScroll;
+ }
+
+ this.layout.innerCt.scrollTo('top', newPosition, animate ? this.getScrollAnim() : false);
+
+ if (animate) {
+ this.scrolling = true;
+ } else {
+ this.scrolling = false;
+ this.updateScrollButtons();
+ }
+ }
+ },
+
+ /**
+ * Returns the current scroll position of the innerCt element
+ * @return {Number} The current scroll position
+ */
+ getScrollPosition: function(){
+ return parseInt(this.layout.innerCt.dom.scrollTop, 10) || 0;
+ },
+
+ /**
+ * @private
+ * Returns the maximum value we can scrollTo
+ * @return {Number} The max scroll value
+ */
+ getMaxScrollBottom: function() {
+ return this.layout.innerCt.dom.scrollHeight - this.layout.innerCt.getHeight();
+ },
+
+ /**
+ * @private
+ * Returns true if the innerCt scroll is already at its right-most point
+ * @return {Boolean} True if already at furthest right point
+ */
+ atExtremeAfter: function() {
+ return this.getScrollPosition() >= this.getMaxScrollBottom();
+ }
+});
+
+Ext.layout.boxOverflow.scroller.vbox = Ext.layout.boxOverflow.VerticalScroller;
+
+
+/**
+ * @class Ext.layout.boxOverflow.HorizontalScroller
+ * @extends Ext.layout.boxOverflow.Scroller
+ * Description
+ */
+Ext.layout.boxOverflow.HorizontalScroller = Ext.extend(Ext.layout.boxOverflow.Scroller, {
+ handleOverflow: function(calculations, targetSize) {
+ Ext.layout.boxOverflow.HorizontalScroller.superclass.handleOverflow.apply(this, arguments);
+
+ return {
+ targetSize: {
+ height: targetSize.height,
+ width : targetSize.width - (this.beforeCt.getWidth() + this.afterCt.getWidth())
+ }
+ };
+ },
+
+ /**
+ * @private
+ * Creates the beforeCt and afterCt elements if they have not already been created
+ */
+ createInnerElements: function() {
+ var target = this.layout.innerCt;
+
+ //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of
+ //special items such as scrollers or dropdown menu triggers
+ if (!this.beforeCt) {
+ this.afterCt = target.insertSibling({cls: this.afterCls}, 'before');
+ this.beforeCt = target.insertSibling({cls: this.beforeCls}, 'before');
+
+ this.createWheelListener();
+ }
+ },
+
+ /**
+ * @private
+ * Scrolls to the given position. Performs bounds checking.
+ * @param {Number} position The position to scroll to. This is constrained.
+ * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
+ */
+ scrollTo: function(position, animate) {
+ var oldPosition = this.getScrollPosition(),
+ newPosition = position.constrain(0, this.getMaxScrollRight());
+
+ if (newPosition != oldPosition && !this.scrolling) {
+ if (animate == undefined) {
+ animate = this.animateScroll;
+ }
+
+ this.layout.innerCt.scrollTo('left', newPosition, animate ? this.getScrollAnim() : false);
+
+ if (animate) {
+ this.scrolling = true;
+ } else {
+ this.scrolling = false;
+ this.updateScrollButtons();
+ }
+ }
+ },
+
+ /**
+ * Returns the current scroll position of the innerCt element
+ * @return {Number} The current scroll position
+ */
+ getScrollPosition: function(){
+ return parseInt(this.layout.innerCt.dom.scrollLeft, 10) || 0;
+ },
+
+ /**
+ * @private
+ * Returns the maximum value we can scrollTo
+ * @return {Number} The max scroll value
+ */
+ getMaxScrollRight: function() {
+ return this.layout.innerCt.dom.scrollWidth - this.layout.innerCt.getWidth();
+ },
+
+ /**
+ * @private
+ * Returns true if the innerCt scroll is already at its right-most point
+ * @return {Boolean} True if already at furthest right point
+ */
+ atExtremeAfter: function() {
+ return this.getScrollPosition() >= this.getMaxScrollRight();
+ }
+});
+
+Ext.layout.boxOverflow.scroller.hbox = Ext.layout.boxOverflow.HorizontalScroller;/**
+ * @class Ext.layout.HBoxLayout
+ * @extends Ext.layout.BoxLayout
+ * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
+ * space between child items containing a numeric <code>flex</code> configuration.</p>
+ * 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:
+ * <div class="mdetail-params"><ul>
+ * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
+ * at the <b>top</b> of the container</div></li>
+ * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
+ * <b>middle</b> of the container</div></li>
+ * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
+ * the height of the container</div></li>
+ * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
+ * the height of the largest item.</div></li>
+ */
+ align: 'top', // top, middle, stretch, strechmax
+
+ type : 'hbox',
+
+ /**
+ * @cfg {String} pack
+ * Controls how the child items of the container are packed together. Acceptable configuration values
+ * for this property are:
+ * <div class="mdetail-params"><ul>
+ * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
+ * <b>left</b> side of container</div></li>
+ * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
+ * <b>mid-width</b> of container</div></li>
+ * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
+ * side of container</div></li>
+ * </ul></div>
+ */
+ /**
+ * @cfg {Number} flex
+ * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
+ * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
+ * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
+ * a <tt>flex</tt> value specified. Any child items that have either a <tt>flex = 0</tt> or
+ * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
+ */
+
+ /**
+ * @private
+ * Calculates the size and positioning of each item in the HBox. This iterates over all of the rendered,
+ * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
+ * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
+ * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
+ * @param {Object} targetSize Object containing target size and height
+ * @return {Object} Object containing box measurements for each child, plus meta data
+ */
+ calculateChildBoxes: function(visibleItems, targetSize) {
+ var visibleCount = visibleItems.length,
+
+ padding = this.padding,
+ topOffset = padding.top,
+ leftOffset = padding.left,
+ paddingVert = topOffset + padding.bottom,
+ paddingHoriz = leftOffset + padding.right,
+
+ width = targetSize.width - this.scrollOffset,
+ height = targetSize.height,
+ availHeight = Math.max(0, height - paddingVert),
+
+ isStart = this.pack == 'start',
+ isCenter = this.pack == 'center',
+ isEnd = this.pack == 'end',
+
+ nonFlexWidth = 0,
+ maxHeight = 0,
+ totalFlex = 0,
+ desiredWidth = 0,
+ minimumWidth = 0,
+
+ //used to cache the calculated size and position values for each child item
+ boxes = [],
+
+ //used in the for loops below, just declared here for brevity
+ child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth,
+ horizMargins, vertMargins, stretchHeight;
+
+ //gather the total flex of all flexed items and the width taken up by fixed width items
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ childHeight = child.height;
+ childWidth = child.width;
+ canLayout = !child.hasLayout && typeof child.doLayout == 'function';
+
+ // Static width (numeric) requires no calcs
+ if (typeof childWidth != 'number') {
+
+ // flex and not 'auto' width
+ if (child.flex && !childWidth) {
+ totalFlex += child.flex;
+
+ // Not flexed or 'auto' width or undefined width
+ } else {
+ //Render and layout sub-containers without a flex or width defined, as otherwise we
+ //don't know how wide the sub-container should be and cannot calculate flexed widths
+ if (!childWidth && canLayout) {
+ child.doLayout();
+ }
+
+ childSize = child.getSize();
+ childWidth = childSize.width;
+ childHeight = childSize.height;
+ }
+ }
+
+ childMargins = child.margins;
+ horizMargins = childMargins.left + childMargins.right;
+
+ nonFlexWidth += horizMargins + (childWidth || 0);
+ desiredWidth += horizMargins + (child.flex ? child.minWidth || 0 : childWidth);
+ minimumWidth += horizMargins + (child.minWidth || childWidth || 0);
+
+ // Max height for align - force layout of non-laid out subcontainers without a numeric height
+ if (typeof childHeight != 'number') {
+ if (canLayout) {
+ child.doLayout();
+ }
+ childHeight = child.getHeight();
+ }
+
+ maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
+
+ //cache the size of each child component. Don't set height or width to 0, keep undefined instead
+ boxes.push({
+ component: child,
+ height : childHeight || undefined,
+ width : childWidth || undefined
+ });
+ }
+
+ var shortfall = desiredWidth - width,
+ tooNarrow = minimumWidth > width;
+
+ //the width available to the flexed items
+ var availableWidth = Math.max(0, width - nonFlexWidth - paddingHoriz);
+
+ if (tooNarrow) {
+ for (i = 0; i < visibleCount; i++) {
+ boxes[i].width = visibleItems[i].minWidth || visibleItems[i].width || boxes[i].width;
+ }
+ } else {
+ //all flexed items should be sized to their minimum width, other items should be shrunk down until
+ //the shortfall has been accounted for
+ if (shortfall > 0) {
+ var minWidths = [];
+
+ /**
+ * When we have a shortfall but are not tooNarrow, we need to shrink the width of each non-flexed item.
+ * Flexed items are immediately reduced to their minWidth and anything already at minWidth is ignored.
+ * The remaining items are collected into the minWidths array, which is later used to distribute the shortfall.
+ */
+ for (var index = 0, length = visibleCount; index < length; index++) {
+ var item = visibleItems[index],
+ minWidth = item.minWidth || 0;
+
+ //shrink each non-flex tab by an equal amount to make them all fit. Flexed items are all
+ //shrunk to their minWidth because they're flexible and should be the first to lose width
+ if (item.flex) {
+ boxes[index].width = minWidth;
+ } else {
+ minWidths.push({
+ minWidth : minWidth,
+ available: boxes[index].width - minWidth,
+ index : index
+ });
+ }
+ }
+
+ //sort by descending amount of width remaining before minWidth is reached
+ minWidths.sort(function(a, b) {
+ return a.available > b.available ? 1 : -1;
+ });
+
+ /*
+ * Distribute the shortfall (difference between total desired with of all items and actual width available)
+ * between the non-flexed items. We try to distribute the shortfall evenly, but apply it to items with the
+ * smallest difference between their width and minWidth first, so that if reducing the width by the average
+ * amount would make that item less than its minWidth, we carry the remainder over to the next item.
+ */
+ for (var i = 0, length = minWidths.length; i < length; i++) {
+ var itemIndex = minWidths[i].index;
+
+ if (itemIndex == undefined) {
+ continue;
+ }
+
+ var item = visibleItems[itemIndex],
+ box = boxes[itemIndex],
+ oldWidth = box.width,
+ minWidth = item.minWidth,
+ newWidth = Math.max(minWidth, oldWidth - Math.ceil(shortfall / (length - i))),
+ reduction = oldWidth - newWidth;
+
+ boxes[itemIndex].width = newWidth;
+ shortfall -= reduction;
+ }
+ } else {
+ //temporary variables used in the flex width calculations below
+ var remainingWidth = availableWidth,
+ remainingFlex = totalFlex;
+
+ //calculate the widths of each flexed item
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ calcs = boxes[i];
+
+ childMargins = child.margins;
+ vertMargins = childMargins.top + childMargins.bottom;
+
+ if (isStart && child.flex && !child.width) {
+ flexedWidth = Math.ceil((child.flex / remainingFlex) * remainingWidth);
+ remainingWidth -= flexedWidth;
+ remainingFlex -= child.flex;
+
+ calcs.width = flexedWidth;
+ calcs.dirtySize = true;
+ }
+ }
+ }
+ }
+
+ if (isCenter) {
+ leftOffset += availableWidth / 2;
+ } else if (isEnd) {
+ leftOffset += availableWidth;
+ }
+
+ //finally, calculate the left and top position of each item
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ calcs = boxes[i];
+
+ childMargins = child.margins;
+ leftOffset += childMargins.left;
+ vertMargins = childMargins.top + childMargins.bottom;
+
+ calcs.left = leftOffset;
+ calcs.top = topOffset + childMargins.top;
+
+ switch (this.align) {
+ case 'stretch':
+ stretchHeight = availHeight - vertMargins;
+ calcs.height = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
+ calcs.dirtySize = true;
+ break;
+ case 'stretchmax':
+ stretchHeight = maxHeight - vertMargins;
+ calcs.height = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
+ calcs.dirtySize = true;
+ break;
+ case 'middle':
+ var diff = availHeight - calcs.height - vertMargins;
+ if (diff > 0) {
+ calcs.top = topOffset + vertMargins + (diff / 2);
+ }
+ }
+
+ leftOffset += calcs.width + childMargins.right;
+ }
+
+ return {
+ boxes: boxes,
+ meta : {
+ maxHeight : maxHeight,
+ nonFlexWidth: nonFlexWidth,
+ desiredWidth: desiredWidth,
+ minimumWidth: minimumWidth,
+ shortfall : desiredWidth - width,
+ tooNarrow : tooNarrow
+ }
+ };
+ }
+});
+
+Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;/**
+ * @class Ext.layout.VBoxLayout
+ * @extends Ext.layout.BoxLayout
+ * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
+ * space between child items containing a numeric <code>flex</code> configuration.</p>
+ * 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:
+ * <div class="mdetail-params"><ul>
+ * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
+ * at the <b>left</b> side of the container</div></li>
+ * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
+ * <b>mid-width</b> of the container</div></li>
+ * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
+ * the width of the container</div></li>
+ * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
+ * the size of the largest item.</div></li>
+ * </ul></div>
+ */
+ align : 'left', // left, center, stretch, strechmax
+ type: 'vbox',
+
+ /**
+ * @cfg {String} pack
+ * Controls how the child items of the container are packed together. Acceptable configuration values
+ * for this property are:
+ * <div class="mdetail-params"><ul>
+ * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
+ * <b>top</b> side of container</div></li>
+ * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
+ * <b>mid-height</b> of container</div></li>
+ * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>
+ * side of container</div></li>
+ * </ul></div>
+ */
+
+ /**
+ * @cfg {Number} flex
+ * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
+ * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>
+ * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
+ * a <tt>flex</tt> value specified. Any child items that have either a <tt>flex = 0</tt> or
+ * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
+ */
+
+ /**
+ * @private
+ * Calculates the size and positioning of each item in the VBox. This iterates over all of the rendered,
+ * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
+ * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
+ * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
+ * @param {Object} targetSize Object containing target size and height
+ * @return {Object} Object containing box measurements for each child, plus meta data
+ */
+ calculateChildBoxes: function(visibleItems, targetSize) {
+ var visibleCount = visibleItems.length,
+
+ padding = this.padding,
+ topOffset = padding.top,
+ leftOffset = padding.left,
+ paddingVert = topOffset + padding.bottom,
+ paddingHoriz = leftOffset + padding.right,
+
+ width = targetSize.width - this.scrollOffset,
+ height = targetSize.height,
+ availWidth = Math.max(0, width - paddingHoriz),
+
+ isStart = this.pack == 'start',
+ isCenter = this.pack == 'center',
+ isEnd = this.pack == 'end',
+
+ nonFlexHeight= 0,
+ maxWidth = 0,
+ totalFlex = 0,
+ desiredHeight= 0,
+ minimumHeight= 0,
+
+ //used to cache the calculated size and position values for each child item
+ boxes = [],
+
+ //used in the for loops below, just declared here for brevity
+ child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth,
+ horizMargins, vertMargins, stretchWidth;
+
+ //gather the total flex of all flexed items and the width taken up by fixed width items
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ childHeight = child.height;
+ childWidth = child.width;
+ canLayout = !child.hasLayout && typeof child.doLayout == 'function';
+
+ // Static height (numeric) requires no calcs
+ if (typeof childHeight != 'number') {
+
+ // flex and not 'auto' height
+ if (child.flex && !childHeight) {
+ totalFlex += child.flex;
+
+ // Not flexed or 'auto' height or undefined height
+ } else {
+ //Render and layout sub-containers without a flex or width defined, as otherwise we
+ //don't know how wide the sub-container should be and cannot calculate flexed widths
+ if (!childHeight && canLayout) {
+ child.doLayout();
+ }
+
+ childSize = child.getSize();
+ childWidth = childSize.width;
+ childHeight = childSize.height;
+ }
+ }
+
+ childMargins = child.margins;
+ vertMargins = childMargins.top + childMargins.bottom;
+
+ nonFlexHeight += vertMargins + (childHeight || 0);
+ desiredHeight += vertMargins + (child.flex ? child.minHeight || 0 : childHeight);
+ minimumHeight += vertMargins + (child.minHeight || childHeight || 0);
+
+ // Max width for align - force layout of non-layed out subcontainers without a numeric width
+ if (typeof childWidth != 'number') {
+ if (canLayout) {
+ child.doLayout();
+ }
+ childWidth = child.getWidth();
+ }
+
+ maxWidth = Math.max(maxWidth, childWidth + childMargins.left + childMargins.right);
+
+ //cache the size of each child component
+ boxes.push({
+ component: child,
+ height : childHeight || undefined,
+ width : childWidth || undefined
+ });
+ }
+
+ var shortfall = desiredHeight - height,
+ tooNarrow = minimumHeight > height;
+
+ //the height available to the flexed items
+ var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
+
+ if (tooNarrow) {
+ for (i = 0, length = visibleCount; i < length; i++) {
+ boxes[i].height = visibleItems[i].minHeight || visibleItems[i].height || boxes[i].height;
+ }
+ } else {
+ //all flexed items should be sized to their minimum width, other items should be shrunk down until
+ //the shortfall has been accounted for
+ if (shortfall > 0) {
+ var minHeights = [];
+
+ /**
+ * When we have a shortfall but are not tooNarrow, we need to shrink the height of each non-flexed item.
+ * Flexed items are immediately reduced to their minHeight and anything already at minHeight is ignored.
+ * The remaining items are collected into the minHeights array, which is later used to distribute the shortfall.
+ */
+ for (var index = 0, length = visibleCount; index < length; index++) {
+ var item = visibleItems[index],
+ minHeight = item.minHeight || 0;
+
+ //shrink each non-flex tab by an equal amount to make them all fit. Flexed items are all
+ //shrunk to their minHeight because they're flexible and should be the first to lose height
+ if (item.flex) {
+ boxes[index].height = minHeight;
+ } else {
+ minHeights.push({
+ minHeight: minHeight,
+ available: boxes[index].height - minHeight,
+ index : index
+ });
+ }
+ }
+
+ //sort by descending minHeight value
+ minHeights.sort(function(a, b) {
+ return a.available > b.available ? 1 : -1;
+ });
+
+ /*
+ * Distribute the shortfall (difference between total desired with of all items and actual height available)
+ * between the non-flexed items. We try to distribute the shortfall evenly, but apply it to items with the
+ * smallest difference between their height and minHeight first, so that if reducing the height by the average
+ * amount would make that item less than its minHeight, we carry the remainder over to the next item.
+ */
+ for (var i = 0, length = minHeights.length; i < length; i++) {
+ var itemIndex = minHeights[i].index;
+
+ if (itemIndex == undefined) {
+ continue;
+ }
+
+ var item = visibleItems[itemIndex],
+ box = boxes[itemIndex],
+ oldHeight = box.height,
+ minHeight = item.minHeight,
+ newHeight = Math.max(minHeight, oldHeight - Math.ceil(shortfall / (length - i))),
+ reduction = oldHeight - newHeight;
+
+ boxes[itemIndex].height = newHeight;
+ shortfall -= reduction;
+ }
+ } else {
+ //temporary variables used in the flex height calculations below
+ var remainingHeight = availableHeight,
+ remainingFlex = totalFlex;
+
+ //calculate the height of each flexed item
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ calcs = boxes[i];
+
+ childMargins = child.margins;
+ horizMargins = childMargins.left + childMargins.right;
+
+ if (isStart && child.flex && !child.height) {
+ flexedHeight = Math.ceil((child.flex / remainingFlex) * remainingHeight);
+ remainingHeight -= flexedHeight;
+ remainingFlex -= child.flex;
+
+ calcs.height = flexedHeight;
+ calcs.dirtySize = true;
+ }
+ }
+ }
+ }
+
+ if (isCenter) {
+ topOffset += availableHeight / 2;
+ } else if (isEnd) {
+ topOffset += availableHeight;
+ }
+
+ //finally, calculate the left and top position of each item
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ calcs = boxes[i];
+
+ childMargins = child.margins;
+ topOffset += childMargins.top;
+ horizMargins = childMargins.left + childMargins.right;
+
+
+ calcs.left = leftOffset + childMargins.left;
+ calcs.top = topOffset;
+
+ switch (this.align) {
+ case 'stretch':
+ stretchWidth = availWidth - horizMargins;
+ calcs.width = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
+ calcs.dirtySize = true;
+ break;
+ case 'stretchmax':
+ stretchWidth = maxWidth - horizMargins;
+ calcs.width = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
+ calcs.dirtySize = true;
+ break;
+ case 'center':
+ var diff = availWidth - calcs.width - horizMargins;
+ if (diff > 0) {
+ calcs.left = leftOffset + horizMargins + (diff / 2);
+ }
+ }
+
+ topOffset += calcs.height + childMargins.bottom;
+ }
+
+ return {
+ boxes: boxes,
+ meta : {
+ maxWidth : maxWidth,
+ nonFlexHeight: nonFlexHeight,
+ desiredHeight: desiredHeight,
+ minimumHeight: minimumHeight,
+ shortfall : desiredHeight - height,
+ tooNarrow : tooNarrow
+ }
+ };
+ }
+});
+
+Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;
+/**
+ * @class Ext.layout.ToolbarLayout
+ * @extends Ext.layout.ContainerLayout
+ * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not
+ * usually be used by any other class.
+ */
+Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ monitorResize : true,
+
+ type: 'toolbar',
+
+ /**
+ * @property triggerWidth
+ * @type Number
+ * The width allocated for the menu trigger at the extreme right end of the Toolbar
+ */
+ triggerWidth: 18,
+
+ /**
+ * @property noItemsMenuText
+ * @type String
+ * HTML fragment to render into the toolbar overflow menu if there are no items to display
+ */
+ noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
+
+ /**
+ * @private
+ * @property lastOverflow
+ * @type Boolean
+ * Used internally to record whether the last layout caused an overflow or not
+ */
+ lastOverflow: false,
+
+ /**
+ * @private
+ * @property tableHTML
+ * @type String
+ * String used to build the HTML injected to support the Toolbar's layout. The align property is
+ * injected into this string inside the td.x-toolbar-left element during onLayout.
+ */
+ tableHTML: [
+ '<table cellspacing="0" class="x-toolbar-ct">',
+ '<tbody>',
+ '<tr>',
+ '<td class="x-toolbar-left" align="{0}">',
+ '<table cellspacing="0">',
+ '<tbody>',
+ '<tr class="x-toolbar-left-row"></tr>',
+ '</tbody>',
+ '</table>',
+ '</td>',
+ '<td class="x-toolbar-right" align="right">',
+ '<table cellspacing="0" class="x-toolbar-right-ct">',
+ '<tbody>',
+ '<tr>',
+ '<td>',
+ '<table cellspacing="0">',
+ '<tbody>',
+ '<tr class="x-toolbar-right-row"></tr>',
+ '</tbody>',
+ '</table>',
+ '</td>',
+ '<td>',
+ '<table cellspacing="0">',
+ '<tbody>',
+ '<tr class="x-toolbar-extras-row"></tr>',
+ '</tbody>',
+ '</table>',
+ '</td>',
+ '</tr>',
+ '</tbody>',
+ '</table>',
+ '</td>',
+ '</tr>',
+ '</tbody>',
+ '</table>'
+ ].join(""),
+
+ /**
+ * @private
+ * Create the wrapping Toolbar HTML and render/move all the items into the correct places
+ */
+ onLayout : function(ct, target) {
+ //render the Toolbar <table> HTML if it's not already present
+ if (!this.leftTr) {
+ var align = ct.buttonAlign == 'center' ? 'center' : 'left';
+
+ target.addClass('x-toolbar-layout-ct');
+ target.insertHtml('beforeEnd', String.format(this.tableHTML, align));
+
+ this.leftTr = target.child('tr.x-toolbar-left-row', true);
+ this.rightTr = target.child('tr.x-toolbar-right-row', true);
+ this.extrasTr = target.child('tr.x-toolbar-extras-row', true);
+
+ if (this.hiddenItem == undefined) {
+ /**
+ * @property hiddenItems
+ * @type Array
+ * Holds all items that are currently hidden due to there not being enough space to render them
+ * These items will appear on the expand menu.
+ */
+ this.hiddenItems = [];
+ }
+ }
+
+ var side = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
+ items = ct.items.items,
+ position = 0;
+
+ //render each item if not already rendered, place it into the correct (left or right) target
+ for (var i = 0, len = items.length, c; i < len; i++, position++) {
+ c = items[i];
+
+ if (c.isFill) {
+ side = this.rightTr;
+ position = -1;
+ } else if (!c.rendered) {
+ c.render(this.insertCell(c, side, position));
+ this.configureItem(c);
+ } else {
+ if (!c.xtbHidden && !this.isValidParent(c, side.childNodes[position])) {
+ var td = this.insertCell(c, side, position);
+ td.appendChild(c.getPositionEl().dom);
+ c.container = Ext.get(td);
+ }
+ }
+ }
+
+ //strip extra empty cells
+ this.cleanup(this.leftTr);
+ this.cleanup(this.rightTr);
+ this.cleanup(this.extrasTr);
+ this.fitToSize(target);
+ },
+
+ /**
+ * @private
+ * Removes any empty nodes from the given element
+ * @param {Ext.Element} el The element to clean up
+ */
+ cleanup : function(el) {
+ var cn = el.childNodes, i, c;
+
+ for (i = cn.length-1; i >= 0 && (c = cn[i]); i--) {
+ if (!c.firstChild) {
+ el.removeChild(c);
+ }
+ }
+ },
+
+ /**
+ * @private
+ * Inserts the given Toolbar item into the given element
+ * @param {Ext.Component} c The component to add
+ * @param {Ext.Element} target The target to add the component to
+ * @param {Number} position The position to add the component at
+ */
+ insertCell : function(c, target, position) {
+ var td = document.createElement('td');
+ td.className = 'x-toolbar-cell';
+
+ target.insertBefore(td, target.childNodes[position] || null);
+
+ return td;
+ },
+
+ /**
+ * @private
+ * Hides an item because it will not fit in the available width. The item will be unhidden again
+ * if the Toolbar is resized to be large enough to show it
+ * @param {Ext.Component} item The item to hide
+ */
+ hideItem : function(item) {
+ this.hiddenItems.push(item);
+
+ item.xtbHidden = true;
+ item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
+ item.hide();
+ },
+
+ /**
+ * @private
+ * Unhides an item that was previously hidden due to there not being enough space left on the Toolbar
+ * @param {Ext.Component} item The item to show
+ */
+ unhideItem : function(item) {
+ item.show();
+ item.xtbHidden = false;
+ this.hiddenItems.remove(item);
+ },
+
+ /**
+ * @private
+ * Returns the width of the given toolbar item. If the item is currently hidden because there
+ * is not enough room to render it, its previous width is returned
+ * @param {Ext.Component} c The component to measure
+ * @return {Number} The width of the item
+ */
+ getItemWidth : function(c) {
+ return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
+ },
+
+ /**
+ * @private
+ * Called at the end of onLayout. At this point the Toolbar has already been resized, so we need
+ * to fit the items into the available width. We add up the width required by all of the items in
+ * the toolbar - if we don't have enough space we hide the extra items and render the expand menu
+ * trigger.
+ * @param {Ext.Element} target The Element the Toolbar is currently laid out within
+ */
+ fitToSize : function(target) {
+ if (this.container.enableOverflow === false) {
+ return;
+ }
+
+ var width = target.dom.clientWidth,
+ tableWidth = target.dom.firstChild.offsetWidth,
+ clipWidth = width - this.triggerWidth,
+ lastWidth = this.lastWidth || 0,
+
+ hiddenItems = this.hiddenItems,
+ hasHiddens = hiddenItems.length != 0,
+ isLarger = width >= lastWidth;
+
+ this.lastWidth = width;
+
+ if (tableWidth > width || (hasHiddens && isLarger)) {
+ var items = this.container.items.items,
+ len = items.length,
+ loopWidth = 0,
+ item;
+
+ for (var i = 0; i < len; i++) {
+ item = items[i];
+
+ if (!item.isFill) {
+ loopWidth += this.getItemWidth(item);
+ if (loopWidth > clipWidth) {
+ if (!(item.hidden || item.xtbHidden)) {
+ this.hideItem(item);
+ }
+ } else if (item.xtbHidden) {
+ this.unhideItem(item);
+ }
+ }
+ }
+ }
+
+ //test for number of hidden items again here because they may have changed above
+ hasHiddens = hiddenItems.length != 0;
+
+ if (hasHiddens) {
+ this.initMore();
+
+ if (!this.lastOverflow) {
+ this.container.fireEvent('overflowchange', this.container, true);
+ this.lastOverflow = true;
+ }
+ } else if (this.more) {
+ this.clearMenu();
+ this.more.destroy();
+ delete this.more;
+
+ if (this.lastOverflow) {
+ this.container.fireEvent('overflowchange', this.container, false);
+ this.lastOverflow = false;
+ }
+ }
+ },
+
+ /**
+ * @private
+ * Returns a menu config for a given component. This config is used to create a menu item
+ * to be added to the expander menu
+ * @param {Ext.Component} component The component to create the config for
+ * @param {Boolean} hideOnClick Passed through to the menu item
+ */
+ createMenuConfig : function(component, hideOnClick){
+ var config = Ext.apply({}, component.initialConfig),
+ group = component.toggleGroup;
+
+ Ext.copyTo(config, component, [
+ 'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
+ ]);
+
+ Ext.apply(config, {
+ text : component.overflowText || component.text,
+ hideOnClick: hideOnClick
+ });
+
+ if (group || component.enableToggle) {
+ Ext.apply(config, {
+ group : group,
+ checked: component.pressed,
+ listeners: {
+ checkchange: function(item, checked){
+ component.toggle(checked);
+ }
+ }
+ });
+ }
+
+ delete config.ownerCt;
+ delete config.xtype;
+ delete config.id;
+
+ return config;
+ },
+
+ /**
+ * @private
+ * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
+ * @param {Ext.menu.Menu} menu The menu to add to
+ * @param {Ext.Component} component The component to add
+ */
+ addComponentToMenu : function(menu, component) {
+ if (component instanceof Ext.Toolbar.Separator) {
+ menu.add('-');
+
+ } else if (Ext.isFunction(component.isXType)) {
+ if (component.isXType('splitbutton')) {
+ menu.add(this.createMenuConfig(component, true));
+
+ } else if (component.isXType('button')) {
+ menu.add(this.createMenuConfig(component, !component.menu));
+
+ } else if (component.isXType('buttongroup')) {
+ component.items.each(function(item){
+ this.addComponentToMenu(menu, item);
+ }, this);
+ }
+ }
+ },
+
+ /**
+ * @private
+ * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
+ * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
+ */
+ clearMenu : function(){
+ var menu = this.moreMenu;
+ if (menu && menu.items) {
+ menu.items.each(function(item){
+ delete item.menu;
+ });
+ }
+ },
+
+ /**
+ * @private
+ * Called before the expand menu is shown, this rebuilds the menu since it was last shown because
+ * it is possible that the items hidden due to space limitations on the Toolbar have changed since.
+ * @param {Ext.menu.Menu} m The menu
+ */
+ beforeMoreShow : function(menu) {
+ var items = this.container.items.items,
+ len = items.length,
+ item,
+ prev;
+
+ var needsSep = function(group, item){
+ return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
+ };
+
+ this.clearMenu();
+ menu.removeAll();
+ for (var i = 0; i < len; i++) {
+ item = items[i];
+ if (item.xtbHidden) {
+ if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
+ menu.add('-');
+ }
+ this.addComponentToMenu(menu, item);
+ prev = item;
+ }
+ }
+
+ // put something so the menu isn't empty if no compatible items found
+ if (menu.items.length < 1) {
+ menu.add(this.noItemsMenuText);
+ }
+ },
+
+ /**
+ * @private
+ * Creates the expand trigger and menu, adding them to the <tr> at the extreme right of the
+ * Toolbar table
+ */
+ initMore : function(){
+ if (!this.more) {
+ /**
+ * @private
+ * @property moreMenu
+ * @type Ext.menu.Menu
+ * The expand menu - holds items for every Toolbar item that cannot be shown
+ * because the Toolbar is currently not wide enough.
+ */
+ this.moreMenu = new Ext.menu.Menu({
+ ownerCt : this.container,
+ listeners: {
+ beforeshow: this.beforeMoreShow,
+ scope: this
+ }
+ });
+
+ /**
+ * @private
+ * @property more
+ * @type Ext.Button
+ * The expand button which triggers the overflow menu to be shown
+ */
+ this.more = new Ext.Button({
+ iconCls: 'x-toolbar-more-icon',
+ cls : 'x-toolbar-more',
+ menu : this.moreMenu,
+ ownerCt: this.container
+ });
+
+ var td = this.insertCell(this.more, this.extrasTr, 100);
+ this.more.render(td);
+ }
+ },
+
+ destroy : function(){
+ Ext.destroy(this.more, this.moreMenu);
+ delete this.leftTr;
+ delete this.rightTr;
+ delete this.extrasTr;
+ Ext.layout.ToolbarLayout.superclass.destroy.call(this);
+ }
+});
+
+Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;
+/**
+ * @class Ext.layout.MenuLayout
+ * @extends Ext.layout.ContainerLayout
+ * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>
+ */
+ Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ monitorResize : true,
+
+ type: 'menu',
+
+ setContainer : function(ct){
+ this.monitorResize = !ct.floating;
+ // This event is only fired by the menu in IE, used so we don't couple
+ // the menu with the layout.
+ ct.on('autosize', this.doAutoSize, this);
+ Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);
+ },
+
+ renderItem : function(c, position, target){
+ if (!this.itemTpl) {
+ this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(
+ '<li id="{itemId}" class="{itemCls}">',
+ '<tpl if="needsIcon">',
+ '<img alt="{altText}" src="{icon}" class="{iconCls}"/>',
+ '</tpl>',
+ '</li>'
+ );
+ }
+
+ if(c && !c.rendered){
+ if(Ext.isNumber(position)){
+ position = target.dom.childNodes[position];
+ }
+ var a = this.getItemArgs(c);
+
+// The Component's positionEl is the <li> it is rendered into
+ c.render(c.positionEl = position ?
+ this.itemTpl.insertBefore(position, a, true) :
+ this.itemTpl.append(target, a, true));
+
+// Link the containing <li> to the item.
+ c.positionEl.menuItemId = c.getItemId();
+
+// If rendering a regular Component, and it needs an icon,
+// move the Component rightwards.
+ if (!a.isMenuItem && a.needsIcon) {
+ c.positionEl.addClass('x-menu-list-item-indent');
+ }
+ this.configureItem(c);
+ }else if(c && !this.isValidParent(c, target)){
+ if(Ext.isNumber(position)){
+ position = target.dom.childNodes[position];
+ }
+ target.dom.insertBefore(c.getActionEl().dom, position || null);
+ }
+ },
+
+ getItemArgs : function(c) {
+ var isMenuItem = c instanceof Ext.menu.Item,
+ canHaveIcon = !(isMenuItem || c instanceof Ext.menu.Separator);
+
+ return {
+ isMenuItem: isMenuItem,
+ needsIcon: canHaveIcon && (c.icon || c.iconCls),
+ icon: c.icon || Ext.BLANK_IMAGE_URL,
+ iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),
+ itemId: 'x-menu-el-' + c.id,
+ itemCls: 'x-menu-list-item ',
+ altText: c.altText || ''
+ };
+ },
+
+ // Valid if the Component is in a <li> which is part of our target <ul>
+ isValidParent : function(c, target) {
+ return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);
+ },
+
+ onLayout : function(ct, target){
+ Ext.layout.MenuLayout.superclass.onLayout.call(this, ct, target);
+ this.doAutoSize();
+ },
+
+ doAutoSize : function(){
+ var ct = this.container, w = ct.width;
+ if(ct.floating){
+ if(w){
+ ct.setWidth(w);
+ }else if(Ext.isIE){
+ ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);
+ var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc
+ ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));
+ }
+ }
+ }
+});
+Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;
+/**
+ * @class Ext.Viewport
+ * @extends Ext.Container
+ * <p>A specialized container representing the viewable application area (the browser viewport).</p>
+ * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of
+ * the browser viewport and manages window resizing. There may only be one Viewport created
+ * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s
+ * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}
+ * method of any of its child Panels may themselves have a layout.</p>
+ * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide
+ * for scrolling if needed using the {@link #autoScroll} config.</p>
+ * <p>An example showing a classic application border layout:</p><pre><code>
+new Ext.Viewport({
+ layout: 'border',
+ items: [{
+ region: 'north',
+ html: '<h1 class="x-panel-header">Page Title</h1>',
+ autoHeight: true,
+ border: false,
+ margins: '0 0 5 0'
+ }, {
+ region: 'west',
+ 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}
+ }, {
+ region: 'south',
+ title: 'Title for Panel',
+ collapsible: true,
+ html: 'Information goes here',
+ split: true,
+ height: 100,
+ minHeight: 100
+ }, {
+ region: 'east',
+ title: 'Title for the Grid Panel',
+ collapsible: true,
+ split: true,
+ width: 200,
+ xtype: 'grid',
+ // remaining grid configuration not shown ...
+ // notice that the GridPanel is added directly as the region
+ // it is not "overnested" inside another Panel
+ }, {
+ region: 'center',
+ xtype: 'tabpanel', // TabPanel itself has no title
+ items: {
+ title: 'Default Tab',
+ html: 'The first tab\'s content. Others may be added dynamically'
+ }
+ }]
+});
+</code></pre>
+ * @constructor
+ * Create a new Viewport
+ * @param {Object} config The config object
+ * @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.
+ */
+ /**
+ * @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';
+ this.el = Ext.getBody();
+ this.el.setHeight = Ext.emptyFn;
+ this.el.setWidth = Ext.emptyFn;
+ this.el.setSize = Ext.emptyFn;
+ this.el.dom.scroll = 'no';
+ this.allowDomMove = false;
+ this.autoWidth = true;
+ this.autoHeight = true;
+ Ext.EventManager.onWindowResize(this.fireResize, this);
+ this.renderTo = this.el;
+ },
+
+ fireResize : function(w, h){
+ this.fireEvent('resize', this, w, h, w, h);
+ }
+});
+Ext.reg('viewport', Ext.Viewport);
+/**
+ * @class Ext.Panel
+ * @extends Ext.Container
+ * <p>Panel is a container that has specific functionality and structural components that make
+ * it the perfect building block for application-oriented user interfaces.</p>
+ * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
+ * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
+ * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
+ * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
+ * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.Container#layout layout}</b></code> schemes. By
+ * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
+ * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
+ * at all.</p>
+ * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
+ * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
+ * information).</p>
+ * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
+ * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
+ * behavior. Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
+ * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
+ * @constructor
+ * @param {Object} config The config object
+ * @xtype panel
+ */
+Ext.Panel = Ext.extend(Ext.Container, {
+ /**
+ * The Panel's header {@link Ext.Element Element}. Read-only.
+ * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
+ * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
+ * @type Ext.Element
+ * @property header
+ */
+ /**
+ * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
+ * The content may be specified in the {@link #html} config, or it may be loaded using the
+ * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
+ * <p>If this is used to load visible HTML elements in either way, then
+ * the Panel may not be used as a Layout for hosting nested Panels.</p>
+ * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
+ * then the body Element must not be loaded or changed - it is under the control
+ * of the Panel's Layout.
+ * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
+ * @type Ext.Element
+ * @property body
+ */
+ /**
+ * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
+ * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
+ * @type Ext.Element
+ * @property bwrap
+ */
+ /**
+ * True if this panel is collapsed. Read-only.
+ * @type Boolean
+ * @property collapsed
+ */
+ /**
+ * @cfg {Object} bodyCfg
+ * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
+ * Panel Element.</p>
+ * <p>By default, the Default element in the table below will be used for the html markup to
+ * create a child element with the commensurate Default class name (<code>baseCls</code> will be
+ * replaced by <code>{@link #baseCls}</code>):</p>
+ * <pre>
+ * Panel Default Default Custom Additional Additional
+ * Element element class element class style
+ * ======== ========================== ========= ============== ===========
+ * {@link #header} div {@link #baseCls}+'-header' {@link #headerCfg} headerCssClass headerStyle
+ * {@link #bwrap} div {@link #baseCls}+'-bwrap' {@link #bwrapCfg} bwrapCssClass bwrapStyle
+ * + tbar div {@link #baseCls}+'-tbar' {@link #tbarCfg} tbarCssClass tbarStyle
+ * + {@link #body} div {@link #baseCls}+'-body' {@link #bodyCfg} {@link #bodyCssClass} {@link #bodyStyle}
+ * + bbar div {@link #baseCls}+'-bbar' {@link #bbarCfg} bbarCssClass bbarStyle
+ * + {@link #footer} div {@link #baseCls}+'-footer' {@link #footerCfg} footerCssClass footerStyle
+ * </pre>
+ * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
+ * to use a different form of markup than is created by default. An example of this might be
+ * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
+ * a header, or forcing centering of all Panel content by having the body be a <center>
+ * element:</p>
+ * <pre><code>
+new Ext.Panel({
+ title: 'Message Title',
+ renderTo: Ext.getBody(),
+ width: 200, height: 130,
+ <b>bodyCfg</b>: {
+ tag: 'center',
+ cls: 'x-panel-body', // Default class not applied if Custom element specified
+ html: 'Message'
+ },
+ footerCfg: {
+ tag: 'h2',
+ cls: 'x-panel-footer', // same as the Default class
+ html: 'footer html'
+ },
+ footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
+ footerStyle: 'background-color:red' // see {@link #bodyStyle}
+});
+ * </code></pre>
+ * <p>The example above also explicitly creates a <code>{@link #footer}</code> with custom markup and
+ * styling applied.</p>
+ */
+ /**
+ * @cfg {Object} headerCfg
+ * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
+ * of this Panel's {@link #header} Element. See <code>{@link #bodyCfg}</code> also.</p>
+ */
+ /**
+ * @cfg {Object} bwrapCfg
+ * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
+ * of this Panel's {@link #bwrap} Element. See <code>{@link #bodyCfg}</code> also.</p>
+ */
+ /**
+ * @cfg {Object} tbarCfg
+ * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
+ * of this Panel's {@link #tbar} Element. See <code>{@link #bodyCfg}</code> also.</p>
+ */
+ /**
+ * @cfg {Object} bbarCfg
+ * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
+ * of this Panel's {@link #bbar} Element. See <code>{@link #bodyCfg}</code> also.</p>
+ */
+ /**
+ * @cfg {Object} footerCfg
+ * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
+ * of this Panel's {@link #footer} Element. See <code>{@link #bodyCfg}</code> also.</p>
+ */
+ /**
+ * @cfg {Boolean} closable
+ * Panels themselves do not directly support being closed, but some Panel subclasses do (like
+ * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}. Specify <code>true</code>
+ * to enable closing in such situations. Defaults to <code>false</code>.
+ */
+ /**
+ * The Panel's footer {@link Ext.Element Element}. Read-only.
+ * <p>This Element is used to house the Panel's <code>{@link #buttons}</code> or <code>{@link #fbar}</code>.</p>
+ * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
+ * @type Ext.Element
+ * @property footer
+ */
+ /**
+ * @cfg {Mixed} applyTo
+ * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
+ * the document that specifies some panel-specific structural markup. When <code>applyTo</code> is used,
+ * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
+ * will automatically create those components from that markup. Any required components not specified in the
+ * markup will be autogenerated if necessary.</p>
+ * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
+ * <ul><li>baseCls + '-header'</li>
+ * <li>baseCls + '-header-text'</li>
+ * <li>baseCls + '-bwrap'</li>
+ * <li>baseCls + '-tbar'</li>
+ * <li>baseCls + '-body'</li>
+ * <li>baseCls + '-bbar'</li>
+ * <li>baseCls + '-footer'</li></ul>
+ * <p>Using this config, a call to render() is not required. If applyTo is specified, any value passed for
+ * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
+ * panel's container.</p>
+ */
+ /**
+ * @cfg {Object/Array} tbar
+ * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
+ * buttons/button configs to be added to the toolbar. Note that this is not available as a property after render.
+ * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
+ * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
+ * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
+ * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
+ * submission parameters are collected from the DOM tree.</p>
+ */
+ /**
+ * @cfg {Object/Array} bbar
+ * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
+ * buttons/button configs to be added to the toolbar. Note that this is not available as a property after render.
+ * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
+ * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
+ * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
+ * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
+ * submission parameters are collected from the DOM tree.</p>
+ */
+ /** @cfg {Object/Array} fbar
+ * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
+ * {@link Ext.Button Button}s/{@link Ext.Button Button} configs, describing a {@link Ext.Toolbar Toolbar} to be rendered into this Panel's footer element.</p>
+ * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
+ * <p>If <code>{@link #buttons}</code> are specified, they will supersede the <code>fbar</code> configuration property.</p>
+ * The Panel's <code>{@link #buttonAlign}</code> configuration affects the layout of these items, for example:
+ * <pre><code>
+var w = new Ext.Window({
+ height: 250,
+ width: 500,
+ bbar: new Ext.Toolbar({
+ items: [{
+ text: 'bbar Left'
+ },'->',{
+ text: 'bbar Right'
+ }]
+ }),
+ {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
+ // to control the alignment of fbar items
+ fbar: [{
+ text: 'fbar Left'
+ },'->',{
+ text: 'fbar Right'
+ }]
+}).show();
+ * </code></pre>
+ * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
+ * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
+ * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
+ * submission parameters are collected from the DOM tree.</p>
+ */
+ /**
+ * @cfg {Boolean} header
+ * <code>true</code> to create the Panel's header element explicitly, <code>false</code> to skip creating
+ * it. If a <code>{@link #title}</code> is set the header will be created automatically, otherwise it will not.
+ * If a <code>{@link #title}</code> is set but <code>header</code> is explicitly set to <code>false</code>, the header
+ * will not be rendered.
+ */
+ /**
+ * @cfg {Boolean} footer
+ * <code>true</code> to create the footer element explicitly, false to skip creating it. The footer
+ * will be created automatically if <code>{@link #buttons}</code> or a <code>{@link #fbar}</code> have
+ * been configured. See <code>{@link #bodyCfg}</code> for an example.
+ */
+ /**
+ * @cfg {String} title
+ * The title text to be used as innerHTML (html tags are accepted) to display in the panel
+ * <code>{@link #header}</code> (defaults to ''). When a <code>title</code> is specified the
+ * <code>{@link #header}</code> element will automatically be created and displayed unless
+ * {@link #header} is explicitly set to <code>false</code>. If you do not want to specify a
+ * <code>title</code> at config time, but you may want one later, you must either specify a non-empty
+ * <code>title</code> (a blank space ' ' will do) or <code>header:true</code> so that the container
+ * element will get created.
+ */
+ /**
+ * @cfg {Array} buttons
+ * <code>buttons</code> will be used as <code>{@link Ext.Container#items items}</code> for the toolbar in
+ * the footer (<code>{@link #fbar}</code>). Typically the value of this configuration property will be
+ * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
+ * If an item is configured with <code>minWidth</code> or the Panel is configured with <code>minButtonWidth</code>,
+ * that width will be applied to the item.
+ */
+ /**
+ * @cfg {Object/String/Function} autoLoad
+ * A valid url spec according to the Updater {@link Ext.Updater#update} method.
+ * If autoLoad is not null, the panel will attempt to load its contents
+ * immediately upon render.<p>
+ * The URL will become the default URL for this panel's {@link #body} element,
+ * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
+ */
+ /**
+ * @cfg {Boolean} frame
+ * <code>false</code> by default to render with plain 1px square borders. <code>true</code> to render with
+ * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
+ * <p>The template generated for each condition is depicted below:</p><pre><code>
+ *
+// frame = false
+<div id="developer-specified-id-goes-here" class="x-panel">
+
+ <div class="x-panel-header"><span class="x-panel-header-text">Title: (frame:false)</span></div>
+
+ <div class="x-panel-bwrap">
+ <div class="x-panel-body"><p>html value goes here</p></div>
+ </div>
+</div>
+
+// frame = true (create 9 elements)
+<div id="developer-specified-id-goes-here" class="x-panel">
+ <div class="x-panel-tl"><div class="x-panel-tr"><div class="x-panel-tc">
+ <div class="x-panel-header"><span class="x-panel-header-text">Title: (frame:true)</span></div>
+ </div></div></div>
+
+ <div class="x-panel-bwrap">
+ <div class="x-panel-ml"><div class="x-panel-mr"><div class="x-panel-mc">
+ <div class="x-panel-body"><p>html value goes here</p></div>
+ </div></div></div>
+
+ <div class="x-panel-bl"><div class="x-panel-br"><div class="x-panel-bc"/>
+ </div></div></div>
+</div>
+ * </code></pre>
+ */
+ /**
+ * @cfg {Boolean} border
+ * True to display the borders of the panel's body element, false to hide them (defaults to true). By default,
+ * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
+ */
+ /**
+ * @cfg {Boolean} bodyBorder
+ * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
+ * This only applies when {@link #border} == true. If border == true and bodyBorder == false, the border will display
+ * as a 1px wide inset border, giving the entire body element an inset appearance.
+ */
+ /**
+ * @cfg {String/Object/Function} bodyCssClass
+ * Additional css class selector to be applied to the {@link #body} element in the format expected by
+ * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
+ */
+ /**
+ * @cfg {String/Object/Function} bodyStyle
+ * Custom CSS styles to be applied to the {@link #body} element in the format expected by
+ * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
+ */
+ /**
+ * @cfg {String} iconCls
+ * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
+ * <p>An example of specifying a custom icon class would be something like:
+ * </p><pre><code>
+// specify the property in the config for the class:
+ ...
+ iconCls: 'my-icon'
+
+// css class that specifies background image to be used as the icon image:
+.my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
+</code></pre>
+ */
+ /**
+ * @cfg {Boolean} collapsible
+ * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
+ * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
+ */
+ /**
+ * @cfg {Array} tools
+ * An array of tool button configs to be added to the header tool area. When rendered, each tool is
+ * stored as an {@link Ext.Element Element} referenced by a public property called <code><b></b>tools.<i><tool-type></i></code>
+ * <p>Each tool config may contain the following properties:
+ * <div class="mdetail-params"><ul>
+ * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
+ * of tool to create. By default, this assigns a CSS class of the form <code>x-tool-<i><tool-type></i></code> to the
+ * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
+ * The developer may implement custom tools by supplying alternate CSS rules and background images:
+ * <ul>
+ * <div class="x-tool x-tool-toggle" style="float:left; margin-right:5;"> </div><div><code> toggle</code> (Created by default when {@link #collapsible} is <code>true</code>)</div>
+ * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><code> close</code></div>
+ * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><code> minimize</code></div>
+ * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><code> maximize</code></div>
+ * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><code> restore</code></div>
+ * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><code> gear</code></div>
+ * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><code> pin</code></div>
+ * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><code> unpin</code></div>
+ * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><code> right</code></div>
+ * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><code> left</code></div>
+ * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><code> up</code></div>
+ * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><code> down</code></div>
+ * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><code> refresh</code></div>
+ * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><code> minus</code></div>
+ * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><code> plus</code></div>
+ * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><code> help</code></div>
+ * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><code> search</code></div>
+ * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><code> save</code></div>
+ * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><code> print</code></div>
+ * </ul></div></li>
+ * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
+ * call when clicked. Arguments passed are:<ul>
+ * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
+ * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
+ * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
+ * <li><b>tc</b> : Object<div class="sub-desc">The tool configuration object</div></li>
+ * </ul></div></li>
+ * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
+ * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
+ * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
+ * a config argument to {@link Ext.QuickTip#register}</div></li>
+ * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
+ * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
+ * event listeners in the format of an argument to {@link #addListener}</div></li>
+ * </ul></div>
+ * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
+ * tools only provide the visual button. Any required functionality must be provided by adding
+ * handlers that implement the necessary behavior.</p>
+ * <p>Example usage:</p>
+ * <pre><code>
+tools:[{
+ id:'refresh',
+ qtip: 'Refresh form Data',
+ // hidden:true,
+ handler: function(event, toolEl, panel){
+ // refresh logic
+ }
+},
+{
+ id:'help',
+ qtip: 'Get Help',
+ handler: function(event, toolEl, panel){
+ // whatever
+ }
+}]
+</code></pre>
+ * <p>For the custom id of <code>'help'</code> define two relevant css classes with a link to
+ * a 15x15 image:</p>
+ * <pre><code>
+.x-tool-help {background-image: url(images/help.png);}
+.x-tool-help-over {background-image: url(images/help_over.png);}
+// if using an image sprite:
+.x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
+.x-tool-help-over {background-position:-15px 0;}
+</code></pre>