/*!
- * Ext JS Library 3.1.1
- * Copyright(c) 2006-2010 Ext JS, LLC
+ * Ext JS Library 3.2.1
+ * Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
* The initial set of data to apply to the <code>{@link #tpl}</code> to
* update the content area of the Component.
*/
+
+ /**
+ * @cfg {Array} bubbleEvents
+ * <p>An array of events that, when fired, should be bubbled to any parent container.
+ * See {@link Ext.util.Observable#enableBubble}.
+ * Defaults to <tt>[]</tt>.
+ */
+ bubbleEvents: [],
// private
}
</code></pre>
*/
- initComponent : Ext.emptyFn,
+ initComponent : function(){
+ /*
+ * this is double processing, however it allows people to be able to do
+ * Ext.apply(this, {
+ * listeners: {
+ * //here
+ * }
+ * });
+ * MyClass.superclass.initComponent.call(this);
+ */
+ if(this.listeners){
+ this.on(this.listeners);
+ delete this.listeners;
+ }
+ this.enableBubble(this.bubbleEvents);
+ },
/**
* <p>Render this Component into the passed HTML element.</p>
this.container.remove();
}
}
+ // Stop any buffered tasks
+ if(this.focusTask && this.focusTask.cancel){
+ this.focusTask.cancel();
+ }
this.onDestroy();
Ext.ComponentMgr.unregister(this);
this.fireEvent('destroy', this);
*/
focus : function(selectText, delay){
if(delay){
- this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]);
+ this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]);
+ this.focusTask.delay(Ext.isNumber(delay) ? delay : 10);
return;
}
- if(this.rendered){
+ if(this.rendered && !this.isDestroyed){
this.el.focus();
if(selectText === true){
this.el.dom.select();
}
});
-Ext.reg('component', Ext.Component);/**\r
- * @class Ext.Action\r
- * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it\r
- * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI\r
- * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}\r
- * and {@link Ext.menu.Menu} components).</p>\r
- * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support\r
- * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),\r
- * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>\r
- * Example usage:<br>\r
- * <pre><code>\r
-// Define the shared action. Each component below will have the same\r
-// display text and icon, and will display the same message on click.\r
-var action = new Ext.Action({\r
- {@link #text}: 'Do something',\r
- {@link #handler}: function(){\r
- Ext.Msg.alert('Click', 'You did something.');\r
- },\r
- {@link #iconCls}: 'do-something',\r
- {@link #itemId}: 'myAction'\r
-});\r
-\r
-var panel = new Ext.Panel({\r
- title: 'Actions',\r
- width: 500,\r
- height: 300,\r
- tbar: [\r
- // Add the action directly to a toolbar as a menu button\r
- action,\r
- {\r
- text: 'Action Menu',\r
- // Add the action to a menu as a text item\r
- menu: [action]\r
- }\r
- ],\r
- items: [\r
- // Add the action to the panel body as a standard button\r
- new Ext.Button(action)\r
- ],\r
- renderTo: Ext.getBody()\r
-});\r
-\r
-// Change the text for all components using the action\r
-action.setText('Something else');\r
-\r
-// Reference an action through a container using the itemId\r
-var btn = panel.getComponent('myAction');\r
-var aRef = btn.baseAction;\r
-aRef.setText('New text');\r
-</code></pre>\r
- * @constructor\r
- * @param {Object} config The configuration options\r
- */\r
-Ext.Action = Ext.extend(Object, {\r
- /**\r
- * @cfg {String} text The text to set for all components using this action (defaults to '').\r
- */\r
- /**\r
- * @cfg {String} iconCls\r
- * The CSS class selector that specifies a background image to be used as the header icon for\r
- * all components using this action (defaults to '').\r
- * <p>An example of specifying a custom icon class would be something like:\r
- * </p><pre><code>\r
-// specify the property in the config for the class:\r
- ...\r
- iconCls: 'do-something'\r
-\r
-// css class that specifies background image to be used as the icon image:\r
-.do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }\r
-</code></pre>\r
- */\r
- /**\r
- * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).\r
- */\r
- /**\r
- * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).\r
- */\r
- /**\r
- * @cfg {Function} handler The function that will be invoked by each component tied to this action\r
- * when the component's primary event is triggered (defaults to undefined).\r
- */\r
- /**\r
- * @cfg {String} itemId\r
- * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.\r
- */\r
- /**\r
- * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the\r
- * <code>{@link #handler}</code> is executed. Defaults to this Button.\r
- */\r
-\r
- constructor : function(config){\r
- this.initialConfig = config;\r
- this.itemId = config.itemId = (config.itemId || config.id || Ext.id());\r
- this.items = [];\r
- },\r
- \r
- // private\r
- isAction : true,\r
-\r
- /**\r
- * Sets the text to be displayed by all components using this action.\r
- * @param {String} text The text to display\r
- */\r
- setText : function(text){\r
- this.initialConfig.text = text;\r
- this.callEach('setText', [text]);\r
- },\r
-\r
- /**\r
- * Gets the text currently displayed by all components using this action.\r
- */\r
- getText : function(){\r
- return this.initialConfig.text;\r
- },\r
-\r
- /**\r
- * Sets the icon CSS class for all components using this action. The class should supply\r
- * a background image that will be used as the icon image.\r
- * @param {String} cls The CSS class supplying the icon image\r
- */\r
- setIconClass : function(cls){\r
- this.initialConfig.iconCls = cls;\r
- this.callEach('setIconClass', [cls]);\r
- },\r
-\r
- /**\r
- * Gets the icon CSS class currently used by all components using this action.\r
- */\r
- getIconClass : function(){\r
- return this.initialConfig.iconCls;\r
- },\r
-\r
- /**\r
- * Sets the disabled state of all components using this action. Shortcut method\r
- * for {@link #enable} and {@link #disable}.\r
- * @param {Boolean} disabled True to disable the component, false to enable it\r
- */\r
- setDisabled : function(v){\r
- this.initialConfig.disabled = v;\r
- this.callEach('setDisabled', [v]);\r
- },\r
-\r
- /**\r
- * Enables all components using this action.\r
- */\r
- enable : function(){\r
- this.setDisabled(false);\r
- },\r
-\r
- /**\r
- * Disables all components using this action.\r
- */\r
- disable : function(){\r
- this.setDisabled(true);\r
- },\r
-\r
- /**\r
- * Returns true if the components using this action are currently disabled, else returns false. \r
- */\r
- isDisabled : function(){\r
- return this.initialConfig.disabled;\r
- },\r
-\r
- /**\r
- * Sets the hidden state of all components using this action. Shortcut method\r
- * for <code>{@link #hide}</code> and <code>{@link #show}</code>.\r
- * @param {Boolean} hidden True to hide the component, false to show it\r
- */\r
- setHidden : function(v){\r
- this.initialConfig.hidden = v;\r
- this.callEach('setVisible', [!v]);\r
- },\r
-\r
- /**\r
- * Shows all components using this action.\r
- */\r
- show : function(){\r
- this.setHidden(false);\r
- },\r
-\r
- /**\r
- * Hides all components using this action.\r
- */\r
- hide : function(){\r
- this.setHidden(true);\r
- },\r
-\r
- /**\r
- * Returns true if the components using this action are currently hidden, else returns false. \r
- */\r
- isHidden : function(){\r
- return this.initialConfig.hidden;\r
- },\r
-\r
- /**\r
- * Sets the function that will be called by each Component using this action when its primary event is triggered.\r
- * @param {Function} fn The function that will be invoked by the action's components. The function\r
- * will be called with no arguments.\r
- * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.\r
- */\r
- setHandler : function(fn, scope){\r
- this.initialConfig.handler = fn;\r
- this.initialConfig.scope = scope;\r
- this.callEach('setHandler', [fn, scope]);\r
- },\r
-\r
- /**\r
- * Executes the specified function once for each Component currently tied to this action. The function passed\r
- * in should accept a single argument that will be an object that supports the basic Action config/method interface.\r
- * @param {Function} fn The function to execute for each component\r
- * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component.\r
- */\r
- each : function(fn, scope){\r
- Ext.each(this.items, fn, scope);\r
- },\r
-\r
- // private\r
- callEach : function(fnName, args){\r
- var cs = this.items;\r
- for(var i = 0, len = cs.length; i < len; i++){\r
- cs[i][fnName].apply(cs[i], args);\r
- }\r
- },\r
-\r
- // private\r
- addComponent : function(comp){\r
- this.items.push(comp);\r
- comp.on('destroy', this.removeComponent, this);\r
- },\r
-\r
- // private\r
- removeComponent : function(comp){\r
- this.items.remove(comp);\r
- },\r
-\r
- /**\r
- * Executes this action manually using the handler function specified in the original config object\r
- * or the handler function set with <code>{@link #setHandler}</code>. Any arguments passed to this\r
- * function will be passed on to the handler function.\r
- * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function\r
- * @param {Mixed} arg2 (optional)\r
- * @param {Mixed} etc... (optional)\r
- */\r
- execute : function(){\r
- this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);\r
- }\r
-});\r
+Ext.reg('component', Ext.Component);/**
+ * @class Ext.Action
+ * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it
+ * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI
+ * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}
+ * and {@link Ext.menu.Menu} components).</p>
+ * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support
+ * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),
+ * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>
+ * Example usage:<br>
+ * <pre><code>
+// Define the shared action. Each component below will have the same
+// display text and icon, and will display the same message on click.
+var action = new Ext.Action({
+ {@link #text}: 'Do something',
+ {@link #handler}: function(){
+ Ext.Msg.alert('Click', 'You did something.');
+ },
+ {@link #iconCls}: 'do-something',
+ {@link #itemId}: 'myAction'
+});
+
+var panel = new Ext.Panel({
+ title: 'Actions',
+ width: 500,
+ height: 300,
+ tbar: [
+ // Add the action directly to a toolbar as a menu button
+ action,
+ {
+ text: 'Action Menu',
+ // Add the action to a menu as a text item
+ menu: [action]
+ }
+ ],
+ items: [
+ // Add the action to the panel body as a standard button
+ new Ext.Button(action)
+ ],
+ renderTo: Ext.getBody()
+});
+
+// Change the text for all components using the action
+action.setText('Something else');
+
+// Reference an action through a container using the itemId
+var btn = panel.getComponent('myAction');
+var aRef = btn.baseAction;
+aRef.setText('New text');
+</code></pre>
+ * @constructor
+ * @param {Object} config The configuration options
+ */
+Ext.Action = Ext.extend(Object, {
+ /**
+ * @cfg {String} text The text to set for all components using this action (defaults to '').
+ */
+ /**
+ * @cfg {String} iconCls
+ * The CSS class selector that specifies a background image to be used as the header icon for
+ * all components using this action (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: 'do-something'
+
+// css class that specifies background image to be used as the icon image:
+.do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
+</code></pre>
+ */
+ /**
+ * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).
+ */
+ /**
+ * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).
+ */
+ /**
+ * @cfg {Function} handler The function that will be invoked by each component tied to this action
+ * when the component's primary event is triggered (defaults to undefined).
+ */
+ /**
+ * @cfg {String} itemId
+ * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.
+ */
+ /**
+ * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
+ * <code>{@link #handler}</code> is executed. Defaults to this Button.
+ */
+
+ constructor : function(config){
+ this.initialConfig = config;
+ this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
+ this.items = [];
+ },
+
+ // private
+ isAction : true,
+
+ /**
+ * Sets the text to be displayed by all components using this action.
+ * @param {String} text The text to display
+ */
+ setText : function(text){
+ this.initialConfig.text = text;
+ this.callEach('setText', [text]);
+ },
+
+ /**
+ * Gets the text currently displayed by all components using this action.
+ */
+ getText : function(){
+ return this.initialConfig.text;
+ },
+
+ /**
+ * Sets the icon CSS class for all components using this action. The class should supply
+ * a background image that will be used as the icon image.
+ * @param {String} cls The CSS class supplying the icon image
+ */
+ setIconClass : function(cls){
+ this.initialConfig.iconCls = cls;
+ this.callEach('setIconClass', [cls]);
+ },
+
+ /**
+ * Gets the icon CSS class currently used by all components using this action.
+ */
+ getIconClass : function(){
+ return this.initialConfig.iconCls;
+ },
+
+ /**
+ * Sets the disabled state of all components using this action. Shortcut method
+ * for {@link #enable} and {@link #disable}.
+ * @param {Boolean} disabled True to disable the component, false to enable it
+ */
+ setDisabled : function(v){
+ this.initialConfig.disabled = v;
+ this.callEach('setDisabled', [v]);
+ },
+
+ /**
+ * Enables all components using this action.
+ */
+ enable : function(){
+ this.setDisabled(false);
+ },
+
+ /**
+ * Disables all components using this action.
+ */
+ disable : function(){
+ this.setDisabled(true);
+ },
+
+ /**
+ * Returns true if the components using this action are currently disabled, else returns false.
+ */
+ isDisabled : function(){
+ return this.initialConfig.disabled;
+ },
+
+ /**
+ * Sets the hidden state of all components using this action. Shortcut method
+ * for <code>{@link #hide}</code> and <code>{@link #show}</code>.
+ * @param {Boolean} hidden True to hide the component, false to show it
+ */
+ setHidden : function(v){
+ this.initialConfig.hidden = v;
+ this.callEach('setVisible', [!v]);
+ },
+
+ /**
+ * Shows all components using this action.
+ */
+ show : function(){
+ this.setHidden(false);
+ },
+
+ /**
+ * Hides all components using this action.
+ */
+ hide : function(){
+ this.setHidden(true);
+ },
+
+ /**
+ * Returns true if the components using this action are currently hidden, else returns false.
+ */
+ isHidden : function(){
+ return this.initialConfig.hidden;
+ },
+
+ /**
+ * Sets the function that will be called by each Component using this action when its primary event is triggered.
+ * @param {Function} fn The function that will be invoked by the action's components. The function
+ * will be called with no arguments.
+ * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.
+ */
+ setHandler : function(fn, scope){
+ this.initialConfig.handler = fn;
+ this.initialConfig.scope = scope;
+ this.callEach('setHandler', [fn, scope]);
+ },
+
+ /**
+ * Executes the specified function once for each Component currently tied to this action. The function passed
+ * in should accept a single argument that will be an object that supports the basic Action config/method interface.
+ * @param {Function} fn The function to execute for each component
+ * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component.
+ */
+ each : function(fn, scope){
+ Ext.each(this.items, fn, scope);
+ },
+
+ // private
+ callEach : function(fnName, args){
+ var cs = this.items;
+ for(var i = 0, len = cs.length; i < len; i++){
+ cs[i][fnName].apply(cs[i], args);
+ }
+ },
+
+ // private
+ addComponent : function(comp){
+ this.items.push(comp);
+ comp.on('destroy', this.removeComponent, this);
+ },
+
+ // private
+ removeComponent : function(comp){
+ this.items.remove(comp);
+ },
+
+ /**
+ * Executes this action manually using the handler function specified in the original config object
+ * or the handler function set with <code>{@link #setHandler}</code>. Any arguments passed to this
+ * function will be passed on to the handler function.
+ * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function
+ * @param {Mixed} arg2 (optional)
+ * @param {Mixed} etc... (optional)
+ */
+ execute : function(){
+ this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);
+ }
+});
/**
* @class Ext.Layer
* @extends Ext.Element
// this code can execute repeatedly in milliseconds (i.e. during a drag) so
// code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
sync : function(doShow){
- var sw = this.shadow;
- if(!this.updating && this.isVisible() && (sw || this.useShim)){
- var sh = this.getShim();
-
- var w = this.getWidth(),
- h = this.getHeight();
-
- var l = this.getLeft(true),
+ var shadow = this.shadow;
+ if(!this.updating && this.isVisible() && (shadow || this.useShim)){
+ var shim = this.getShim(),
+ w = this.getWidth(),
+ h = this.getHeight(),
+ l = this.getLeft(true),
t = this.getTop(true);
- if(sw && !this.shadowDisabled){
- if(doShow && !sw.isVisible()){
- sw.show(this);
+ if(shadow && !this.shadowDisabled){
+ if(doShow && !shadow.isVisible()){
+ shadow.show(this);
}else{
- sw.realign(l, t, w, h);
+ shadow.realign(l, t, w, h);
}
- if(sh){
+ if(shim){
if(doShow){
- sh.show();
+ shim.show();
}
// fit the shim behind the shadow, so it is shimmed too
- var a = sw.adjusts, s = sh.dom.style;
- s.left = (Math.min(l, l+a.l))+'px';
- s.top = (Math.min(t, t+a.t))+'px';
- s.width = (w+a.w)+'px';
- s.height = (h+a.h)+'px';
+ var shadowAdj = shadow.el.getXY(), shimStyle = shim.dom.style,
+ shadowSize = shadow.el.getSize();
+ shimStyle.left = (shadowAdj[0])+'px';
+ shimStyle.top = (shadowAdj[1])+'px';
+ shimStyle.width = (shadowSize.width)+'px';
+ shimStyle.height = (shadowSize.height)+'px';
}
- }else if(sh){
+ }else if(shim){
if(doShow){
- sh.show();
+ shim.show();
}
- sh.setSize(w, h);
- sh.setLeftTop(l, t);
+ shim.setSize(w, h);
+ shim.setLeftTop(l, t);
}
-
}
},
// support for standard size objects
if(typeof w == 'object'){
- h = w.height, w = w.width;
+ h = w.height;
+ w = w.width;
}
if (Ext.isDefined(w) && Ext.isDefined(this.boxMinWidth) && (w < this.boxMinWidth)) {
w = this.boxMinWidth;
}
// not rendered
if(!this.boxReady){
- this.width = w, this.height = h;
+ this.width = w;
+ this.height = h;
return this;
}
/**
* Sets the width of the component. This method fires the {@link #resize} event.
- * @param {Number} width The new width to setThis may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
+ * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
+ * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
* <li>A String used to set the CSS width style.</li>
* </ul></div>
* @return {Ext.BoxComponent} this
/**
* Sets the height of the component. This method fires the {@link #resize} event.
- * @param {Number} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
+ * @param {Mixed} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
+ * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
* <li>A String used to set the CSS height style.</li>
* <li><i>undefined</i> to leave the height unchanged.</li>
* </ul></div>
Ext.Spacer = Ext.extend(Ext.BoxComponent, {
autoEl:'div'
});
-Ext.reg('spacer', Ext.Spacer);/**\r
- * @class Ext.SplitBar\r
- * @extends Ext.util.Observable\r
- * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).\r
- * <br><br>\r
- * Usage:\r
- * <pre><code>\r
-var split = new Ext.SplitBar("elementToDrag", "elementToSize",\r
- Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);\r
-split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));\r
-split.minSize = 100;\r
-split.maxSize = 600;\r
-split.animate = true;\r
-split.on('moved', splitterMoved);\r
-</code></pre>\r
- * @constructor\r
- * Create a new SplitBar\r
- * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.\r
- * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged\r
- * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
- * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or\r
- Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial\r
- position of the SplitBar).\r
- */\r
-Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){\r
-\r
- /** @private */\r
- this.el = Ext.get(dragElement, true);\r
- this.el.dom.unselectable = "on";\r
- /** @private */\r
- this.resizingEl = Ext.get(resizingElement, true);\r
-\r
- /**\r
- * @private\r
- * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
- * Note: If this is changed after creating the SplitBar, the placement property must be manually updated\r
- * @type Number\r
- */\r
- this.orientation = orientation || Ext.SplitBar.HORIZONTAL;\r
-\r
- /**\r
- * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.\r
- * @type Number\r
- * @property tickSize\r
- */\r
- /**\r
- * The minimum size of the resizing element. (Defaults to 0)\r
- * @type Number\r
- */\r
- this.minSize = 0;\r
-\r
- /**\r
- * The maximum size of the resizing element. (Defaults to 2000)\r
- * @type Number\r
- */\r
- this.maxSize = 2000;\r
-\r
- /**\r
- * Whether to animate the transition to the new size\r
- * @type Boolean\r
- */\r
- this.animate = false;\r
-\r
- /**\r
- * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.\r
- * @type Boolean\r
- */\r
- this.useShim = false;\r
-\r
- /** @private */\r
- this.shim = null;\r
-\r
- if(!existingProxy){\r
- /** @private */\r
- this.proxy = Ext.SplitBar.createProxy(this.orientation);\r
- }else{\r
- this.proxy = Ext.get(existingProxy).dom;\r
- }\r
- /** @private */\r
- this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});\r
-\r
- /** @private */\r
- this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);\r
-\r
- /** @private */\r
- this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);\r
-\r
- /** @private */\r
- this.dragSpecs = {};\r
-\r
- /**\r
- * @private The adapter to use to positon and resize elements\r
- */\r
- this.adapter = new Ext.SplitBar.BasicLayoutAdapter();\r
- this.adapter.init(this);\r
-\r
- if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
- /** @private */\r
- this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);\r
- this.el.addClass("x-splitbar-h");\r
- }else{\r
- /** @private */\r
- this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);\r
- this.el.addClass("x-splitbar-v");\r
- }\r
-\r
- this.addEvents(\r
- /**\r
- * @event resize\r
- * Fires when the splitter is moved (alias for {@link #moved})\r
- * @param {Ext.SplitBar} this\r
- * @param {Number} newSize the new width or height\r
- */\r
- "resize",\r
- /**\r
- * @event moved\r
- * Fires when the splitter is moved\r
- * @param {Ext.SplitBar} this\r
- * @param {Number} newSize the new width or height\r
- */\r
- "moved",\r
- /**\r
- * @event beforeresize\r
- * Fires before the splitter is dragged\r
- * @param {Ext.SplitBar} this\r
- */\r
- "beforeresize",\r
-\r
- "beforeapply"\r
- );\r
-\r
- Ext.SplitBar.superclass.constructor.call(this);\r
-};\r
-\r
-Ext.extend(Ext.SplitBar, Ext.util.Observable, {\r
- onStartProxyDrag : function(x, y){\r
- this.fireEvent("beforeresize", this);\r
- this.overlay = Ext.DomHelper.append(document.body, {cls: "x-drag-overlay", html: " "}, true);\r
- this.overlay.unselectable();\r
- this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
- this.overlay.show();\r
- Ext.get(this.proxy).setDisplayed("block");\r
- var size = this.adapter.getElementSize(this);\r
- this.activeMinSize = this.getMinimumSize();\r
- this.activeMaxSize = this.getMaximumSize();\r
- var c1 = size - this.activeMinSize;\r
- var c2 = Math.max(this.activeMaxSize - size, 0);\r
- if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
- this.dd.resetConstraints();\r
- this.dd.setXConstraint(\r
- this.placement == Ext.SplitBar.LEFT ? c1 : c2,\r
- this.placement == Ext.SplitBar.LEFT ? c2 : c1,\r
- this.tickSize\r
- );\r
- this.dd.setYConstraint(0, 0);\r
- }else{\r
- this.dd.resetConstraints();\r
- this.dd.setXConstraint(0, 0);\r
- this.dd.setYConstraint(\r
- this.placement == Ext.SplitBar.TOP ? c1 : c2,\r
- this.placement == Ext.SplitBar.TOP ? c2 : c1,\r
- this.tickSize\r
- );\r
- }\r
- this.dragSpecs.startSize = size;\r
- this.dragSpecs.startPoint = [x, y];\r
- Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);\r
- },\r
-\r
- /**\r
- * @private Called after the drag operation by the DDProxy\r
- */\r
- onEndProxyDrag : function(e){\r
- Ext.get(this.proxy).setDisplayed(false);\r
- var endPoint = Ext.lib.Event.getXY(e);\r
- if(this.overlay){\r
- Ext.destroy(this.overlay);\r
- delete this.overlay;\r
- }\r
- var newSize;\r
- if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
- newSize = this.dragSpecs.startSize +\r
- (this.placement == Ext.SplitBar.LEFT ?\r
- endPoint[0] - this.dragSpecs.startPoint[0] :\r
- this.dragSpecs.startPoint[0] - endPoint[0]\r
- );\r
- }else{\r
- newSize = this.dragSpecs.startSize +\r
- (this.placement == Ext.SplitBar.TOP ?\r
- endPoint[1] - this.dragSpecs.startPoint[1] :\r
- this.dragSpecs.startPoint[1] - endPoint[1]\r
- );\r
- }\r
- newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);\r
- if(newSize != this.dragSpecs.startSize){\r
- if(this.fireEvent('beforeapply', this, newSize) !== false){\r
- this.adapter.setElementSize(this, newSize);\r
- this.fireEvent("moved", this, newSize);\r
- this.fireEvent("resize", this, newSize);\r
- }\r
- }\r
- },\r
-\r
- /**\r
- * Get the adapter this SplitBar uses\r
- * @return The adapter object\r
- */\r
- getAdapter : function(){\r
- return this.adapter;\r
- },\r
-\r
- /**\r
- * Set the adapter this SplitBar uses\r
- * @param {Object} adapter A SplitBar adapter object\r
- */\r
- setAdapter : function(adapter){\r
- this.adapter = adapter;\r
- this.adapter.init(this);\r
- },\r
-\r
- /**\r
- * Gets the minimum size for the resizing element\r
- * @return {Number} The minimum size\r
- */\r
- getMinimumSize : function(){\r
- return this.minSize;\r
- },\r
-\r
- /**\r
- * Sets the minimum size for the resizing element\r
- * @param {Number} minSize The minimum size\r
- */\r
- setMinimumSize : function(minSize){\r
- this.minSize = minSize;\r
- },\r
-\r
- /**\r
- * Gets the maximum size for the resizing element\r
- * @return {Number} The maximum size\r
- */\r
- getMaximumSize : function(){\r
- return this.maxSize;\r
- },\r
-\r
- /**\r
- * Sets the maximum size for the resizing element\r
- * @param {Number} maxSize The maximum size\r
- */\r
- setMaximumSize : function(maxSize){\r
- this.maxSize = maxSize;\r
- },\r
-\r
- /**\r
- * Sets the initialize size for the resizing element\r
- * @param {Number} size The initial size\r
- */\r
- setCurrentSize : function(size){\r
- var oldAnimate = this.animate;\r
- this.animate = false;\r
- this.adapter.setElementSize(this, size);\r
- this.animate = oldAnimate;\r
- },\r
-\r
- /**\r
- * Destroy this splitbar.\r
- * @param {Boolean} removeEl True to remove the element\r
- */\r
- destroy : function(removeEl){\r
- Ext.destroy(this.shim, Ext.get(this.proxy));\r
- this.dd.unreg();\r
- if(removeEl){\r
- this.el.remove();\r
- }\r
- this.purgeListeners();\r
- }\r
-});\r
-\r
-/**\r
- * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.\r
- */\r
-Ext.SplitBar.createProxy = function(dir){\r
- var proxy = new Ext.Element(document.createElement("div"));\r
- document.body.appendChild(proxy.dom);\r
- proxy.unselectable();\r
- var cls = 'x-splitbar-proxy';\r
- proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));\r
- return proxy.dom;\r
-};\r
-\r
-/**\r
- * @class Ext.SplitBar.BasicLayoutAdapter\r
- * Default Adapter. It assumes the splitter and resizing element are not positioned\r
- * elements and only gets/sets the width of the element. Generally used for table based layouts.\r
- */\r
-Ext.SplitBar.BasicLayoutAdapter = function(){\r
-};\r
-\r
-Ext.SplitBar.BasicLayoutAdapter.prototype = {\r
- // do nothing for now\r
- init : function(s){\r
-\r
- },\r
- /**\r
- * Called before drag operations to get the current size of the resizing element.\r
- * @param {Ext.SplitBar} s The SplitBar using this adapter\r
- */\r
- getElementSize : function(s){\r
- if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
- return s.resizingEl.getWidth();\r
- }else{\r
- return s.resizingEl.getHeight();\r
- }\r
- },\r
-\r
- /**\r
- * Called after drag operations to set the size of the resizing element.\r
- * @param {Ext.SplitBar} s The SplitBar using this adapter\r
- * @param {Number} newSize The new size to set\r
- * @param {Function} onComplete A function to be invoked when resizing is complete\r
- */\r
- setElementSize : function(s, newSize, onComplete){\r
- if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
- if(!s.animate){\r
- s.resizingEl.setWidth(newSize);\r
- if(onComplete){\r
- onComplete(s, newSize);\r
- }\r
- }else{\r
- s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');\r
- }\r
- }else{\r
-\r
- if(!s.animate){\r
- s.resizingEl.setHeight(newSize);\r
- if(onComplete){\r
- onComplete(s, newSize);\r
- }\r
- }else{\r
- s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');\r
- }\r
- }\r
- }\r
-};\r
-\r
-/**\r
- *@class Ext.SplitBar.AbsoluteLayoutAdapter\r
- * @extends Ext.SplitBar.BasicLayoutAdapter\r
- * Adapter that moves the splitter element to align with the resized sizing element.\r
- * Used with an absolute positioned SplitBar.\r
- * @param {Mixed} container The container that wraps around the absolute positioned content. If it's\r
- * document.body, make sure you assign an id to the body element.\r
- */\r
-Ext.SplitBar.AbsoluteLayoutAdapter = function(container){\r
- this.basic = new Ext.SplitBar.BasicLayoutAdapter();\r
- this.container = Ext.get(container);\r
-};\r
-\r
-Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {\r
- init : function(s){\r
- this.basic.init(s);\r
- },\r
-\r
- getElementSize : function(s){\r
- return this.basic.getElementSize(s);\r
- },\r
-\r
- setElementSize : function(s, newSize, onComplete){\r
- this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));\r
- },\r
-\r
- moveSplitter : function(s){\r
- var yes = Ext.SplitBar;\r
- switch(s.placement){\r
- case yes.LEFT:\r
- s.el.setX(s.resizingEl.getRight());\r
- break;\r
- case yes.RIGHT:\r
- s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");\r
- break;\r
- case yes.TOP:\r
- s.el.setY(s.resizingEl.getBottom());\r
- break;\r
- case yes.BOTTOM:\r
- s.el.setY(s.resizingEl.getTop() - s.el.getHeight());\r
- break;\r
- }\r
- }\r
-};\r
-\r
-/**\r
- * Orientation constant - Create a vertical SplitBar\r
- * @static\r
- * @type Number\r
- */\r
-Ext.SplitBar.VERTICAL = 1;\r
-\r
-/**\r
- * Orientation constant - Create a horizontal SplitBar\r
- * @static\r
- * @type Number\r
- */\r
-Ext.SplitBar.HORIZONTAL = 2;\r
-\r
-/**\r
- * Placement constant - The resizing element is to the left of the splitter element\r
- * @static\r
- * @type Number\r
- */\r
-Ext.SplitBar.LEFT = 1;\r
-\r
-/**\r
- * Placement constant - The resizing element is to the right of the splitter element\r
- * @static\r
- * @type Number\r
- */\r
-Ext.SplitBar.RIGHT = 2;\r
-\r
-/**\r
- * Placement constant - The resizing element is positioned above the splitter element\r
- * @static\r
- * @type Number\r
- */\r
-Ext.SplitBar.TOP = 3;\r
-\r
-/**\r
- * Placement constant - The resizing element is positioned under splitter element\r
- * @static\r
- * @type Number\r
- */\r
-Ext.SplitBar.BOTTOM = 4;\r
-/**
- * @class Ext.Container
- * @extends Ext.BoxComponent
- * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
- * basic behavior of containing items, namely adding, inserting and removing items.</p>
- *
- * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
- * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
- * Container to be encapsulated by an HTML element to your specifications by using the
- * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option. This is a useful technique when creating
- * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
- * for example.</p>
- *
- * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
- * create one using the <b><code>'container'</code></b> xtype:<pre><code>
-// explicitly create a Container
-var embeddedColumns = new Ext.Container({
- autoEl: 'div', // This is the default
- layout: 'column',
- defaults: {
- // implicitly create Container by specifying xtype
- xtype: 'container',
- autoEl: 'div', // This is the default.
- layout: 'form',
- columnWidth: 0.5,
- style: {
- padding: '10px'
- }
+Ext.reg('spacer', Ext.Spacer);/**
+ * @class Ext.SplitBar
+ * @extends Ext.util.Observable
+ * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
+ * <br><br>
+ * Usage:
+ * <pre><code>
+var split = new Ext.SplitBar("elementToDrag", "elementToSize",
+ Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);
+split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));
+split.minSize = 100;
+split.maxSize = 600;
+split.animate = true;
+split.on('moved', splitterMoved);
+</code></pre>
+ * @constructor
+ * Create a new SplitBar
+ * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.
+ * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged
+ * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
+ * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or
+ Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
+ position of the SplitBar).
+ */
+Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
+
+ /** @private */
+ this.el = Ext.get(dragElement, true);
+ this.el.dom.unselectable = "on";
+ /** @private */
+ this.resizingEl = Ext.get(resizingElement, true);
+
+ /**
+ * @private
+ * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
+ * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
+ * @type Number
+ */
+ this.orientation = orientation || Ext.SplitBar.HORIZONTAL;
+
+ /**
+ * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.
+ * @type Number
+ * @property tickSize
+ */
+ /**
+ * The minimum size of the resizing element. (Defaults to 0)
+ * @type Number
+ */
+ this.minSize = 0;
+
+ /**
+ * The maximum size of the resizing element. (Defaults to 2000)
+ * @type Number
+ */
+ this.maxSize = 2000;
+
+ /**
+ * Whether to animate the transition to the new size
+ * @type Boolean
+ */
+ this.animate = false;
+
+ /**
+ * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
+ * @type Boolean
+ */
+ this.useShim = false;
+
+ /** @private */
+ this.shim = null;
+
+ if(!existingProxy){
+ /** @private */
+ this.proxy = Ext.SplitBar.createProxy(this.orientation);
+ }else{
+ this.proxy = Ext.get(existingProxy).dom;
+ }
+ /** @private */
+ this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
+
+ /** @private */
+ this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
+
+ /** @private */
+ this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
+
+ /** @private */
+ this.dragSpecs = {};
+
+ /**
+ * @private The adapter to use to positon and resize elements
+ */
+ this.adapter = new Ext.SplitBar.BasicLayoutAdapter();
+ this.adapter.init(this);
+
+ if(this.orientation == Ext.SplitBar.HORIZONTAL){
+ /** @private */
+ this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);
+ this.el.addClass("x-splitbar-h");
+ }else{
+ /** @private */
+ this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);
+ this.el.addClass("x-splitbar-v");
+ }
+
+ this.addEvents(
+ /**
+ * @event resize
+ * Fires when the splitter is moved (alias for {@link #moved})
+ * @param {Ext.SplitBar} this
+ * @param {Number} newSize the new width or height
+ */
+ "resize",
+ /**
+ * @event moved
+ * Fires when the splitter is moved
+ * @param {Ext.SplitBar} this
+ * @param {Number} newSize the new width or height
+ */
+ "moved",
+ /**
+ * @event beforeresize
+ * Fires before the splitter is dragged
+ * @param {Ext.SplitBar} this
+ */
+ "beforeresize",
+
+ "beforeapply"
+ );
+
+ Ext.SplitBar.superclass.constructor.call(this);
+};
+
+Ext.extend(Ext.SplitBar, Ext.util.Observable, {
+ onStartProxyDrag : function(x, y){
+ this.fireEvent("beforeresize", this);
+ this.overlay = Ext.DomHelper.append(document.body, {cls: "x-drag-overlay", html: " "}, true);
+ this.overlay.unselectable();
+ this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
+ this.overlay.show();
+ Ext.get(this.proxy).setDisplayed("block");
+ var size = this.adapter.getElementSize(this);
+ this.activeMinSize = this.getMinimumSize();
+ this.activeMaxSize = this.getMaximumSize();
+ var c1 = size - this.activeMinSize;
+ var c2 = Math.max(this.activeMaxSize - size, 0);
+ if(this.orientation == Ext.SplitBar.HORIZONTAL){
+ this.dd.resetConstraints();
+ this.dd.setXConstraint(
+ this.placement == Ext.SplitBar.LEFT ? c1 : c2,
+ this.placement == Ext.SplitBar.LEFT ? c2 : c1,
+ this.tickSize
+ );
+ this.dd.setYConstraint(0, 0);
+ }else{
+ this.dd.resetConstraints();
+ this.dd.setXConstraint(0, 0);
+ this.dd.setYConstraint(
+ this.placement == Ext.SplitBar.TOP ? c1 : c2,
+ this.placement == Ext.SplitBar.TOP ? c2 : c1,
+ this.tickSize
+ );
+ }
+ this.dragSpecs.startSize = size;
+ this.dragSpecs.startPoint = [x, y];
+ Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
},
-// The two items below will be Ext.Containers, each encapsulated by a <DIV> element.
- items: [{
- items: {
- xtype: 'datefield',
- name: 'startDate',
- fieldLabel: 'Start date'
+
+ /**
+ * @private Called after the drag operation by the DDProxy
+ */
+ onEndProxyDrag : function(e){
+ Ext.get(this.proxy).setDisplayed(false);
+ var endPoint = Ext.lib.Event.getXY(e);
+ if(this.overlay){
+ Ext.destroy(this.overlay);
+ delete this.overlay;
}
- }, {
- items: {
- xtype: 'datefield',
- name: 'endDate',
- fieldLabel: 'End date'
+ var newSize;
+ if(this.orientation == Ext.SplitBar.HORIZONTAL){
+ newSize = this.dragSpecs.startSize +
+ (this.placement == Ext.SplitBar.LEFT ?
+ endPoint[0] - this.dragSpecs.startPoint[0] :
+ this.dragSpecs.startPoint[0] - endPoint[0]
+ );
+ }else{
+ newSize = this.dragSpecs.startSize +
+ (this.placement == Ext.SplitBar.TOP ?
+ endPoint[1] - this.dragSpecs.startPoint[1] :
+ this.dragSpecs.startPoint[1] - endPoint[1]
+ );
}
- }]
-});</code></pre></p>
- *
- * <p><u><b>Layout</b></u></p>
- * <p>Container classes delegate the rendering of child Components to a layout
- * manager class which must be configured into the Container using the
- * <code><b>{@link #layout}</b></code> configuration property.</p>
- * <p>When either specifying child <code>{@link #items}</code> of a Container,
- * or dynamically {@link #add adding} Components to a Container, remember to
- * consider how you wish the Container to arrange those child elements, and
- * whether those child elements need to be sized using one of Ext's built-in
- * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
- * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
- * renders child components, appending them one after the other inside the
- * Container, and <b>does not apply any sizing</b> at all.</p>
- * <p>A common mistake is when a developer neglects to specify a
+ newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
+ if(newSize != this.dragSpecs.startSize){
+ if(this.fireEvent('beforeapply', this, newSize) !== false){
+ this.adapter.setElementSize(this, newSize);
+ this.fireEvent("moved", this, newSize);
+ this.fireEvent("resize", this, newSize);
+ }
+ }
+ },
+
+ /**
+ * Get the adapter this SplitBar uses
+ * @return The adapter object
+ */
+ getAdapter : function(){
+ return this.adapter;
+ },
+
+ /**
+ * Set the adapter this SplitBar uses
+ * @param {Object} adapter A SplitBar adapter object
+ */
+ setAdapter : function(adapter){
+ this.adapter = adapter;
+ this.adapter.init(this);
+ },
+
+ /**
+ * Gets the minimum size for the resizing element
+ * @return {Number} The minimum size
+ */
+ getMinimumSize : function(){
+ return this.minSize;
+ },
+
+ /**
+ * Sets the minimum size for the resizing element
+ * @param {Number} minSize The minimum size
+ */
+ setMinimumSize : function(minSize){
+ this.minSize = minSize;
+ },
+
+ /**
+ * Gets the maximum size for the resizing element
+ * @return {Number} The maximum size
+ */
+ getMaximumSize : function(){
+ return this.maxSize;
+ },
+
+ /**
+ * Sets the maximum size for the resizing element
+ * @param {Number} maxSize The maximum size
+ */
+ setMaximumSize : function(maxSize){
+ this.maxSize = maxSize;
+ },
+
+ /**
+ * Sets the initialize size for the resizing element
+ * @param {Number} size The initial size
+ */
+ setCurrentSize : function(size){
+ var oldAnimate = this.animate;
+ this.animate = false;
+ this.adapter.setElementSize(this, size);
+ this.animate = oldAnimate;
+ },
+
+ /**
+ * Destroy this splitbar.
+ * @param {Boolean} removeEl True to remove the element
+ */
+ destroy : function(removeEl){
+ Ext.destroy(this.shim, Ext.get(this.proxy));
+ this.dd.unreg();
+ if(removeEl){
+ this.el.remove();
+ }
+ this.purgeListeners();
+ }
+});
+
+/**
+ * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
+ */
+Ext.SplitBar.createProxy = function(dir){
+ var proxy = new Ext.Element(document.createElement("div"));
+ document.body.appendChild(proxy.dom);
+ proxy.unselectable();
+ var cls = 'x-splitbar-proxy';
+ proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
+ return proxy.dom;
+};
+
+/**
+ * @class Ext.SplitBar.BasicLayoutAdapter
+ * Default Adapter. It assumes the splitter and resizing element are not positioned
+ * elements and only gets/sets the width of the element. Generally used for table based layouts.
+ */
+Ext.SplitBar.BasicLayoutAdapter = function(){
+};
+
+Ext.SplitBar.BasicLayoutAdapter.prototype = {
+ // do nothing for now
+ init : function(s){
+
+ },
+ /**
+ * Called before drag operations to get the current size of the resizing element.
+ * @param {Ext.SplitBar} s The SplitBar using this adapter
+ */
+ getElementSize : function(s){
+ if(s.orientation == Ext.SplitBar.HORIZONTAL){
+ return s.resizingEl.getWidth();
+ }else{
+ return s.resizingEl.getHeight();
+ }
+ },
+
+ /**
+ * Called after drag operations to set the size of the resizing element.
+ * @param {Ext.SplitBar} s The SplitBar using this adapter
+ * @param {Number} newSize The new size to set
+ * @param {Function} onComplete A function to be invoked when resizing is complete
+ */
+ setElementSize : function(s, newSize, onComplete){
+ if(s.orientation == Ext.SplitBar.HORIZONTAL){
+ if(!s.animate){
+ s.resizingEl.setWidth(newSize);
+ if(onComplete){
+ onComplete(s, newSize);
+ }
+ }else{
+ s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
+ }
+ }else{
+
+ if(!s.animate){
+ s.resizingEl.setHeight(newSize);
+ if(onComplete){
+ onComplete(s, newSize);
+ }
+ }else{
+ s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
+ }
+ }
+ }
+};
+
+/**
+ *@class Ext.SplitBar.AbsoluteLayoutAdapter
+ * @extends Ext.SplitBar.BasicLayoutAdapter
+ * Adapter that moves the splitter element to align with the resized sizing element.
+ * Used with an absolute positioned SplitBar.
+ * @param {Mixed} container The container that wraps around the absolute positioned content. If it's
+ * document.body, make sure you assign an id to the body element.
+ */
+Ext.SplitBar.AbsoluteLayoutAdapter = function(container){
+ this.basic = new Ext.SplitBar.BasicLayoutAdapter();
+ this.container = Ext.get(container);
+};
+
+Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {
+ init : function(s){
+ this.basic.init(s);
+ },
+
+ getElementSize : function(s){
+ return this.basic.getElementSize(s);
+ },
+
+ setElementSize : function(s, newSize, onComplete){
+ this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
+ },
+
+ moveSplitter : function(s){
+ var yes = Ext.SplitBar;
+ switch(s.placement){
+ case yes.LEFT:
+ s.el.setX(s.resizingEl.getRight());
+ break;
+ case yes.RIGHT:
+ s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
+ break;
+ case yes.TOP:
+ s.el.setY(s.resizingEl.getBottom());
+ break;
+ case yes.BOTTOM:
+ s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
+ break;
+ }
+ }
+};
+
+/**
+ * Orientation constant - Create a vertical SplitBar
+ * @static
+ * @type Number
+ */
+Ext.SplitBar.VERTICAL = 1;
+
+/**
+ * Orientation constant - Create a horizontal SplitBar
+ * @static
+ * @type Number
+ */
+Ext.SplitBar.HORIZONTAL = 2;
+
+/**
+ * Placement constant - The resizing element is to the left of the splitter element
+ * @static
+ * @type Number
+ */
+Ext.SplitBar.LEFT = 1;
+
+/**
+ * Placement constant - The resizing element is to the right of the splitter element
+ * @static
+ * @type Number
+ */
+Ext.SplitBar.RIGHT = 2;
+
+/**
+ * Placement constant - The resizing element is positioned above the splitter element
+ * @static
+ * @type Number
+ */
+Ext.SplitBar.TOP = 3;
+
+/**
+ * Placement constant - The resizing element is positioned under splitter element
+ * @static
+ * @type Number
+ */
+Ext.SplitBar.BOTTOM = 4;
+/**
+ * @class Ext.Container
+ * @extends Ext.BoxComponent
+ * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
+ * basic behavior of containing items, namely adding, inserting and removing items.</p>
+ *
+ * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
+ * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
+ * Container to be encapsulated by an HTML element to your specifications by using the
+ * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option. This is a useful technique when creating
+ * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
+ * for example.</p>
+ *
+ * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
+ * create one using the <b><code>'container'</code></b> xtype:<pre><code>
+// explicitly create a Container
+var embeddedColumns = new Ext.Container({
+ autoEl: 'div', // This is the default
+ layout: 'column',
+ defaults: {
+ // implicitly create Container by specifying xtype
+ xtype: 'container',
+ autoEl: 'div', // This is the default.
+ layout: 'form',
+ columnWidth: 0.5,
+ style: {
+ padding: '10px'
+ }
+ },
+// The two items below will be Ext.Containers, each encapsulated by a <DIV> element.
+ items: [{
+ items: {
+ xtype: 'datefield',
+ name: 'startDate',
+ fieldLabel: 'Start date'
+ }
+ }, {
+ items: {
+ xtype: 'datefield',
+ name: 'endDate',
+ fieldLabel: 'End date'
+ }
+ }]
+});</code></pre></p>
+ *
+ * <p><u><b>Layout</b></u></p>
+ * <p>Container classes delegate the rendering of child Components to a layout
+ * manager class which must be configured into the Container using the
+ * <code><b>{@link #layout}</b></code> configuration property.</p>
+ * <p>When either specifying child <code>{@link #items}</code> of a Container,
+ * or dynamically {@link #add adding} Components to a Container, remember to
+ * consider how you wish the Container to arrange those child elements, and
+ * whether those child elements need to be sized using one of Ext's built-in
+ * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
+ * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
+ * renders child components, appending them one after the other inside the
+ * Container, and <b>does not apply any sizing</b> at all.</p>
+ * <p>A common mistake is when a developer neglects to specify a
* <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
* TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
* has been specified). If a Container is left to use the default
* the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
* with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to <code>50</code>.
*/
- // Deprecated - will be removed in 3.2.x
bufferResize: 50,
/**
'remove'
);
- this.enableBubble(this.bubbleEvents);
-
/**
* The collection of components in this container as a {@link Ext.util.MixedCollection}
* @type MixedCollection
if(this.layout && this.layout != layout){
this.layout.setContainer(null);
}
- this.initItems();
this.layout = layout;
+ this.initItems();
layout.setContainer(this);
},
* may not be removed or added. See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
* for more details.</li>
* </ul></div>
- * @param {Object/Array} component
- * <p>Either a single component or an Array of components to add. See
+ * @param {...Object/Array} component
+ * <p>Either one or more Components to add or an Array of Components to add. See
* <code>{@link #items}</code> for additional information.</p>
- * @param {Object} (Optional) component_2
- * @param {Object} (Optional) component_n
- * @return {Ext.Component} component The Component (or config object) that was added.
+ * @return {Ext.Component/Array} The Components that were added.
*/
add : function(comp){
this.initItems();
// private
createComponent : function(config, defaultType){
+ if (config.render) {
+ return config;
+ }
// add in ownerCt at creation time but then immediately
// remove so that onBeforeAdd can handle it
- var c = config.render ? config : Ext.create(Ext.apply({
+ var c = Ext.create(Ext.apply({
ownerCt: this
}, config), defaultType || this.defaultType);
+ delete c.initialConfig.ownerCt;
delete c.ownerCt;
return c;
},
/**
- * We can only lay out if there is a view area in which to layout.
- * display:none on the layout target, *or any of its parent elements* will mean it has no view area.
- */
-
- // private
+ * @private
+ * We can only lay out if there is a view area in which to layout.
+ * display:none on the layout target, *or any of its parent elements* will mean it has no view area.
+ */
canLayout : function() {
var el = this.getVisibilityEl();
return el && el.dom && !el.isStyle("display", "none");
*/
getLayout : function(){
if(!this.layout){
- var layout = new Ext.layout.ContainerLayout(this.layoutConfig);
+ var layout = new Ext.layout.AutoLayout(this.layoutConfig);
this.setLayout(layout);
}
return this.layout;
}
},
- // private
+ /**
+ * @private
+ * Renders the given Component into the target Element. If the Component is already rendered,
+ * it is moved to the provided target instead.
+ * @param {Ext.Component} c The Component to render
+ * @param {Number} position The position within the target to render the item to
+ * @param {Ext.Element} target The target Element
+ */
renderItem : function(c, position, target){
- if(c){
- if(!c.rendered){
+ if (c) {
+ if (!c.rendered) {
c.render(target, position);
this.configureItem(c, position);
- }else if(!this.isValidParent(c, target)){
- if(Ext.isNumber(position)){
+ } else if (!this.isValidParent(c, target)) {
+ if (Ext.isNumber(position)) {
position = target.dom.childNodes[position];
}
+
target.dom.insertBefore(c.getPositionEl().dom, position || null);
c.container = target;
this.configureItem(c, position);
return items;
},
- // private
+ /**
+ * @private
+ * Applies extraCls and hides the item if renderHidden is true
+ */
configureItem: function(c, position){
- if(this.extraCls){
+ if (this.extraCls) {
var t = c.getPositionEl ? c.getPositionEl() : c;
t.addClass(this.extraCls);
}
+
// If we are forcing a layout, do so *before* we hide so elements have height/width
- if(c.doLayout && this.forceLayout){
+ if (c.doLayout && this.forceLayout) {
c.doLayout();
}
if (this.renderHidden && c != this.activeItem) {
},
onRemove: function(c){
- if(this.activeItem == c){
+ if(this.activeItem == c){
delete this.activeItem;
- }
- if(c.rendered && this.extraCls){
+ }
+ if(c.rendered && this.extraCls){
var t = c.getPositionEl ? c.getPositionEl() : c;
t.removeClass(this.extraCls);
}
if(ct.collapsed){
return;
}
- if(b = ct.bufferResize){
- // Only allow if we should buffer the layout
- if(ct.shouldBufferLayout()){
- if(!this.resizeTask){
- this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
- this.resizeBuffer = Ext.isNumber(b) ? b : 50;
- }
- ct.layoutPending = true;
- this.resizeTask.delay(this.resizeBuffer);
+ if(b = ct.bufferResize && ct.shouldBufferLayout()){
+ if(!this.resizeTask){
+ this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
+ this.resizeBuffer = Ext.isNumber(b) ? b : 50;
}
+ ct.layoutPending = true;
+ this.resizeTask.delay(this.resizeBuffer);
}else{
this.runLayout();
}
runLayout: function(){
var ct = this.container;
- // AutoLayout is known to require the recursive doLayout call, others need this currently (BorderLayout for example)
- // but shouldn't. A more extensive review will take place for 3.2 which requires a ContainerMgr with hierarchy lookups.
- //this.layout();
- //ct.onLayout();
- ct.doLayout();
+ this.layout();
+ ct.onLayout();
delete ct.layoutPending;
},
// private
setContainer : function(ct){
- if (!Ext.LayoutManager) {
- Ext.LayoutManager = {};
- }
-
- /* This monitorResize flag will be renamed soon as to avoid confusion
- * with the Container version which hooks onWindowResize to doLayout
- *
- * monitorResize flag in this context attaches the resize event between
- * a container and it's layout
- */
-
+ /**
+ * This monitorResize flag will be renamed soon as to avoid confusion
+ * with the Container version which hooks onWindowResize to doLayout
+ *
+ * monitorResize flag in this context attaches the resize event between
+ * a container and it's layout
+ */
if(this.monitorResize && ct != this.container){
var old = this.container;
if(old){
this.container = ct;
},
- // private
+ /**
+ * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
+ * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
+ * @param {Number|String} v The encoded margins
+ * @return {Object} An object with margin sizes for top, right, bottom and left
+ */
parseMargins : function(v){
- if(Ext.isNumber(v)){
+ if (Ext.isNumber(v)) {
v = v.toString();
}
- var ms = v.split(' ');
- var len = ms.length;
- if(len == 1){
+ var ms = v.split(' '),
+ len = ms.length;
+
+ if (len == 1) {
ms[1] = ms[2] = ms[3] = ms[0];
- } else if(len == 2){
+ } else if(len == 2) {
ms[2] = ms[0];
ms[3] = ms[1];
- } else if(len == 3){
+ } else if(len == 3) {
ms[3] = ms[1];
}
+
return {
- top:parseInt(ms[0], 10) || 0,
- right:parseInt(ms[1], 10) || 0,
+ top :parseInt(ms[0], 10) || 0,
+ right :parseInt(ms[1], 10) || 0,
bottom:parseInt(ms[2], 10) || 0,
- left:parseInt(ms[3], 10) || 0
+ left :parseInt(ms[3], 10) || 0
};
},
* @protected
*/
destroy : function(){
+ // Stop any buffered layout tasks
+ if(this.resizeTask && this.resizeTask.cancel){
+ this.resizeTask.cancel();
+ }
if(!Ext.isEmpty(this.targetCls)){
var target = this.container.getLayoutTarget();
if(target){
* @class Ext.layout.AutoLayout
* <p>The AutoLayout is the default layout manager delegated by {@link Ext.Container} to
* render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
- * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout
- * classes in Ext. It simply renders all child Components into the Container, performing no sizing or
- * positioning services. To utilize a layout that provides sizing and positioning of child Components,
- * specify an appropriate <tt>{@link Ext.Container#layout layout}</tt>.</p>
+ * a {@link Ext.Container Container}.</tt>. AutoLayout provides only a passthrough of any layout calls
+ * to any child containers.</p>
*/
Ext.layout.AutoLayout = Ext.extend(Ext.layout.ContainerLayout, {
- runLayout: function(){
- var ct = this.container;
- ct.doLayout();
- delete ct.layoutPending;
+ type: 'auto',
+
+ monitorResize: true,
+
+ onLayout : function(ct, target){
+ Ext.layout.AutoLayout.superclass.onLayout.call(this, ct, target);
+ var cs = this.getRenderedItems(ct), len = cs.length, i, c;
+ for(i = 0; i < len; i++){
+ c = cs[i];
+ if (c.doLayout){
+ // Shallow layout children
+ c.doLayout(true);
+ }
+ }
}
});
Ext.Container.LAYOUTS['auto'] = Ext.layout.AutoLayout;
-/**\r
- * @class Ext.layout.FitLayout\r
- * @extends Ext.layout.ContainerLayout\r
- * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's\r
- * container. This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}\r
- * config, and should generally not need to be created directly via the new keyword.</p>\r
- * <p>FitLayout does not have any direct config options (other than inherited ones). To fit a panel to a container\r
- * using FitLayout, simply set layout:'fit' on the container and add a single panel to it. If the container has\r
- * multiple panels, only the first one will be displayed. Example usage:</p>\r
- * <pre><code>\r
-var p = new Ext.Panel({\r
- title: 'Fit Layout',\r
- layout:'fit',\r
- items: {\r
- title: 'Inner Panel',\r
- html: '<p>This is the inner panel content</p>',\r
- border: false\r
- }\r
-});\r
-</code></pre>\r
- */\r
-Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
- // private\r
- monitorResize:true,\r
-\r
- type: 'fit',\r
-\r
- getLayoutTargetSize : function() {\r
- var target = this.container.getLayoutTarget();\r
- if (!target) {\r
- return {};\r
- }\r
- // Style Sized (scrollbars not included)\r
- return target.getStyleSize();\r
- },\r
-\r
- // private\r
- onLayout : function(ct, target){\r
- Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);\r
- if(!ct.collapsed){\r
- this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());\r
- }\r
- },\r
-\r
- // private\r
- setItemSize : function(item, size){\r
- if(item && size.height > 0){ // display none?\r
- item.setSize(size);\r
- }\r
- }\r
-});\r
-Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**\r
- * @class Ext.layout.CardLayout\r
- * @extends Ext.layout.FitLayout\r
- * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be\r
- * visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc.\r
- * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,\r
- * and should generally not need to be created directly via the new keyword.</p>\r
- * <p>The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time,\r
- * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of\r
- * the next panel to display. The layout itself does not provide a user interface for handling this navigation,\r
- * so that functionality must be provided by the developer.</p>\r
- * <p>In the following example, a simplistic wizard setup is demonstrated. A button bar is added\r
- * to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a\r
- * common navigation routine -- for this example, the implementation of that routine has been ommitted since\r
- * it can be any type of custom logic. Note that other uses of a CardLayout (like a tab control) would require a\r
- * completely different implementation. For serious implementations, a better approach would be to extend\r
- * CardLayout to provide the custom functionality needed. Example usage:</p>\r
- * <pre><code>\r
-var navHandler = function(direction){\r
- // This routine could contain business logic required to manage the navigation steps.\r
- // It would call setActiveItem as needed, manage navigation button state, handle any\r
- // branching logic that might be required, handle alternate actions like cancellation\r
- // or finalization, etc. A complete wizard implementation could get pretty\r
- // sophisticated depending on the complexity required, and should probably be\r
- // done as a subclass of CardLayout in a real-world implementation.\r
-};\r
-\r
-var card = new Ext.Panel({\r
- title: 'Example Wizard',\r
- layout:'card',\r
- activeItem: 0, // make sure the active item is set on the container config!\r
- bodyStyle: 'padding:15px',\r
- defaults: {\r
- // applied to each contained panel\r
- border:false\r
- },\r
- // just an example of one possible navigation scheme, using buttons\r
- bbar: [\r
- {\r
- id: 'move-prev',\r
- text: 'Back',\r
- handler: navHandler.createDelegate(this, [-1]),\r
- disabled: true\r
- },\r
- '->', // greedy spacer so that the buttons are aligned to each side\r
- {\r
- id: 'move-next',\r
- text: 'Next',\r
- handler: navHandler.createDelegate(this, [1])\r
- }\r
- ],\r
- // the panels (or "cards") within the layout\r
- items: [{\r
- id: 'card-0',\r
- html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'\r
- },{\r
- id: 'card-1',\r
- html: '<p>Step 2 of 3</p>'\r
- },{\r
- id: 'card-2',\r
- html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'\r
- }]\r
-});\r
-</code></pre>\r
- */\r
-Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {\r
- /**\r
- * @cfg {Boolean} deferredRender\r
- * True to render each contained item at the time it becomes active, false to render all contained items\r
- * as soon as the layout is rendered (defaults to false). If there is a significant amount of content or\r
- * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to\r
- * true might improve performance.\r
- */\r
- deferredRender : false,\r
-\r
- /**\r
- * @cfg {Boolean} layoutOnCardChange\r
- * True to force a layout of the active item when the active card is changed. Defaults to false.\r
- */\r
- layoutOnCardChange : false,\r
-\r
- /**\r
- * @cfg {Boolean} renderHidden @hide\r
- */\r
- // private\r
- renderHidden : true,\r
-\r
- type: 'card',\r
-\r
- constructor: function(config){\r
- Ext.layout.CardLayout.superclass.constructor.call(this, config);\r
- },\r
-\r
- /**\r
- * Sets the active (visible) item in the layout.\r
- * @param {String/Number} item The string component id or numeric index of the item to activate\r
- */\r
- setActiveItem : function(item){\r
- var ai = this.activeItem,\r
- ct = this.container;\r
- item = ct.getComponent(item);\r
-\r
- // Is this a valid, different card?\r
- if(item && ai != item){\r
-\r
- // Changing cards, hide the current one\r
- if(ai){\r
- ai.hide();\r
- if (ai.hidden !== true) {\r
- return false;\r
- }\r
- ai.fireEvent('deactivate', ai);\r
- }\r
- // Change activeItem reference\r
- this.activeItem = item;\r
-\r
- // The container is about to get a recursive layout, remove any deferLayout reference\r
- // because it will trigger a redundant layout.\r
- delete item.deferLayout;\r
-\r
- // Show the new component\r
- item.show();\r
-\r
- this.layout();\r
-\r
- if(item.doLayout){\r
- item.doLayout();\r
- }\r
- item.fireEvent('activate', item);\r
- }\r
- },\r
-\r
- // private\r
- renderAll : function(ct, target){\r
- if(this.deferredRender){\r
- this.renderItem(this.activeItem, undefined, target);\r
- }else{\r
- Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);\r
- }\r
- }\r
-});\r
-Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/**
- * @class Ext.layout.AnchorLayout
+/**
+ * @class Ext.layout.FitLayout
* @extends Ext.layout.ContainerLayout
- * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
- * If the container is resized, all anchored items are automatically rerendered according to their
- * <b><tt>{@link #anchor}</tt></b> rules.</p>
- * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}
+ * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
+ * container. This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}
* config, and should generally not need to be created directly via the new keyword.</p>
- * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
- * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
- * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
- * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
- * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
- * logic if necessary. For example:</p>
+ * <p>FitLayout does not have any direct config options (other than inherited ones). To fit a panel to a container
+ * using FitLayout, simply set layout:'fit' on the container and add a single panel to it. If the container has
+ * multiple panels, only the first one will be displayed. Example usage:</p>
* <pre><code>
-var viewport = new Ext.Viewport({
- layout:'anchor',
- anchorSize: {width:800, height:600},
- items:[{
- title:'Item 1',
- html:'Content 1',
- width:800,
- anchor:'right 20%'
- },{
- title:'Item 2',
- html:'Content 2',
- width:300,
- anchor:'50% 30%'
- },{
- title:'Item 3',
- html:'Content 3',
- width:600,
- anchor:'-100 50%'
- }]
+var p = new Ext.Panel({
+ title: 'Fit Layout',
+ layout:'fit',
+ items: {
+ title: 'Inner Panel',
+ html: '<p>This is the inner panel content</p>',
+ border: false
+ }
});
- * </code></pre>
+</code></pre>
*/
-Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
- /**
- * @cfg {String} anchor
- * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
- * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
- *
- * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
- * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
- * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
- * The following types of anchor values are supported:<div class="mdetail-params"><ul>
- *
- * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
+Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ // private
+ monitorResize:true,
+
+ type: 'fit',
+
+ getLayoutTargetSize : function() {
+ var target = this.container.getLayoutTarget();
+ if (!target) {
+ return {};
+ }
+ // Style Sized (scrollbars not included)
+ return target.getStyleSize();
+ },
+
+ // private
+ onLayout : function(ct, target){
+ Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);
+ if(!ct.collapsed){
+ this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());
+ }
+ },
+
+ // private
+ setItemSize : function(item, size){
+ if(item && size.height > 0){ // display none?
+ item.setSize(size);
+ }
+ }
+});
+Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**
+ * @class Ext.layout.CardLayout
+ * @extends Ext.layout.FitLayout
+ * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
+ * visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc.
+ * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,
+ * and should generally not need to be created directly via the new keyword.</p>
+ * <p>The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time,
+ * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of
+ * the next panel to display. The layout itself does not provide a user interface for handling this navigation,
+ * so that functionality must be provided by the developer.</p>
+ * <p>In the following example, a simplistic wizard setup is demonstrated. A button bar is added
+ * to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a
+ * common navigation routine -- for this example, the implementation of that routine has been ommitted since
+ * it can be any type of custom logic. Note that other uses of a CardLayout (like a tab control) would require a
+ * completely different implementation. For serious implementations, a better approach would be to extend
+ * CardLayout to provide the custom functionality needed. Example usage:</p>
+ * <pre><code>
+var navHandler = function(direction){
+ // This routine could contain business logic required to manage the navigation steps.
+ // It would call setActiveItem as needed, manage navigation button state, handle any
+ // branching logic that might be required, handle alternate actions like cancellation
+ // or finalization, etc. A complete wizard implementation could get pretty
+ // sophisticated depending on the complexity required, and should probably be
+ // done as a subclass of CardLayout in a real-world implementation.
+};
+
+var card = new Ext.Panel({
+ title: 'Example Wizard',
+ layout:'card',
+ activeItem: 0, // make sure the active item is set on the container config!
+ bodyStyle: 'padding:15px',
+ defaults: {
+ // applied to each contained panel
+ border:false
+ },
+ // just an example of one possible navigation scheme, using buttons
+ bbar: [
+ {
+ id: 'move-prev',
+ text: 'Back',
+ handler: navHandler.createDelegate(this, [-1]),
+ disabled: true
+ },
+ '->', // greedy spacer so that the buttons are aligned to each side
+ {
+ id: 'move-next',
+ text: 'Next',
+ handler: navHandler.createDelegate(this, [1])
+ }
+ ],
+ // the panels (or "cards") within the layout
+ items: [{
+ id: 'card-0',
+ html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'
+ },{
+ id: 'card-1',
+ html: '<p>Step 2 of 3</p>'
+ },{
+ id: 'card-2',
+ html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'
+ }]
+});
+</code></pre>
+ */
+Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {
+ /**
+ * @cfg {Boolean} deferredRender
+ * True to render each contained item at the time it becomes active, false to render all contained items
+ * as soon as the layout is rendered (defaults to false). If there is a significant amount of content or
+ * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to
+ * true might improve performance.
+ */
+ deferredRender : false,
+
+ /**
+ * @cfg {Boolean} layoutOnCardChange
+ * True to force a layout of the active item when the active card is changed. Defaults to false.
+ */
+ layoutOnCardChange : false,
+
+ /**
+ * @cfg {Boolean} renderHidden @hide
+ */
+ // private
+ renderHidden : true,
+
+ type: 'card',
+
+ /**
+ * Sets the active (visible) item in the layout.
+ * @param {String/Number} item The string component id or numeric index of the item to activate
+ */
+ setActiveItem : function(item){
+ var ai = this.activeItem,
+ ct = this.container;
+ item = ct.getComponent(item);
+
+ // Is this a valid, different card?
+ if(item && ai != item){
+
+ // Changing cards, hide the current one
+ if(ai){
+ ai.hide();
+ if (ai.hidden !== true) {
+ return false;
+ }
+ ai.fireEvent('deactivate', ai);
+ }
+
+ var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered);
+
+ // Change activeItem reference
+ this.activeItem = item;
+
+ // The container is about to get a recursive layout, remove any deferLayout reference
+ // because it will trigger a redundant layout.
+ delete item.deferLayout;
+
+ // Show the new component
+ item.show();
+
+ this.layout();
+
+ if(layout){
+ item.doLayout();
+ }
+ item.fireEvent('activate', item);
+ }
+ },
+
+ // private
+ renderAll : function(ct, target){
+ if(this.deferredRender){
+ this.renderItem(this.activeItem, undefined, target);
+ }else{
+ Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);
+ }
+ }
+});
+Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;
+/**
+ * @class Ext.layout.AnchorLayout
+ * @extends Ext.layout.ContainerLayout
+ * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
+ * If the container is resized, all anchored items are automatically rerendered according to their
+ * <b><tt>{@link #anchor}</tt></b> rules.</p>
+ * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}
+ * config, and should generally not need to be created directly via the new keyword.</p>
+ * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
+ * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
+ * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
+ * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
+ * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
+ * logic if necessary. For example:</p>
+ * <pre><code>
+var viewport = new Ext.Viewport({
+ layout:'anchor',
+ anchorSize: {width:800, height:600},
+ items:[{
+ title:'Item 1',
+ html:'Content 1',
+ width:800,
+ anchor:'right 20%'
+ },{
+ title:'Item 2',
+ html:'Content 2',
+ width:300,
+ anchor:'50% 30%'
+ },{
+ title:'Item 3',
+ html:'Content 3',
+ width:600,
+ anchor:'-100 50%'
+ }]
+});
+ * </code></pre>
+ */
+Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ /**
+ * @cfg {String} anchor
+ * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
+ * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
+ *
+ * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
+ * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
+ * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
+ * The following types of anchor values are supported:<div class="mdetail-params"><ul>
+ *
+ * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
* The first anchor is the percentage width that the item should take up within the container, and the
* second is the percentage height. For example:<pre><code>
// two values specified
*/
// private
- monitorResize:true,
- type: 'anchor',
+ monitorResize : true,
+
+ type : 'anchor',
+
+ /**
+ * @cfg {String} defaultAnchor
+ *
+ * default anchor for all child container items applied if no anchor or specific width is set on the child item. Defaults to '100%'.
+ *
+ */
+ defaultAnchor : '100%',
+
+ parseAnchorRE : /^(r|right|b|bottom)$/i,
getLayoutTargetSize : function() {
var target = this.container.getLayoutTarget();
ah = ct.initialConfig.height;
}
- var cs = this.getRenderedItems(ct), len = cs.length, i, c, a, cw, ch, el, vs;
+ var cs = this.getRenderedItems(ct), len = cs.length, i, c, a, cw, ch, el, vs, boxes = [];
for(i = 0; i < len; i++){
c = cs[i];
el = c.getPositionEl();
+
+ // If a child container item has no anchor and no specific width, set the child to the default anchor size
+ if (!c.anchor && c.items && !Ext.isNumber(c.width) && !(Ext.isIE6 && Ext.isStrict)){
+ c.anchor = this.defaultAnchor;
+ }
+
if(c.anchor){
a = c.anchorSpec;
if(!a){ // cache all anchor values
ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h) - el.getMargins('tb'), c) : undefined;
if(cw || ch){
- c.setSize(cw || undefined, ch || undefined);
+ boxes.push({
+ comp: c,
+ width: cw || undefined,
+ height: ch || undefined
+ });
}
}
}
+ for (i = 0, len = boxes.length; i < len; i++) {
+ c = boxes[i];
+ c.comp.setSize(c.width, c.height);
+ }
},
// private
parseAnchor : function(a, start, cstart){
if(a && a != 'none'){
var last;
- if(/^(r|right|b|bottom)$/i.test(a)){ // standard anchor
+ // standard anchor
+ if(this.parseAnchorRE.test(a)){
var diff = cstart - start;
return function(v){
if(v !== last){
return v - diff;
}
}
+ // percentage
}else if(a.indexOf('%') != -1){
- var ratio = parseFloat(a.replace('%', ''))*.01; // percentage
+ var ratio = parseFloat(a.replace('%', ''))*.01;
return function(v){
if(v !== last){
last = v;
return Math.floor(v*ratio);
}
}
+ // simple offset adjustment
}else{
a = parseInt(a, 10);
- if(!isNaN(a)){ // simple offset adjustment
+ if(!isNaN(a)){
return function(v){
if(v !== last){
last = v;
*/
});
Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;
-/**\r
- * @class Ext.layout.ColumnLayout\r
- * @extends Ext.layout.ContainerLayout\r
- * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of\r
- * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.\r
- * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,\r
- * and should generally not need to be created directly via the new keyword.</p>\r
- * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a\r
- * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it. The\r
- * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.\r
- * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>\r
- * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.\r
- * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and\r
- * less than 1 (e.g., .25).</p>\r
- * <p>The basic rules for specifying column widths are pretty simple. The logic makes two passes through the\r
- * set of contained panels. During the first layout pass, all panels that either have a fixed width or none\r
- * specified (auto) are skipped, but their widths are subtracted from the overall container width. During the second\r
- * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on\r
- * the total <b>remaining</b> container width. In other words, percentage width panels are designed to fill the space\r
- * left over by all the fixed-width and/or auto-width panels. Because of this, while you can specify any number of columns\r
- * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your\r
- * layout may not render as expected. Example usage:</p>\r
- * <pre><code>\r
-// All columns are percentages -- they must add up to 1\r
-var p = new Ext.Panel({\r
- title: 'Column Layout - Percentage Only',\r
- layout:'column',\r
- items: [{\r
- title: 'Column 1',\r
- columnWidth: .25\r
- },{\r
- title: 'Column 2',\r
- columnWidth: .6\r
- },{\r
- title: 'Column 3',\r
- columnWidth: .15\r
- }]\r
-});\r
-\r
-// Mix of width and columnWidth -- all columnWidth values must add up\r
-// to 1. The first column will take up exactly 120px, and the last two\r
-// columns will fill the remaining container width.\r
-var p = new Ext.Panel({\r
- title: 'Column Layout - Mixed',\r
- layout:'column',\r
- items: [{\r
- title: 'Column 1',\r
- width: 120\r
- },{\r
- title: 'Column 2',\r
- columnWidth: .8\r
- },{\r
- title: 'Column 3',\r
- columnWidth: .2\r
- }]\r
-});\r
-</code></pre>\r
- */\r
-Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
- // private\r
- monitorResize:true,\r
-\r
- type: 'column',\r
-\r
- extraCls: 'x-column',\r
-\r
- scrollOffset : 0,\r
-\r
- // private\r
-\r
- targetCls: 'x-column-layout-ct',\r
-\r
- isValidParent : function(c, target){\r
- return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;\r
- },\r
-\r
- getLayoutTargetSize : function() {\r
- var target = this.container.getLayoutTarget(), ret;\r
- if (target) {\r
- ret = target.getViewSize();\r
- ret.width -= target.getPadding('lr');\r
- ret.height -= target.getPadding('tb');\r
- }\r
- return ret;\r
- },\r
-\r
- renderAll : function(ct, target) {\r
- if(!this.innerCt){\r
- // the innerCt prevents wrapping and shuffling while\r
- // the container is resizing\r
- this.innerCt = target.createChild({cls:'x-column-inner'});\r
- this.innerCt.createChild({cls:'x-clear'});\r
- }\r
- Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt);\r
- },\r
-\r
- // private\r
- onLayout : function(ct, target){\r
- var cs = ct.items.items, len = cs.length, c, i;\r
-\r
- this.renderAll(ct, target);\r
-\r
- var size = this.getLayoutTargetSize();\r
-\r
- if(size.width < 1 && size.height < 1){ // display none?\r
- return;\r
- }\r
-\r
- var w = size.width - this.scrollOffset,\r
- h = size.height,\r
- pw = w;\r
-\r
- this.innerCt.setWidth(w);\r
-\r
- // some columns can be percentages while others are fixed\r
- // so we need to make 2 passes\r
-\r
- for(i = 0; i < len; i++){\r
- c = cs[i];\r
- if(!c.columnWidth){\r
- pw -= (c.getWidth() + c.getPositionEl().getMargins('lr'));\r
- }\r
- }\r
-\r
- pw = pw < 0 ? 0 : pw;\r
-\r
- for(i = 0; i < len; i++){\r
- c = cs[i];\r
- if(c.columnWidth){\r
- c.setSize(Math.floor(c.columnWidth * pw) - c.getPositionEl().getMargins('lr'));\r
- }\r
- }\r
-\r
- // Browsers differ as to when they account for scrollbars. We need to re-measure to see if the scrollbar\r
- // spaces were accounted for properly. If not, re-layout.\r
- if (Ext.isIE) {\r
- if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {\r
- var ts = this.getLayoutTargetSize();\r
- if (ts.width != size.width){\r
- this.adjustmentPass = true;\r
- this.onLayout(ct, target);\r
- }\r
- }\r
- }\r
- delete this.adjustmentPass;\r
- }\r
-\r
- /**\r
- * @property activeItem\r
- * @hide\r
- */\r
-});\r
-\r
-Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;/**
+/**
+ * @class Ext.layout.ColumnLayout
+ * @extends Ext.layout.ContainerLayout
+ * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of
+ * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.
+ * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,
+ * and should generally not need to be created directly via the new keyword.</p>
+ * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
+ * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it. The
+ * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.
+ * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>
+ * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.
+ * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and
+ * less than 1 (e.g., .25).</p>
+ * <p>The basic rules for specifying column widths are pretty simple. The logic makes two passes through the
+ * set of contained panels. During the first layout pass, all panels that either have a fixed width or none
+ * specified (auto) are skipped, but their widths are subtracted from the overall container width. During the second
+ * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on
+ * the total <b>remaining</b> container width. In other words, percentage width panels are designed to fill the space
+ * left over by all the fixed-width and/or auto-width panels. Because of this, while you can specify any number of columns
+ * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your
+ * layout may not render as expected. Example usage:</p>
+ * <pre><code>
+// All columns are percentages -- they must add up to 1
+var p = new Ext.Panel({
+ title: 'Column Layout - Percentage Only',
+ layout:'column',
+ items: [{
+ title: 'Column 1',
+ columnWidth: .25
+ },{
+ title: 'Column 2',
+ columnWidth: .6
+ },{
+ title: 'Column 3',
+ columnWidth: .15
+ }]
+});
+
+// Mix of width and columnWidth -- all columnWidth values must add up
+// to 1. The first column will take up exactly 120px, and the last two
+// columns will fill the remaining container width.
+var p = new Ext.Panel({
+ title: 'Column Layout - Mixed',
+ layout:'column',
+ items: [{
+ title: 'Column 1',
+ width: 120
+ },{
+ title: 'Column 2',
+ columnWidth: .8
+ },{
+ title: 'Column 3',
+ columnWidth: .2
+ }]
+});
+</code></pre>
+ */
+Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ // private
+ monitorResize:true,
+
+ type: 'column',
+
+ extraCls: 'x-column',
+
+ scrollOffset : 0,
+
+ // private
+
+ targetCls: 'x-column-layout-ct',
+
+ isValidParent : function(c, target){
+ return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
+ },
+
+ 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;
+ },
+
+ renderAll : function(ct, target) {
+ if(!this.innerCt){
+ // the innerCt prevents wrapping and shuffling while
+ // the container is resizing
+ this.innerCt = target.createChild({cls:'x-column-inner'});
+ this.innerCt.createChild({cls:'x-clear'});
+ }
+ Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt);
+ },
+
+ // private
+ onLayout : function(ct, target){
+ var cs = ct.items.items,
+ len = cs.length,
+ c,
+ i,
+ m,
+ margins = [];
+
+ this.renderAll(ct, target);
+
+ var size = this.getLayoutTargetSize();
+
+ if(size.width < 1 && size.height < 1){ // display none?
+ return;
+ }
+
+ var w = size.width - this.scrollOffset,
+ h = size.height,
+ pw = w;
+
+ this.innerCt.setWidth(w);
+
+ // some columns can be percentages while others are fixed
+ // so we need to make 2 passes
+
+ for(i = 0; i < len; i++){
+ c = cs[i];
+ m = c.getPositionEl().getMargins('lr');
+ margins[i] = m;
+ if(!c.columnWidth){
+ pw -= (c.getWidth() + m);
+ }
+ }
+
+ pw = pw < 0 ? 0 : pw;
+
+ for(i = 0; i < len; i++){
+ c = cs[i];
+ m = margins[i];
+ if(c.columnWidth){
+ c.setSize(Math.floor(c.columnWidth * pw) - m);
+ }
+ }
+
+ // Browsers differ as to when they account for scrollbars. We need to re-measure to see if the scrollbar
+ // spaces were accounted for properly. If not, re-layout.
+ if (Ext.isIE) {
+ if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
+ var ts = this.getLayoutTargetSize();
+ if (ts.width != size.width){
+ this.adjustmentPass = true;
+ this.onLayout(ct, target);
+ }
+ }
+ }
+ delete this.adjustmentPass;
+ }
+
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+
+Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;
+/**
* @class Ext.layout.BorderLayout
* @extends Ext.layout.ContainerLayout
* <p>This is a multi-pane, application-oriented UI layout style that supports multiple
initAutoHide : function(){
if(this.autoHide !== false){
if(!this.autoHideHd){
- var st = new Ext.util.DelayedTask(this.slideIn, this);
+ this.autoHideSlideTask = new Ext.util.DelayedTask(this.slideIn, this);
this.autoHideHd = {
"mouseout": function(e){
if(!e.within(this.el, true)){
- st.delay(500);
+ this.autoHideSlideTask.delay(500);
}
},
"mouseover" : function(e){
- st.cancel();
+ this.autoHideSlideTask.cancel();
},
scope : this
};
},
destroy : function(){
+ if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
+ this.autoHideSlideTask.cancel();
+ }
Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
}
};
type: 'form',
-
onRemove: function(c){
Ext.layout.FormLayout.superclass.onRemove.call(this, c);
if(this.trackLabels){
}
// 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){
+ 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){
+ if (c.customItemCt) {
Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
}
}
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){
if (items[i]){
ls += items[i];
if (ls.substr(-1, 1) != ';'){
- ls += ';'
+ ls += ';';
}
}
}
* <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
*/
- // private
+ /**
+ * @private
+ *
+ */
renderItem : function(c, position, target){
if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
var args = this.getTemplateArgs(c);
* 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>
+ * 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>
* 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 An object hash containing the properties required to render the Field.
+ * @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,
- labelStyle: this.getLabelStyle(field.labelStyle),
- elementStyle: this.elementStyle||'',
- labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator),
- itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''),
- clearCls: field.clearCls || 'x-form-clear-left'
+ 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)
};
},
});
Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;
-/**\r
- * @class Ext.layout.AccordionLayout\r
- * @extends Ext.layout.FitLayout\r
- * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only\r
- * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>\r
- * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>\r
- * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>\r
- * configuration property. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>\r
- * <p>Example usage:</p>\r
- * <pre><code>\r
-var accordion = new Ext.Panel({\r
- title: 'Accordion Layout',\r
- layout:'accordion',\r
- defaults: {\r
- // applied to each contained panel\r
- bodyStyle: 'padding:15px'\r
- },\r
- layoutConfig: {\r
- // layout-specific configs go here\r
- titleCollapse: false,\r
- animate: true,\r
- activeOnTop: true\r
- },\r
- items: [{\r
- title: 'Panel 1',\r
- html: '<p>Panel content!</p>'\r
- },{\r
- title: 'Panel 2',\r
- html: '<p>Panel content!</p>'\r
- },{\r
- title: 'Panel 3',\r
- html: '<p>Panel content!</p>'\r
- }]\r
-});\r
-</code></pre>\r
- */\r
-Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {\r
- /**\r
- * @cfg {Boolean} fill\r
- * True to adjust the active item's height to fill the available space in the container, false to use the\r
- * item's current height, or auto height if not explicitly set (defaults to true).\r
- */\r
- fill : true,\r
- /**\r
- * @cfg {Boolean} autoWidth\r
- * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).\r
- * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within\r
- * layouts if they have auto width, so in such cases this config should be set to false.\r
- */\r
- autoWidth : true,\r
- /**\r
- * @cfg {Boolean} titleCollapse\r
- * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow\r
- * expand/collapse only when the toggle tool button is clicked (defaults to true). When set to false,\r
- * {@link #hideCollapseTool} should be false also.\r
- */\r
- titleCollapse : true,\r
- /**\r
- * @cfg {Boolean} hideCollapseTool\r
- * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).\r
- * When set to true, {@link #titleCollapse} should be true also.\r
- */\r
- hideCollapseTool : false,\r
- /**\r
- * @cfg {Boolean} collapseFirst\r
- * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools\r
- * in the contained panels' title bars, false to render it last (defaults to false).\r
- */\r
- collapseFirst : false,\r
- /**\r
- * @cfg {Boolean} animate\r
- * True to slide the contained panels open and closed during expand/collapse using animation, false to open and\r
- * close directly with no animation (defaults to false). Note: to defer to the specific config setting of each\r
- * contained panel for this property, set this to undefined at the layout level.\r
- */\r
- animate : false,\r
- /**\r
- * @cfg {Boolean} sequence\r
- * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.\r
- */\r
- sequence : false,\r
- /**\r
- * @cfg {Boolean} activeOnTop\r
- * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,\r
- * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).\r
- */\r
- activeOnTop : false,\r
-\r
- type: 'accordion',\r
-\r
- renderItem : function(c){\r
- if(this.animate === false){\r
- c.animCollapse = false;\r
- }\r
- c.collapsible = true;\r
- if(this.autoWidth){\r
- c.autoWidth = true;\r
- }\r
- if(this.titleCollapse){\r
- c.titleCollapse = true;\r
- }\r
- if(this.hideCollapseTool){\r
- c.hideCollapseTool = true;\r
- }\r
- if(this.collapseFirst !== undefined){\r
- c.collapseFirst = this.collapseFirst;\r
- }\r
- if(!this.activeItem && !c.collapsed){\r
- this.setActiveItem(c, true);\r
- }else if(this.activeItem && this.activeItem != c){\r
- c.collapsed = true;\r
- }\r
- Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);\r
- c.header.addClass('x-accordion-hd');\r
- c.on('beforeexpand', this.beforeExpand, this);\r
- },\r
-\r
- onRemove: function(c){\r
- Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);\r
- if(c.rendered){\r
- c.header.removeClass('x-accordion-hd');\r
- }\r
- c.un('beforeexpand', this.beforeExpand, this);\r
- },\r
-\r
- // private\r
- beforeExpand : function(p, anim){\r
- var ai = this.activeItem;\r
- if(ai){\r
- if(this.sequence){\r
- delete this.activeItem;\r
- if (!ai.collapsed){\r
- ai.collapse({callback:function(){\r
- p.expand(anim || true);\r
- }, scope: this});\r
- return false;\r
- }\r
- }else{\r
- ai.collapse(this.animate);\r
- }\r
- }\r
- this.setActive(p);\r
- if(this.activeOnTop){\r
- p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);\r
- }\r
- // Items have been hidden an possibly rearranged, we need to get the container size again.\r
- this.layout();\r
- },\r
-\r
- // private\r
- setItemSize : function(item, size){\r
- if(this.fill && item){\r
- var hh = 0, i, ct = this.getRenderedItems(this.container), len = ct.length, p;\r
- // Add up all the header heights\r
- for (i = 0; i < len; i++) {\r
- if((p = ct[i]) != item){\r
- hh += p.header.getHeight();\r
- }\r
- };\r
- // Subtract the header heights from the container size\r
- size.height -= hh;\r
- // Call setSize on the container to set the correct height. For Panels, deferedHeight\r
- // will simply store this size for when the expansion is done.\r
- item.setSize(size);\r
- }\r
- },\r
-\r
- /**\r
- * Sets the active (expanded) item in the layout.\r
- * @param {String/Number} item The string component id or numeric index of the item to activate\r
- */\r
- setActiveItem : function(item){\r
- this.setActive(item, true);\r
- },\r
-\r
- // private\r
- setActive : function(item, expand){\r
- var ai = this.activeItem;\r
- item = this.container.getComponent(item);\r
- if(ai != item){\r
- if(item.rendered && item.collapsed && expand){\r
- item.expand();\r
- }else{\r
- if(ai){\r
- ai.fireEvent('deactivate', ai);\r
- }\r
- this.activeItem = item;\r
- item.fireEvent('activate', item);\r
- }\r
- }\r
- }\r
-});\r
-Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;\r
-\r
-//backwards compat\r
-Ext.layout.Accordion = Ext.layout.AccordionLayout;/**\r
- * @class Ext.layout.TableLayout\r
- * @extends Ext.layout.ContainerLayout\r
- * <p>This layout allows you to easily render content into an HTML table. The total number of columns can be\r
- * specified, and rowspan and colspan can be used to create complex layouts within the table.\r
- * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,\r
- * and should generally not need to be created directly via the new keyword.</p>\r
- * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via\r
- * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout. In the\r
- * case of TableLayout, the only valid layout config property is {@link #columns}. However, the items added to a\r
- * TableLayout can supply the following table-specific config properties:</p>\r
- * <ul>\r
- * <li><b>rowspan</b> Applied to the table cell containing the item.</li>\r
- * <li><b>colspan</b> Applied to the table cell containing the item.</li>\r
- * <li><b>cellId</b> An id applied to the table cell containing the item.</li>\r
- * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>\r
- * </ul>\r
- * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard\r
- * HTML table. You simply add each panel (or "cell") that you want to include along with any span attributes\r
- * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.\r
- * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the\r
- * total column count in the layoutConfig and start adding panels in their natural order from left to right,\r
- * top to bottom. The layout will automatically figure out, based on the column count, rowspans and colspans,\r
- * how to position each panel within the table. Just like with HTML tables, your rowspans and colspans must add\r
- * up correctly in your overall layout or you'll end up with missing and/or extra cells! Example usage:</p>\r
- * <pre><code>\r
-// This code will generate a layout table that is 3 columns by 2 rows\r
-// with some spanning included. The basic layout will be:\r
-// +--------+-----------------+\r
-// | A | B |\r
-// | |--------+--------|\r
-// | | C | D |\r
-// +--------+--------+--------+\r
-var table = new Ext.Panel({\r
- title: 'Table Layout',\r
- layout:'table',\r
- defaults: {\r
- // applied to each contained panel\r
- bodyStyle:'padding:20px'\r
- },\r
- layoutConfig: {\r
- // The total column count must be specified here\r
- columns: 3\r
- },\r
- items: [{\r
- html: '<p>Cell A content</p>',\r
- rowspan: 2\r
- },{\r
- html: '<p>Cell B content</p>',\r
- colspan: 2\r
- },{\r
- html: '<p>Cell C content</p>',\r
- cellCls: 'highlight'\r
- },{\r
- html: '<p>Cell D content</p>'\r
- }]\r
-});\r
-</code></pre>\r
- */\r
-Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
- /**\r
- * @cfg {Number} columns\r
- * The total number of columns to create in the table for this layout. If not specified, all Components added to\r
- * this layout will be rendered into a single row using one column per Component.\r
- */\r
-\r
- // private\r
- monitorResize:false,\r
-\r
- type: 'table',\r
-\r
- targetCls: 'x-table-layout-ct',\r
-\r
- /**\r
- * @cfg {Object} tableAttrs\r
- * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification\r
- * used to create the layout's <tt><table></tt> element. Example:</p><pre><code>\r
-{\r
- xtype: 'panel',\r
- layout: 'table',\r
- layoutConfig: {\r
- tableAttrs: {\r
- style: {\r
- width: '100%'\r
- }\r
- },\r
- columns: 3\r
- }\r
-}</code></pre>\r
- */\r
- tableAttrs:null,\r
-\r
- // private\r
- setContainer : function(ct){\r
- Ext.layout.TableLayout.superclass.setContainer.call(this, ct);\r
-\r
- this.currentRow = 0;\r
- this.currentColumn = 0;\r
- this.cells = [];\r
- },\r
- \r
- // private\r
- onLayout : function(ct, target){\r
- var cs = ct.items.items, len = cs.length, c, i;\r
-\r
- if(!this.table){\r
- target.addClass('x-table-layout-ct');\r
-\r
- this.table = target.createChild(\r
- Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);\r
- }\r
- this.renderAll(ct, target);\r
- },\r
-\r
- // private\r
- getRow : function(index){\r
- var row = this.table.tBodies[0].childNodes[index];\r
- if(!row){\r
- row = document.createElement('tr');\r
- this.table.tBodies[0].appendChild(row);\r
- }\r
- return row;\r
- },\r
-\r
- // private\r
- getNextCell : function(c){\r
- var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);\r
- var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];\r
- for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){\r
- if(!this.cells[rowIndex]){\r
- this.cells[rowIndex] = [];\r
- }\r
- for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){\r
- this.cells[rowIndex][colIndex] = true;\r
- }\r
- }\r
- var td = document.createElement('td');\r
- if(c.cellId){\r
- td.id = c.cellId;\r
- }\r
- var cls = 'x-table-layout-cell';\r
- if(c.cellCls){\r
- cls += ' ' + c.cellCls;\r
- }\r
- td.className = cls;\r
- if(c.colspan){\r
- td.colSpan = c.colspan;\r
- }\r
- if(c.rowspan){\r
- td.rowSpan = c.rowspan;\r
- }\r
- this.getRow(curRow).appendChild(td);\r
- return td;\r
- },\r
-\r
- // private\r
- getNextNonSpan: function(colIndex, rowIndex){\r
- var cols = this.columns;\r
- while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {\r
- if(cols && colIndex >= cols){\r
- rowIndex++;\r
- colIndex = 0;\r
- }else{\r
- colIndex++;\r
- }\r
- }\r
- return [colIndex, rowIndex];\r
- },\r
-\r
- // private\r
- renderItem : function(c, position, target){\r
- // Ensure we have our inner table to get cells to render into.\r
- if(!this.table){\r
- this.table = target.createChild(\r
- Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);\r
- }\r
- if(c && !c.rendered){\r
- c.render(this.getNextCell(c));\r
- this.configureItem(c, position);\r
- }else if(c && !this.isValidParent(c, target)){\r
- var container = this.getNextCell(c);\r
- container.insertBefore(c.getPositionEl().dom, null);\r
- c.container = Ext.get(container);\r
- this.configureItem(c, position);\r
- }\r
- },\r
-\r
- // private\r
- isValidParent : function(c, target){\r
- return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);\r
- }\r
-\r
- /**\r
- * @property activeItem\r
- * @hide\r
- */\r
-});\r
-\r
-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>
+/**
+ * @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 form = new Ext.form.FormPanel({
- title: 'Absolute Layout',
- layout:'absolute',
+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
- extraCls: 'x-abs-layout-item',
+ titleCollapse: false,
+ animate: true,
+ activeOnTop: true
},
- baseCls: 'x-plain',
- url:'save-form.php',
- defaultType: 'textfield',
items: [{
- x: 0,
- y: 5,
- xtype:'label',
- text: 'Send To:'
+ title: 'Panel 1',
+ html: '<p>Panel content!</p>'
},{
- x: 60,
+ 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, position);
+ }else if(c && !this.isValidParent(c, target)){
+ var container = this.getNextCell(c);
+ container.insertBefore(c.getPositionEl().dom, null);
+ c.container = Ext.get(container);
+ this.configureItem(c, position);
+ }
+ },
+
+ // private
+ isValidParent : function(c, target){
+ return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);
+ }
+
+ /**
+ * @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
extraCls: 'x-abs-layout-item',
- type: 'anchor',
+ type: 'absolute',
onLayout : function(ct, target){
target.position();
constructor : function(config){
Ext.layout.BoxLayout.superclass.constructor.call(this, config);
- if(Ext.isString(this.defaultMargins)){
+
+ if (Ext.isString(this.defaultMargins)) {
this.defaultMargins = this.parseMargins(this.defaultMargins);
}
},
+ /**
+ * @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 items = this.getVisibleItems(container),
+ tSize = this.getLayoutTargetSize();
+
+ /**
+ * @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 = this.calculateChildBoxes(items, tSize);
+
+ this.updateInnerCtSize(tSize, this.childBoxCache);
+ this.updateChildBoxes(this.childBoxCache.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: Ext.emptyFn,
+
+ /**
+ * @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){
+ items.push(c);
+ }
+ }
+
+ return items;
+ },
+
// private
renderAll : function(ct, target){
if(!this.innerCt){
Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
},
- onLayout : function(ct, target){
- this.renderAll(ct, target);
- },
-
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');
}
*/
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
* 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
* <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
*/
- // private
- onLayout : function(ct, target){
- Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target);
-
- var cs = this.getRenderedItems(ct), csLen = cs.length,
- c, i, cm, ch, margin, cl, diff, aw, availHeight,
- size = this.getLayoutTargetSize(),
- w = size.width,
- h = size.height - this.scrollOffset,
- l = this.padding.left,
- t = this.padding.top,
- isStart = this.pack == 'start',
- extraHeight = 0,
- maxWidth = 0,
- totalFlex = 0,
- usedHeight = 0,
- idx = 0,
- heights = [],
- restore = [];
-
- // Do only width calculations and apply those first, as they can affect height
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- cm = c.margins;
- margin = cm.top + cm.bottom;
- // Max height for align
- maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right);
- }
+ /**
+ * @private
+ * See parent documentation
+ */
+ updateInnerCtSize: function(tSize, calcs) {
+ var innerCtHeight = tSize.height,
+ innerCtWidth = calcs.meta.maxWidth + this.padding.left + this.padding.right;
- var innerCtWidth = maxWidth + this.padding.left + this.padding.right;
- switch(this.align){
- case 'stretch':
- this.innerCt.setSize(w, h);
- break;
- case 'stretchmax':
- case 'left':
- this.innerCt.setSize(innerCtWidth, h);
- break;
- case 'center':
- this.innerCt.setSize(w = Math.max(w, innerCtWidth), h);
- break;
+ if (this.align == 'stretch') {
+ innerCtWidth = tSize.width;
+ } else if (this.align == 'center') {
+ innerCtWidth = Math.max(tSize.width, innerCtWidth);
}
- var availableWidth = Math.max(0, w - this.padding.left - this.padding.right);
- // Apply widths
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- cm = c.margins;
- if(this.align == 'stretch'){
- c.setWidth(((w - (this.padding.left + this.padding.right)) - (cm.left + cm.right)).constrain(
- c.minWidth || 0, c.maxWidth || 1000000));
- }else if(this.align == 'stretchmax'){
- c.setWidth((maxWidth - (cm.left + cm.right)).constrain(
- c.minWidth || 0, c.maxWidth || 1000000));
- }else if(isStart && c.flex){
- c.setWidth();
- }
-
- }
+ //we set the innerCt size first because if our child items are larger than the previous innerCt size
+ //the browser will insert scrollbars and then remove them again immediately afterwards
+ this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
+ },
- // Height calculations
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- // Total of all the flex values
- totalFlex += c.flex || 0;
- // Don't run height calculations on flexed items
- if (!c.flex) {
- // Render and layout sub-containers without a flex or height, once
- if (!c.height && !c.hasLayout && c.doLayout) {
- c.doLayout();
+ /**
+ * @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,
+
+ //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, flexedHeight, horizMargins, 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 && Ext.isFunction(child.doLayout);
+
+
+ // Static height (numeric) requires no calcs
+ if (!Ext.isNumber(childHeight)) {
+
+ // 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;
+ }
}
- ch = c.getHeight();
- } else {
- ch = 0;
+
+ childMargins = child.margins;
+
+ nonFlexHeight += (childHeight || 0) + childMargins.top + childMargins.bottom;
+
+ // Max width for align - force layout of non-layed out subcontainers without a numeric width
+ if (!Ext.isNumber(childWidth)) {
+ 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
+ });
}
- cm = c.margins;
- // Determine how much height is available to flex
- extraHeight += ch + cm.top + cm.bottom;
- }
- // Final avail height calc
- availHeight = Math.max(0, (h - extraHeight - this.padding.top - this.padding.bottom));
+ //the height available to the flexed items
+ var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
- var leftOver = availHeight;
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- if(isStart && c.flex){
- ch = Math.floor(availHeight * (c.flex / totalFlex));
- leftOver -= ch;
- heights.push(ch);
+ if (isCenter) {
+ topOffset += availableHeight / 2;
+ } else if (isEnd) {
+ topOffset += availableHeight;
}
- }
- if(this.pack == 'center'){
- t += availHeight ? availHeight / 2 : 0;
- }else if(this.pack == 'end'){
- t += availHeight;
- }
- idx = 0;
- // Apply heights
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- cm = c.margins;
- t += cm.top;
- aw = availableWidth;
- cl = l + cm.left // default left pos
-
- // Adjust left pos for centering
- if(this.align == 'center'){
- if((diff = availableWidth - (c.getWidth() + cm.left + cm.right)) > 0){
- cl += (diff/2);
- aw -= diff;
+
+ //temporary variables used in the flex height calculations below
+ var remainingHeight = availableHeight,
+ remainingFlex = totalFlex;
+
+ //calculate the height of each flexed item, and the left + top positions of every item
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ calcs = boxes[i];
+
+ childMargins = child.margins;
+ horizMargins = childMargins.left + childMargins.right;
+
+ topOffset += childMargins.top;
+
+ if (isStart && child.flex && !child.height) {
+ flexedHeight = Math.ceil((child.flex / remainingFlex) * remainingHeight);
+ remainingHeight -= flexedHeight;
+ remainingFlex -= child.flex;
+
+ calcs.height = flexedHeight;
+ calcs.dirtySize = true;
}
- }
- c.setPosition(cl, t);
- if(isStart && c.flex){
- ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0));
- c.setSize(aw, ch);
- }else{
- ch = c.getHeight();
+ 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;
}
- t += ch + cm.bottom;
- }
- // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
- if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
- var ts = this.getLayoutTargetSize();
- if (ts.width != size.width || ts.height != size.height){
- this.adjustmentPass = true;
- this.onLayout(ct, target);
+
+ return {
+ boxes: boxes,
+ meta : {
+ maxWidth: maxWidth
}
- }
- delete this.adjustmentPass;
+ };
}
});
* <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',
+ align: 'top', // top, middle, stretch, strechmax
+
+ type : 'hbox',
+
+ /**
+ * @private
+ * See parent documentation
+ */
+ updateInnerCtSize: function(tSize, calcs) {
+ var innerCtWidth = tSize.width,
+ innerCtHeight = calcs.meta.maxHeight + this.padding.top + this.padding.bottom;
+
+ if (this.align == 'stretch') {
+ innerCtHeight = tSize.height;
+ } else if (this.align == 'middle') {
+ innerCtHeight = Math.max(tSize.height, innerCtHeight);
+ }
+
+ this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
+ },
+
/**
* @cfg {String} pack
* Controls how the child items of the container are packed together. Acceptable configuration values
* <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
*/
- // private
- onLayout : function(ct, target){
- Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target);
+ /**
+ * @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',
+ // isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
+
+ nonFlexWidth = 0,
+ maxHeight = 0,
+ totalFlex = 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, 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 && Ext.isFunction(child.doLayout);
+
+ // Static width (numeric) requires no calcs
+ if (!Ext.isNumber(childWidth)) {
+
+ // 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();
+ }
- var cs = this.getRenderedItems(ct), csLen = cs.length,
- c, i, cm, cw, ch, diff, availWidth,
- size = this.getLayoutTargetSize(),
- w = size.width - this.scrollOffset,
- h = size.height,
- l = this.padding.left,
- t = this.padding.top,
- isStart = this.pack == 'start',
- isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
- extraWidth = 0,
- maxHeight = 0,
- totalFlex = 0,
- usedWidth = 0;
-
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- // Total of all the flex values
- totalFlex += c.flex || 0;
- // Don't run width calculations on flexed items
- if (!c.flex) {
- // Render and layout sub-containers without a flex or width, once
- if (!c.width && !c.hasLayout && c.doLayout) {
- c.doLayout();
+ childSize = child.getSize();
+ childWidth = childSize.width;
+ childHeight = childSize.height;
+ }
}
- cw = c.getWidth();
- } else {
- cw = 0;
- }
- cm = c.margins;
- // Determine how much width is available to flex
- extraWidth += cw + cm.left + cm.right;
- // Max height for align
- maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom);
- }
- // Final avail width calc
- availWidth = Math.max(0, (w - extraWidth - this.padding.left - this.padding.right));
- var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom;
- switch(this.align){
- case 'stretch':
- this.innerCt.setSize(w, h);
- break;
- case 'stretchmax':
- case 'top':
- this.innerCt.setSize(w, innerCtHeight);
- break;
- case 'middle':
- this.innerCt.setSize(w, h = Math.max(h, innerCtHeight));
- break;
- }
+ childMargins = child.margins;
- var leftOver = availWidth,
- widths = [],
- restore = [],
- idx = 0,
- availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom);
+ nonFlexWidth += (childWidth || 0) + childMargins.left + childMargins.right;
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- if(isStart && c.flex){
- cw = Math.floor(availWidth * (c.flex / totalFlex));
- leftOver -= cw;
- widths.push(cw);
+ // Max height for align - force layout of non-layed out subcontainers without a numeric height
+ if (!Ext.isNumber(childHeight)) {
+ if (canLayout) {
+ child.doLayout();
+ }
+ childHeight = child.getHeight();
+ }
+
+ maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
+
+ //cache the size of each child component
+ boxes.push({
+ component: child,
+ height : childHeight || undefined,
+ width : childWidth || undefined
+ });
}
- }
- if(this.pack == 'center'){
- l += availWidth ? availWidth / 2 : 0;
- }else if(this.pack == 'end'){
- l += availWidth;
- }
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- cm = c.margins;
- l += cm.left;
- c.setPosition(l, t + cm.top);
- if(isStart && c.flex){
- cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0));
- if(isRestore){
- restore.push(c.getHeight());
- }
- c.setSize(cw, availableHeight);
- }else{
- cw = c.getWidth();
+ //the width available to the flexed items
+ var availableWidth = Math.max(0, (width - nonFlexWidth - paddingHoriz));
+
+ if (isCenter) {
+ leftOffset += availableWidth / 2;
+ } else if (isEnd) {
+ leftOffset += availableWidth;
}
- l += cw + cm.right;
- }
- idx = 0;
- for (i = 0 ; i < csLen; i++) {
- c = cs[i];
- cm = c.margins;
- ch = c.getHeight();
- if(isStart && c.flex){
- ch = restore[idx++];
- }
- if(this.align == 'stretch'){
- c.setHeight(((h - (this.padding.top + this.padding.bottom)) - (cm.top + cm.bottom)).constrain(
- c.minHeight || 0, c.maxHeight || 1000000));
- }else if(this.align == 'stretchmax'){
- c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain(
- c.minHeight || 0, c.maxHeight || 1000000));
- }else{
- if(this.align == 'middle'){
- diff = availableHeight - (ch + cm.top + cm.bottom);
- ch = t + cm.top + (diff/2);
- if(diff > 0){
- c.setPosition(c.x, ch);
- }
+ //temporary variables used in the flex width calculations below
+ var remainingWidth = availableWidth,
+ remainingFlex = totalFlex;
+
+ //calculate the widths of each flexed item, and the left + top positions of every item
+ for (i = 0; i < visibleCount; i++) {
+ child = visibleItems[i];
+ calcs = boxes[i];
+
+ childMargins = child.margins;
+ vertMargins = childMargins.top + childMargins.bottom;
+
+ leftOffset += childMargins.left;
+
+ 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(isStart && c.flex){
- c.setHeight(ch);
+
+ 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;
}
- }
- // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
- if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
- var ts = this.getLayoutTargetSize();
- if (ts.width != size.width || ts.height != size.height){
- this.adjustmentPass = true;
- this.onLayout(ct, target);
+
+ return {
+ boxes: boxes,
+ meta : {
+ maxHeight: maxHeight
}
- }
- delete this.adjustmentPass;
+ };
}
});
/**
* @class Ext.layout.ToolbarLayout
* @extends Ext.layout.ContainerLayout
- * Layout manager implicitly used by Ext.Toolbar.
+ * 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,
- triggerWidth : 18,
- lastOverflow : false,
+ 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
- onLayout : function(ct, target){
- if(!this.leftTr){
+ /**
+ * @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',
- '<table cellspacing="0" class="x-toolbar-ct"><tbody><tr><td class="x-toolbar-left" align="' + align + '"><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>');
- this.leftTr = target.child('tr.x-toolbar-left-row', true);
- this.rightTr = target.child('tr.x-toolbar-right-row', true);
+ 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,
- pos = 0,
- items = ct.items.items;
+ var side = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
+ items = ct.items.items,
+ position = 0;
- for(var i = 0, len = items.length, c; i < len; i++, pos++) {
+ //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;
- pos = -1;
- }else if(!c.rendered){
- c.render(this.insertCell(c, side, pos));
- }else{
- if(!c.xtbHidden && !this.isValidParent(c, side.childNodes[pos])){
- var td = this.insertCell(c, side, pos);
+
+ if (c.isFill) {
+ side = this.rightTr;
+ position = -1;
+ } else if (!c.rendered) {
+ c.render(this.insertCell(c, side, position));
+ } 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.fitToSize(target);
},
- cleanup : function(row){
- var cn = row.childNodes, i, c;
- for(i = cn.length-1; i >= 0 && (c = cn[i]); i--){
- if(!c.firstChild){
- row.removeChild(c);
+ /**
+ * @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);
}
}
},
- insertCell : function(c, side, pos){
+ /**
+ * @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';
- side.insertBefore(td, side.childNodes[pos]||null);
+ td.className = 'x-toolbar-cell';
+
+ target.insertBefore(td, target.childNodes[position] || null);
+
return td;
},
- hideItem : function(item){
- var h = (this.hiddens = this.hiddens || []);
- h.push(item);
+ /**
+ * @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();
},
- unhideItem : function(item){
+ /**
+ * @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.hiddens.remove(item);
- if(this.hiddens.length < 1){
- delete this.hiddens;
- }
+ this.hiddenItems.remove(item);
},
- getItemWidth : function(c){
+ /**
+ * @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;
},
- fitToSize : function(t){
- if(this.container.enableOverflow === false){
+ /**
+ * @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 w = t.dom.clientWidth,
- lw = this.lastWidth || 0,
- iw = t.dom.firstChild.offsetWidth,
- clipWidth = w - this.triggerWidth,
- hideIndex = -1;
- this.lastWidth = w;
+ var width = target.dom.clientWidth,
+ tableWidth = target.dom.firstChild.offsetWidth,
+ clipWidth = width - this.triggerWidth,
+ lastWidth = this.lastWidth || 0,
- if(iw > w || (this.hiddens && w >= lw)){
- var i, items = this.container.items.items,
- len = items.length, c,
- loopWidth = 0;
+ hiddenItems = this.hiddenItems,
+ hasHiddens = hiddenItems.length != 0,
+ isLarger = width >= lastWidth;
- for(i = 0; i < len; i++) {
- c = items[i];
- if(!c.isFill){
- loopWidth += this.getItemWidth(c);
- if(loopWidth > clipWidth){
- if(!(c.hidden || c.xtbHidden)){
- this.hideItem(c);
+ 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(c.xtbHidden){
- this.unhideItem(c);
+ } else if (item.xtbHidden) {
+ this.unhideItem(item);
}
}
}
}
- if(this.hiddens){
+
+ //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){
+
+ if (!this.lastOverflow) {
this.container.fireEvent('overflowchange', this.container, true);
this.lastOverflow = true;
}
- }else if(this.more){
+ } else if (this.more) {
this.clearMenu();
this.more.destroy();
delete this.more;
- if(this.lastOverflow){
+
+ if (this.lastOverflow) {
this.container.fireEvent('overflowchange', this.container, false);
this.lastOverflow = false;
}
}
},
- createMenuConfig : function(c, hideOnClick){
- var cfg = Ext.apply({}, c.initialConfig),
- group = c.toggleGroup;
+ /**
+ * @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(cfg, {
- text: c.overflowText || c.text,
- iconCls: c.iconCls,
- icon: c.icon,
- itemId: c.itemId,
- disabled: c.disabled,
- handler: c.handler,
- scope: c.scope,
- menu: c.menu,
+ Ext.apply(config, {
+ text : component.overflowText || component.text,
hideOnClick: hideOnClick
});
- if(group || c.enableToggle){
- Ext.apply(cfg, {
- group: group,
- checked: c.pressed,
+
+ if (group || component.enableToggle) {
+ Ext.apply(config, {
+ group : group,
+ checked: component.pressed,
listeners: {
checkchange: function(item, checked){
- c.toggle(checked);
+ component.toggle(checked);
}
}
});
}
- delete cfg.ownerCt;
- delete cfg.xtype;
- delete cfg.id;
- return cfg;
+
+ delete config.ownerCt;
+ delete config.xtype;
+ delete config.id;
+
+ return config;
},
- // private
- addComponentToMenu : function(m, c){
- if(c instanceof Ext.Toolbar.Separator){
- m.add('-');
- }else if(Ext.isFunction(c.isXType)){
- if(c.isXType('splitbutton')){
- m.add(this.createMenuConfig(c, true));
- }else if(c.isXType('button')){
- m.add(this.createMenuConfig(c, !c.menu));
- }else if(c.isXType('buttongroup')){
- c.items.each(function(item){
- this.addComponentToMenu(m, item);
+ /**
+ * @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 m = this.moreMenu;
- if(m && m.items){
- m.items.each(function(item){
+ var menu = this.moreMenu;
+ if (menu && menu.items) {
+ menu.items.each(function(item){
delete item.menu;
});
}
},
- // private
- beforeMoreShow : function(m){
- var h = this.container.items.items,
- len = h.length,
- c,
- prev,
- needsSep = function(group, item){
- return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
- };
+ /**
+ * @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();
- m.removeAll();
- for(var i = 0; i < len; i++){
- c = h[i];
- if(c.xtbHidden){
- if(prev && (needsSep(c, prev) || needsSep(prev, c))){
- m.add('-');
+ 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(m, c);
- prev = c;
+ this.addComponentToMenu(menu, item);
+ prev = item;
}
}
- // put something so the menu isn't empty
- // if no compatible items found
- if(m.items.length < 1){
- m.add(this.noItemsMenuText);
+
+ // 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){
+ 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
+ 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);
}
}
});
-Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;/**
+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
}
}
});
-Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;/**\r
- * @class Ext.Viewport\r
- * @extends Ext.Container\r
- * <p>A specialized container representing the viewable application area (the browser viewport).</p>\r
- * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of\r
- * the browser viewport and manages window resizing. There may only be one Viewport created\r
- * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s\r
- * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}\r
- * method of any of its child Panels may themselves have a layout.</p>\r
- * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide\r
- * for scrolling if needed using the {@link #autoScroll} config.</p>\r
- * <p>An example showing a classic application border layout:</p><pre><code>\r
-new Ext.Viewport({\r
- layout: 'border',\r
- items: [{\r
- region: 'north',\r
- html: '<h1 class="x-panel-header">Page Title</h1>',\r
- autoHeight: true,\r
- border: false,\r
- margins: '0 0 5 0'\r
- }, {\r
- region: 'west',\r
- collapsible: true,\r
- title: 'Navigation',\r
- width: 200\r
- // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout}\r
- }, {\r
- region: 'south',\r
- title: 'Title for Panel',\r
- collapsible: true,\r
- html: 'Information goes here',\r
- split: true,\r
- height: 100,\r
- minHeight: 100\r
- }, {\r
- region: 'east',\r
- title: 'Title for the Grid Panel',\r
- collapsible: true,\r
- split: true,\r
- width: 200,\r
- xtype: 'grid',\r
- // remaining grid configuration not shown ...\r
- // notice that the GridPanel is added directly as the region\r
- // it is not "overnested" inside another Panel\r
- }, {\r
- region: 'center',\r
- xtype: 'tabpanel', // TabPanel itself has no title\r
- items: {\r
- title: 'Default Tab',\r
- html: 'The first tab\'s content. Others may be added dynamically'\r
- }\r
- }]\r
-});\r
-</code></pre>\r
- * @constructor\r
- * Create a new Viewport\r
- * @param {Object} config The config object\r
- * @xtype viewport\r
- */\r
-Ext.Viewport = Ext.extend(Ext.Container, {\r
- /*\r
- * Privatize config options which, if used, would interfere with the\r
- * correct operation of the Viewport as the sole manager of the\r
- * layout of the document body.\r
- */\r
- /**\r
- * @cfg {Mixed} applyTo @hide\r
- */\r
- /**\r
- * @cfg {Boolean} allowDomMove @hide\r
- */\r
- /**\r
- * @cfg {Boolean} hideParent @hide\r
- */\r
- /**\r
- * @cfg {Mixed} renderTo @hide\r
- */\r
- /**\r
- * @cfg {Boolean} hideParent @hide\r
- */\r
- /**\r
- * @cfg {Number} height @hide\r
- */\r
- /**\r
- * @cfg {Number} width @hide\r
- */\r
- /**\r
- * @cfg {Boolean} autoHeight @hide\r
- */\r
- /**\r
- * @cfg {Boolean} autoWidth @hide\r
- */\r
- /**\r
- * @cfg {Boolean} deferHeight @hide\r
- */\r
- /**\r
- * @cfg {Boolean} monitorResize @hide\r
- */\r
-\r
- initComponent : function() {\r
- Ext.Viewport.superclass.initComponent.call(this);\r
- document.getElementsByTagName('html')[0].className += ' x-viewport';\r
- this.el = Ext.getBody();\r
- this.el.setHeight = Ext.emptyFn;\r
- this.el.setWidth = Ext.emptyFn;\r
- this.el.setSize = Ext.emptyFn;\r
- this.el.dom.scroll = 'no';\r
- this.allowDomMove = false;\r
- this.autoWidth = true;\r
- this.autoHeight = true;\r
- Ext.EventManager.onWindowResize(this.fireResize, this);\r
- this.renderTo = this.el;\r
- },\r
-\r
- fireResize : function(w, h){\r
- this.fireEvent('resize', this, w, h, w, h);\r
- }\r
-});\r
-Ext.reg('viewport', Ext.Viewport);\r
+Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;
/**
- * @class Ext.Panel
+ * @class Ext.Viewport
* @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>
+ * <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 panel
+ * @xtype viewport
*/
-Ext.Panel = Ext.extend(Ext.Container, {
+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.
+ */
/**
- * 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
+ * @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.
if(this.tbar){
this.elements += ',tbar';
this.topToolbar = this.createToolbar(this.tbar);
- delete this.tbar;
+ this.tbar = null;
}
if(this.bbar){
this.elements += ',bbar';
this.bottomToolbar = this.createToolbar(this.bbar);
- delete this.bbar;
+ this.bbar = null;
}
if(this.header === true){
this.elements += ',header';
- delete this.header;
+ this.header = null;
}else if(this.headerCfg || (this.title && this.header !== false)){
this.elements += ',header';
}
if(this.footerCfg || this.footer === true){
this.elements += ',footer';
- delete this.footer;
+ this.footer = null;
}
if(this.buttons){
this.fbar = this.buttons;
- delete this.buttons;
+ this.buttons = null;
}
if(this.fbar){
this.createFbar(this.fbar);
};
}
});
- //@compat addButton and buttons could possibly be removed
- //@target 4.0
+ // @compat addButton and buttons could possibly be removed
+ // @target 4.0
/**
* This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
* config property. Read only.
if(img){
Ext.fly(img).replaceClass(old, this.iconCls);
}else{
- Ext.DomHelper.insertBefore(hd.dom.firstChild, {
- tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
- });
+ var hdspan = hd.child('span.' + this.headerTextCls);
+ if (hdspan) {
+ Ext.DomHelper.insertBefore(hdspan.dom, {
+ tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
+ });
+ }
}
}
}
return this.bottomToolbar;
},
+ /**
+ * Returns the {@link Ext.Toolbar toolbar} from the footer (<code>{@link #fbar}</code>) section of the panel.
+ * @return {Ext.Toolbar} The toolbar
+ */
+ getFooterToolbar : function() {
+ return this.fbar;
+ },
+
/**
* Adds a button to this panel. Note that this method must be called prior to rendering. The preferred
* approach is to add buttons via the {@link #buttons} config.
config = Ext.apply({
handler: handler,
scope: scope
- }, config)
+ }, config);
}
return this.fbar.add(config);
},
this.tools = [];
}
Ext.each(arguments, function(arg){
- this.tools.push(arg)
+ this.tools.push(arg);
}, this);
return;
}
if(h != this.getToolbarHeight()){
- h = Math.max(0, this.adjustBodyHeight(lsh - this.getFrameHeight()));
+ h = Math.max(0, lsh - this.getFrameHeight());
bd.setHeight(h);
sz = bd.getSize();
this.toolbarHeight = this.getToolbarHeight();
// private
afterEffect : function(anim){
this.syncShadow();
- if(anim !== false){
- this.el.removeClass('x-panel-animated');
- }
+ this.el.removeClass('x-panel-animated');
},
// private - wraps up an animation param with internal callbacks
Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
this.collapseDefaults));
}else{
- this[this.collapseEl].hide();
+ this[this.collapseEl].hide(this.hideMode);
this.afterCollapse(false);
}
},
afterCollapse : function(anim){
this.collapsed = true;
this.el.addClass(this.collapsedCls);
+ if(anim !== false){
+ this[this.collapseEl].hide(this.hideMode);
+ }
this.afterEffect(anim);
+
+ // Reset lastSize of all sub-components so they KNOW they are in a collapsed container
+ this.cascade(function(c) {
+ if (c.lastSize) {
+ c.lastSize = { width: undefined, height: undefined };
+ }
+ });
this.fireEvent('collapse', this);
},
Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
this.expandDefaults));
}else{
- this[this.collapseEl].show();
+ this[this.collapseEl].show(this.hideMode);
this.afterExpand(false);
}
},
// private
afterExpand : function(anim){
this.collapsed = false;
+ if(anim !== false){
+ this[this.collapseEl].show(this.hideMode);
+ }
this.afterEffect(anim);
if (this.deferLayout) {
delete this.deferLayout;
},
// private
- onResize : function(w, h){
+ onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
+ var w = adjWidth,
+ h = adjHeight;
+
if(Ext.isDefined(w) || Ext.isDefined(h)){
if(!this.collapsed){
// First, set the the Panel's body width.
// At this point, the Toolbars must be layed out for getFrameHeight to find a result.
if(Ext.isNumber(h)){
- h = Math.max(0, this.adjustBodyHeight(h - this.getFrameHeight()));
+ h = Math.max(0, h - this.getFrameHeight());
+ //h = Math.max(0, h - (this.getHeight() - this.body.getHeight()));
this.body.setHeight(h);
}else if(h == 'auto'){
this.body.setHeight(h);
this.onBodyResize(w, h);
}
this.syncShadow();
- Ext.Panel.superclass.onResize.call(this);
+ Ext.Panel.superclass.onResize.call(this, adjWidth, adjHeight, rawWidth, rawHeight);
+
},
// private
return h;
},
- // private
+ // deprecate
adjustBodyHeight : function(h){
return h;
},
* header and footer elements, but not including the body height). To retrieve the body height see {@link #getInnerHeight}.
* @return {Number} The frame height
*/
- getFrameHeight : function(){
- var h = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
- h += (this.tbar ? this.tbar.getHeight() : 0) +
- (this.bbar ? this.bbar.getHeight() : 0);
+ getFrameHeight : function() {
+ var h = Math.max(0, this.getHeight() - this.body.getHeight());
- if(this.frame){
- h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
- }else{
- h += (this.header ? this.header.getHeight() : 0) +
- (this.footer ? this.footer.getHeight() : 0);
+ if (isNaN(h)) {
+ h = 0;
}
return h;
+
+ /* Deprecate
+ var h = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
+ h += (this.tbar ? this.tbar.getHeight() : 0) +
+ (this.bbar ? this.bbar.getHeight() : 0);
+
+ if(this.frame){
+ h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
+ }else{
+ h += (this.header ? this.header.getHeight() : 0) +
+ (this.footer ? this.footer.getHeight() : 0);
+ }
+ return h;
+ */
},
/**
* @return {Number} The body height
*/
getInnerHeight : function(){
- return this.getSize().height - this.getFrameHeight();
+ return this.body.getHeight();
+ /* Deprecate
+ return this.getSize().height - this.getFrameHeight();
+ */
},
// private
this.ft,
this.header,
this.footer,
- this.toolbars,
this.tbar,
this.bbar,
this.body,
this.mc,
- this.bwrap
+ this.bwrap,
+ this.dd
);
if (this.fbar) {
Ext.destroy(
this.fbar.el
);
}
- }else{
- Ext.destroy(
- this.topToolbar,
- this.bottomToolbar
- );
}
+ Ext.destroy(this.toolbars);
},
// private
/**
* @cfg {Boolean} allowBlur
* True to {@link #completeEdit complete the editing process} if in edit mode when the
- * field is blurred. Defaults to <tt>false</tt>.
+ * field is blurred. Defaults to <tt>true</tt>.
*/
+ allowBlur: true,
/**
* @cfg {Boolean/String} autoSize
* True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
if(!this.editing){
return;
}
+ // Assert combo values first
+ if (this.field.assertValue) {
+ this.field.assertValue();
+ }
var v = this.getValue();
if(!this.field.isValid()){
if(this.revertInvalid !== false){
// private
onBlur : function(){
// selectSameEditor flag allows the same editor to be started without onBlur firing on itself
- if(this.allowBlur !== true && this.editing && this.selectSameEditor !== true){
+ if(this.allowBlur === true && this.editing && this.selectSameEditor !== true){
this.completeEdit();
}
},
if(this.value){
var s = this.value;
this.value = null;
- this.select(s);
+ this.select(s, true);
}
},
/**
* Selects the specified color in the palette (fires the {@link #select} event)
* @param {String} color A valid 6-digit color hex code (# will be stripped if included)
+ * @param {Boolean} suppressEvent (optional) True to stop the select event from firing. Defaults to <tt>false</tt>.
*/
- select : function(color){
+ select : function(color, suppressEvent){
color = color.replace('#', '');
if(color != this.value || this.allowReselect){
var el = this.el;
}
el.child('a.color-'+color).addClass('x-color-palette-sel');
this.value = color;
- this.fireEvent('select', this, color);
+ if(suppressEvent !== true){
+ this.fireEvent('select', this, color);
+ }
}
}
* @cfg {String} autoEl @hide
*/
});
-Ext.reg('colorpalette', Ext.ColorPalette);
-/**
+Ext.reg('colorpalette', Ext.ColorPalette);/**
* @class Ext.DatePicker
* @extends Ext.Component
* <p>A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
* @cfg {Object} scope
* The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
* function will be called. Defaults to this DatePicker instance.
- */
+ */
/**
* @cfg {String} todayTip
* A string used to format the message for displaying in a tooltip over the button that
* In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
* escape the dot when restricting dates. For example: ['03\\.08\\.03'].
*/
-
+
// private
// Set by other components to stop the picker focus being updated when the value changes.
focusOnSelect: true,
+ // default value used to initialise each date in the DatePicker
+ // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
+ initHour: 12, // 24-hour format
+
// private
initComponent : function(){
Ext.DatePicker.superclass.initComponent.call(this);
var dd = this.disabledDates,
len = dd.length - 1,
re = '(?:';
-
+
Ext.each(dd, function(d, i){
re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
if(i != len){
focus : function(){
this.update(this.activeDate);
},
-
+
// private
onEnable: function(initial){
- Ext.DatePicker.superclass.onEnable.call(this);
+ Ext.DatePicker.superclass.onEnable.call(this);
this.doDisabled(false);
this.update(initial ? this.value : this.activeDate);
if(Ext.isIE){
this.el.repaint();
}
-
+
},
-
+
// private
onDisable : function(){
- Ext.DatePicker.superclass.onDisable.call(this);
+ Ext.DatePicker.superclass.onDisable.call(this);
this.doDisabled(true);
if(Ext.isIE && !Ext.isIE8){
/* Really strange problem in IE6/7, when disabled, have to explicitly
});
}
},
-
+
// private
doDisabled : function(disabled){
this.keyNav.setDisabled(disabled);
if(e.ctrlKey){
this.showPrevMonth();
}else{
- this.update(this.activeDate.add('d', -1));
+ this.update(this.activeDate.add('d', -1));
}
},
if(e.ctrlKey){
this.showNextMonth();
}else{
- this.update(this.activeDate.add('d', 1));
+ this.update(this.activeDate.add('d', 1));
}
},
// private
update : function(date, forceRefresh){
if(this.rendered){
- var vd = this.activeDate, vis = this.isVisible();
- this.activeDate = date;
- if(!forceRefresh && vd && this.el){
- var t = date.getTime();
- if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
- this.cells.removeClass('x-date-selected');
- this.cells.each(function(c){
- if(c.dom.firstChild.dateValue == t){
- c.addClass('x-date-selected');
- if(vis && !this.cancelFocus){
- Ext.fly(c.dom.firstChild).focus(50);
- }
- return false;
- }
- }, this);
- return;
- }
- }
- var days = date.getDaysInMonth(),
- firstOfMonth = date.getFirstDateOfMonth(),
- startingPos = firstOfMonth.getDay()-this.startDay;
-
- if(startingPos < 0){
- startingPos += 7;
- }
- days += startingPos;
-
- var pm = date.add('mo', -1),
- prevStart = pm.getDaysInMonth()-startingPos,
- cells = this.cells.elements,
- textEls = this.textNodes,
- // convert everything to numbers so it's fast
- day = 86400000,
- d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime(),
- today = new Date().clearTime().getTime(),
- sel = date.clearTime(true).getTime(),
- min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
- max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
- ddMatch = this.disabledDatesRE,
- ddText = this.disabledDatesText,
- ddays = this.disabledDays ? this.disabledDays.join('') : false,
- ddaysText = this.disabledDaysText,
- format = this.format;
-
- if(this.showToday){
- var td = new Date().clearTime(),
- disable = (td < min || td > max ||
- (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
- (ddays && ddays.indexOf(td.getDay()) != -1));
-
- if(!this.disabled){
- this.todayBtn.setDisabled(disable);
- this.todayKeyListener[disable ? 'disable' : 'enable']();
- }
- }
-
- var setCellClass = function(cal, cell){
- cell.title = '';
- var t = d.getTime();
- cell.firstChild.dateValue = t;
- if(t == today){
- cell.className += ' x-date-today';
- cell.title = cal.todayText;
- }
- if(t == sel){
- cell.className += ' x-date-selected';
- if(vis){
- Ext.fly(cell.firstChild).focus(50);
- }
- }
- // disabling
- if(t < min) {
- cell.className = ' x-date-disabled';
- cell.title = cal.minText;
- return;
- }
- if(t > max) {
- cell.className = ' x-date-disabled';
- cell.title = cal.maxText;
- return;
- }
- if(ddays){
- if(ddays.indexOf(d.getDay()) != -1){
- cell.title = ddaysText;
- cell.className = ' x-date-disabled';
- }
- }
- if(ddMatch && format){
- var fvalue = d.dateFormat(format);
- if(ddMatch.test(fvalue)){
- cell.title = ddText.replace('%0', fvalue);
- cell.className = ' x-date-disabled';
- }
- }
- };
-
- var i = 0;
- for(; i < startingPos; i++) {
- textEls[i].innerHTML = (++prevStart);
- d.setDate(d.getDate()+1);
- cells[i].className = 'x-date-prevday';
- setCellClass(this, cells[i]);
- }
- for(; i < days; i++){
- var intDay = i - startingPos + 1;
- textEls[i].innerHTML = (intDay);
- d.setDate(d.getDate()+1);
- cells[i].className = 'x-date-active';
- setCellClass(this, cells[i]);
- }
- var extraDays = 0;
- for(; i < 42; i++) {
- textEls[i].innerHTML = (++extraDays);
- d.setDate(d.getDate()+1);
- cells[i].className = 'x-date-nextday';
- setCellClass(this, cells[i]);
- }
-
- this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
-
- if(!this.internalRender){
- var main = this.el.dom.firstChild,
- w = main.offsetWidth;
- this.el.setWidth(w + this.el.getBorderWidth('lr'));
- Ext.fly(main).setWidth(w);
- this.internalRender = true;
- // opera does not respect the auto grow header center column
- // then, after it gets a width opera refuses to recalculate
- // without a second pass
- if(Ext.isOpera && !this.secondPass){
- main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
- this.secondPass = true;
- this.update.defer(10, this, [date]);
- }
- }
+ var vd = this.activeDate, vis = this.isVisible();
+ this.activeDate = date;
+ if(!forceRefresh && vd && this.el){
+ var t = date.getTime();
+ if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
+ this.cells.removeClass('x-date-selected');
+ this.cells.each(function(c){
+ if(c.dom.firstChild.dateValue == t){
+ c.addClass('x-date-selected');
+ if(vis && !this.cancelFocus){
+ Ext.fly(c.dom.firstChild).focus(50);
+ }
+ return false;
+ }
+ }, this);
+ return;
+ }
+ }
+ var days = date.getDaysInMonth(),
+ firstOfMonth = date.getFirstDateOfMonth(),
+ startingPos = firstOfMonth.getDay()-this.startDay;
+
+ if(startingPos < 0){
+ startingPos += 7;
+ }
+ days += startingPos;
+
+ var pm = date.add('mo', -1),
+ prevStart = pm.getDaysInMonth()-startingPos,
+ cells = this.cells.elements,
+ textEls = this.textNodes,
+ // convert everything to numbers so it's fast
+ d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart, this.initHour)),
+ today = new Date().clearTime().getTime(),
+ sel = date.clearTime(true).getTime(),
+ min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
+ max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
+ ddMatch = this.disabledDatesRE,
+ ddText = this.disabledDatesText,
+ ddays = this.disabledDays ? this.disabledDays.join('') : false,
+ ddaysText = this.disabledDaysText,
+ format = this.format;
+
+ if(this.showToday){
+ var td = new Date().clearTime(),
+ disable = (td < min || td > max ||
+ (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
+ (ddays && ddays.indexOf(td.getDay()) != -1));
+
+ if(!this.disabled){
+ this.todayBtn.setDisabled(disable);
+ this.todayKeyListener[disable ? 'disable' : 'enable']();
+ }
+ }
+
+ var setCellClass = function(cal, cell){
+ cell.title = '';
+ var t = d.clearTime(true).getTime();
+ cell.firstChild.dateValue = t;
+ if(t == today){
+ cell.className += ' x-date-today';
+ cell.title = cal.todayText;
+ }
+ if(t == sel){
+ cell.className += ' x-date-selected';
+ if(vis){
+ Ext.fly(cell.firstChild).focus(50);
+ }
+ }
+ // disabling
+ if(t < min) {
+ cell.className = ' x-date-disabled';
+ cell.title = cal.minText;
+ return;
+ }
+ if(t > max) {
+ cell.className = ' x-date-disabled';
+ cell.title = cal.maxText;
+ return;
+ }
+ if(ddays){
+ if(ddays.indexOf(d.getDay()) != -1){
+ cell.title = ddaysText;
+ cell.className = ' x-date-disabled';
+ }
+ }
+ if(ddMatch && format){
+ var fvalue = d.dateFormat(format);
+ if(ddMatch.test(fvalue)){
+ cell.title = ddText.replace('%0', fvalue);
+ cell.className = ' x-date-disabled';
+ }
+ }
+ };
+
+ var i = 0;
+ for(; i < startingPos; i++) {
+ textEls[i].innerHTML = (++prevStart);
+ d.setDate(d.getDate()+1);
+ cells[i].className = 'x-date-prevday';
+ setCellClass(this, cells[i]);
+ }
+ for(; i < days; i++){
+ var intDay = i - startingPos + 1;
+ textEls[i].innerHTML = (intDay);
+ d.setDate(d.getDate()+1);
+ cells[i].className = 'x-date-active';
+ setCellClass(this, cells[i]);
+ }
+ var extraDays = 0;
+ for(; i < 42; i++) {
+ textEls[i].innerHTML = (++extraDays);
+ d.setDate(d.getDate()+1);
+ cells[i].className = 'x-date-nextday';
+ setCellClass(this, cells[i]);
+ }
+
+ this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
+
+ if(!this.internalRender){
+ var main = this.el.dom.firstChild,
+ w = main.offsetWidth;
+ this.el.setWidth(w + this.el.getBorderWidth('lr'));
+ Ext.fly(main).setWidth(w);
+ this.internalRender = true;
+ // opera does not respect the auto grow header center column
+ // then, after it gets a width opera refuses to recalculate
+ // without a second pass
+ if(Ext.isOpera && !this.secondPass){
+ main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
+ this.secondPass = true;
+ this.update.defer(10, this, [date]);
+ }
+ }
}
},
um.un('failure', this.onLoad, this);
}
}
-};/**\r
- * @class Ext.Slider\r
- * @extends Ext.BoxComponent\r
- * Slider which supports vertical or horizontal orientation, keyboard adjustments,\r
- * configurable snapping, axis clicking and animation. Can be added as an item to\r
- * any container. Example usage:\r
-<pre><code>\r
-new Ext.Slider({\r
- renderTo: Ext.getBody(),\r
- width: 200,\r
- value: 50,\r
- increment: 10,\r
- minValue: 0,\r
- maxValue: 100\r
-});\r
-</code></pre>\r
- */\r
-Ext.Slider = Ext.extend(Ext.BoxComponent, {\r
- /**\r
- * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.\r
- */\r
- /**\r
- * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.\r
- */\r
- vertical: false,\r
- /**\r
- * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.\r
- */\r
- minValue: 0,\r
- /**\r
- * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.\r
- */\r
- maxValue: 100,\r
- /**\r
- * @cfg {Number/Boolean} decimalPrecision.\r
- * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>\r
- * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>\r
- */\r
- decimalPrecision: 0,\r
- /**\r
- * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.\r
- */\r
- keyIncrement: 1,\r
- /**\r
- * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.\r
- */\r
- increment: 0,\r
- // private\r
- clickRange: [5,15],\r
- /**\r
- * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true\r
- */\r
- clickToChange : true,\r
- /**\r
- * @cfg {Boolean} animate Turn on or off animation. Defaults to true\r
- */\r
- animate: true,\r
-\r
- /**\r
- * True while the thumb is in a drag operation\r
- * @type boolean\r
- */\r
- dragging: false,\r
-\r
- // private override\r
- initComponent : function(){\r
- if(!Ext.isDefined(this.value)){\r
- this.value = this.minValue;\r
- }\r
- Ext.Slider.superclass.initComponent.call(this);\r
- this.keyIncrement = Math.max(this.increment, this.keyIncrement);\r
- this.addEvents(\r
- /**\r
- * @event beforechange\r
- * Fires before the slider value is changed. By returning false from an event handler,\r
- * you can cancel the event and prevent the slider from changing.\r
- * @param {Ext.Slider} slider The slider\r
- * @param {Number} newValue The new value which the slider is being changed to.\r
- * @param {Number} oldValue The old value which the slider was previously.\r
- */\r
- 'beforechange',\r
- /**\r
- * @event change\r
- * Fires when the slider value is changed.\r
- * @param {Ext.Slider} slider The slider\r
- * @param {Number} newValue The new value which the slider has been changed to.\r
- */\r
- 'change',\r
- /**\r
- * @event changecomplete\r
- * Fires when the slider value is changed by the user and any drag operations have completed.\r
- * @param {Ext.Slider} slider The slider\r
- * @param {Number} newValue The new value which the slider has been changed to.\r
- */\r
- 'changecomplete',\r
- /**\r
- * @event dragstart\r
- * Fires after a drag operation has started.\r
- * @param {Ext.Slider} slider The slider\r
- * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
- */\r
- 'dragstart',\r
- /**\r
- * @event drag\r
- * Fires continuously during the drag operation while the mouse is moving.\r
- * @param {Ext.Slider} slider The slider\r
- * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
- */\r
- 'drag',\r
- /**\r
- * @event dragend\r
- * Fires after the drag operation has completed.\r
- * @param {Ext.Slider} slider The slider\r
- * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
- */\r
- 'dragend'\r
- );\r
-\r
- if(this.vertical){\r
- Ext.apply(this, Ext.Slider.Vertical);\r
- }\r
- },\r
-\r
- // private override\r
- onRender : function(){\r
- this.autoEl = {\r
- cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),\r
- cn:{cls:'x-slider-end',cn:{cls:'x-slider-inner',cn:[{cls:'x-slider-thumb'},{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]}}\r
- };\r
- Ext.Slider.superclass.onRender.apply(this, arguments);\r
- this.endEl = this.el.first();\r
- this.innerEl = this.endEl.first();\r
- this.thumb = this.innerEl.first();\r
- this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2;\r
- this.focusEl = this.thumb.next();\r
- this.initEvents();\r
- },\r
-\r
- // private override\r
- initEvents : function(){\r
- this.thumb.addClassOnOver('x-slider-thumb-over');\r
- this.mon(this.el, {\r
- scope: this,\r
- mousedown: this.onMouseDown,\r
- keydown: this.onKeyDown\r
- });\r
-\r
- this.focusEl.swallowEvent("click", true);\r
-\r
- this.tracker = new Ext.dd.DragTracker({\r
- onBeforeStart: this.onBeforeDragStart.createDelegate(this),\r
- onStart: this.onDragStart.createDelegate(this),\r
- onDrag: this.onDrag.createDelegate(this),\r
- onEnd: this.onDragEnd.createDelegate(this),\r
- tolerance: 3,\r
- autoStart: 300\r
- });\r
- this.tracker.initEl(this.thumb);\r
- },\r
-\r
- // private override\r
- onMouseDown : function(e){\r
- if(this.disabled){\r
- return;\r
- }\r
- if(this.clickToChange && e.target != this.thumb.dom){\r
- var local = this.innerEl.translatePoints(e.getXY());\r
- this.onClickChange(local);\r
- }\r
- this.focus();\r
- },\r
-\r
- // private\r
- onClickChange : function(local){\r
- if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){\r
- this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);\r
- }\r
- },\r
-\r
- // private\r
- onKeyDown : function(e){\r
- if(this.disabled){e.preventDefault();return;}\r
- var k = e.getKey();\r
- switch(k){\r
- case e.UP:\r
- case e.RIGHT:\r
- e.stopEvent();\r
- if(e.ctrlKey){\r
- this.setValue(this.maxValue, undefined, true);\r
- }else{\r
- this.setValue(this.value+this.keyIncrement, undefined, true);\r
- }\r
- break;\r
- case e.DOWN:\r
- case e.LEFT:\r
- e.stopEvent();\r
- if(e.ctrlKey){\r
- this.setValue(this.minValue, undefined, true);\r
- }else{\r
- this.setValue(this.value-this.keyIncrement, undefined, true);\r
- }\r
- break;\r
- default:\r
- e.preventDefault();\r
- }\r
- },\r
-\r
- // private\r
- doSnap : function(value){\r
- if(!(this.increment && value)){\r
- return value;\r
- }\r
- var newValue = value,\r
- inc = this.increment,\r
- m = value % inc;\r
- if(m != 0){\r
- newValue -= m;\r
- if(m * 2 > inc){\r
- newValue += inc;\r
- }else if(m * 2 < -inc){\r
- newValue -= inc;\r
- }\r
- }\r
- return newValue.constrain(this.minValue, this.maxValue);\r
- },\r
-\r
- // private\r
- afterRender : function(){\r
- Ext.Slider.superclass.afterRender.apply(this, arguments);\r
- if(this.value !== undefined){\r
- var v = this.normalizeValue(this.value);\r
- if(v !== this.value){\r
- delete this.value;\r
- this.setValue(v, false);\r
- }else{\r
- this.moveThumb(this.translateValue(v), false);\r
- }\r
- }\r
- },\r
-\r
- // private\r
- getRatio : function(){\r
- var w = this.innerEl.getWidth(),\r
- v = this.maxValue - this.minValue;\r
- return v == 0 ? w : (w/v);\r
- },\r
-\r
- // private\r
- normalizeValue : function(v){\r
- v = this.doSnap(v);\r
- v = Ext.util.Format.round(v, this.decimalPrecision);\r
- v = v.constrain(this.minValue, this.maxValue);\r
- return v;\r
- },\r
- \r
- /**\r
- * Sets the minimum value for the slider instance. If the current value is less than the \r
- * minimum value, the current value will be changed.\r
- * @param {Number} val The new minimum value\r
- */\r
- setMinValue : function(val){\r
- this.minValue = val;\r
- this.syncThumb();\r
- if(this.value < val){\r
- this.setValue(val);\r
- }\r
- },\r
- \r
- /**\r
- * Sets the maximum value for the slider instance. If the current value is more than the \r
- * maximum value, the current value will be changed.\r
- * @param {Number} val The new maximum value\r
- */\r
- setMaxValue : function(val){\r
- this.maxValue = val;\r
- this.syncThumb();\r
- if(this.value > val){\r
- this.setValue(val);\r
- }\r
- },\r
-\r
- /**\r
- * Programmatically sets the value of the Slider. Ensures that the value is constrained within\r
- * the minValue and maxValue.\r
- * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)\r
- * @param {Boolean} animate Turn on or off animation, defaults to true\r
- */\r
- setValue : function(v, animate, changeComplete){\r
- v = this.normalizeValue(v);\r
- if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){\r
- this.value = v;\r
- this.moveThumb(this.translateValue(v), animate !== false);\r
- this.fireEvent('change', this, v);\r
- if(changeComplete){\r
- this.fireEvent('changecomplete', this, v);\r
- }\r
- }\r
- },\r
-\r
- // private\r
- translateValue : function(v){\r
- var ratio = this.getRatio();\r
- return (v * ratio) - (this.minValue * ratio) - this.halfThumb;\r
- },\r
-\r
- reverseValue : function(pos){\r
- var ratio = this.getRatio();\r
- return (pos + (this.minValue * ratio)) / ratio;\r
- },\r
-\r
- // private\r
- moveThumb: function(v, animate){\r
- if(!animate || this.animate === false){\r
- this.thumb.setLeft(v);\r
- }else{\r
- this.thumb.shift({left: v, stopFx: true, duration:.35});\r
- }\r
- },\r
-\r
- // private\r
- focus : function(){\r
- this.focusEl.focus(10);\r
- },\r
-\r
- // private\r
- onBeforeDragStart : function(e){\r
- return !this.disabled;\r
- },\r
-\r
- // private\r
- onDragStart: function(e){\r
- this.thumb.addClass('x-slider-thumb-drag');\r
- this.dragging = true;\r
- this.dragStartValue = this.value;\r
- this.fireEvent('dragstart', this, e);\r
- },\r
-\r
- // private\r
- onDrag: function(e){\r
- var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
- this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false);\r
- this.fireEvent('drag', this, e);\r
- },\r
-\r
- // private\r
- onDragEnd: function(e){\r
- this.thumb.removeClass('x-slider-thumb-drag');\r
- this.dragging = false;\r
- this.fireEvent('dragend', this, e);\r
- if(this.dragStartValue != this.value){\r
- this.fireEvent('changecomplete', this, this.value);\r
- }\r
- },\r
-\r
- // private\r
- onResize : function(w, h){\r
- this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));\r
- this.syncThumb();\r
- Ext.Slider.superclass.onResize.apply(this, arguments);\r
- },\r
-\r
- //private\r
- onDisable: function(){\r
- Ext.Slider.superclass.onDisable.call(this);\r
- this.thumb.addClass(this.disabledClass);\r
- if(Ext.isIE){\r
- //IE breaks when using overflow visible and opacity other than 1.\r
- //Create a place holder for the thumb and display it.\r
- var xy = this.thumb.getXY();\r
- this.thumb.hide();\r
- this.innerEl.addClass(this.disabledClass).dom.disabled = true;\r
- if (!this.thumbHolder){\r
- this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});\r
- }\r
- this.thumbHolder.show().setXY(xy);\r
- }\r
- },\r
-\r
- //private\r
- onEnable: function(){\r
- Ext.Slider.superclass.onEnable.call(this);\r
- this.thumb.removeClass(this.disabledClass);\r
- if(Ext.isIE){\r
- this.innerEl.removeClass(this.disabledClass).dom.disabled = false;\r
- if(this.thumbHolder){\r
- this.thumbHolder.hide();\r
- }\r
- this.thumb.show();\r
- this.syncThumb();\r
- }\r
- },\r
-\r
- /**\r
- * Synchronizes the thumb position to the proper proportion of the total component width based\r
- * on the current slider {@link #value}. This will be called automatically when the Slider\r
- * is resized by a layout, but if it is rendered auto width, this method can be called from\r
- * another resize handler to sync the Slider if necessary.\r
- */\r
- syncThumb : function(){\r
- if(this.rendered){\r
- this.moveThumb(this.translateValue(this.value));\r
- }\r
- },\r
-\r
- /**\r
- * Returns the current value of the slider\r
- * @return {Number} The current value of the slider\r
- */\r
- getValue : function(){\r
- return this.value;\r
- },\r
-\r
- // private\r
- beforeDestroy : function(){\r
- Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder');\r
- Ext.Slider.superclass.beforeDestroy.call(this);\r
- }\r
-});\r
-Ext.reg('slider', Ext.Slider);\r
-\r
-// private class to support vertical sliders\r
-Ext.Slider.Vertical = {\r
- onResize : function(w, h){\r
- this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));\r
- this.syncThumb();\r
- },\r
-\r
- getRatio : function(){\r
- var h = this.innerEl.getHeight(),\r
- v = this.maxValue - this.minValue;\r
- return h/v;\r
- },\r
-\r
- moveThumb: function(v, animate){\r
- if(!animate || this.animate === false){\r
- this.thumb.setBottom(v);\r
- }else{\r
- this.thumb.shift({bottom: v, stopFx: true, duration:.35});\r
- }\r
- },\r
-\r
- onDrag: function(e){\r
- var pos = this.innerEl.translatePoints(this.tracker.getXY()),\r
- bottom = this.innerEl.getHeight()-pos.top;\r
- this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false);\r
- this.fireEvent('drag', this, e);\r
- },\r
-\r
- onClickChange : function(local){\r
- if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){\r
- var bottom = this.innerEl.getHeight() - local.top;\r
- this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true);\r
- }\r
- }\r
-};/**\r
- * @class Ext.ProgressBar\r
- * @extends Ext.BoxComponent\r
- * <p>An updateable progress bar component. The progress bar supports two different modes: manual and automatic.</p>\r
- * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the\r
- * progress bar as needed from your own code. This method is most appropriate when you want to show progress\r
- * throughout an operation that has predictable points of interest at which you can update the control.</p>\r
- * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it\r
- * once the operation is complete. You can optionally have the progress bar wait for a specific amount of time\r
- * and then clear itself. Automatic mode is most appropriate for timed operations or asynchronous operations in\r
- * which you have no need for indicating intermediate progress.</p>\r
- * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
- * @cfg {String} text The progress bar text (defaults to '')\r
- * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress\r
- * bar's internal text element)\r
- * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)\r
- * @xtype progress\r
- */\r
-Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {\r
- /**\r
- * @cfg {String} baseCls\r
- * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')\r
- */\r
- baseCls : 'x-progress',\r
- \r
- /**\r
- * @cfg {Boolean} animate\r
- * True to animate the progress bar during transitions (defaults to false)\r
- */\r
- animate : false,\r
-\r
- // private\r
- waitTimer : null,\r
-\r
- // private\r
- initComponent : function(){\r
- Ext.ProgressBar.superclass.initComponent.call(this);\r
- this.addEvents(\r
- /**\r
- * @event update\r
- * Fires after each update interval\r
- * @param {Ext.ProgressBar} this\r
- * @param {Number} The current progress value\r
- * @param {String} The current progress text\r
- */\r
- "update"\r
- );\r
- },\r
-\r
- // private\r
- onRender : function(ct, position){\r
- var tpl = new Ext.Template(\r
- '<div class="{cls}-wrap">',\r
- '<div class="{cls}-inner">',\r
- '<div class="{cls}-bar">',\r
- '<div class="{cls}-text">',\r
- '<div> </div>',\r
- '</div>',\r
- '</div>',\r
- '<div class="{cls}-text {cls}-text-back">',\r
- '<div> </div>',\r
- '</div>',\r
- '</div>',\r
- '</div>'\r
- );\r
-\r
- this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)\r
- : tpl.append(ct, {cls: this.baseCls}, true);\r
- \r
- if(this.id){\r
- this.el.dom.id = this.id;\r
- }\r
- var inner = this.el.dom.firstChild;\r
- this.progressBar = Ext.get(inner.firstChild);\r
-\r
- if(this.textEl){\r
- //use an external text el\r
- this.textEl = Ext.get(this.textEl);\r
- delete this.textTopEl;\r
- }else{\r
- //setup our internal layered text els\r
- this.textTopEl = Ext.get(this.progressBar.dom.firstChild);\r
- var textBackEl = Ext.get(inner.childNodes[1]);\r
- this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');\r
- this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);\r
- this.textEl.setWidth(inner.offsetWidth);\r
- }\r
- this.progressBar.setHeight(inner.offsetHeight);\r
- },\r
- \r
- // private\r
- afterRender : function(){\r
- Ext.ProgressBar.superclass.afterRender.call(this);\r
- if(this.value){\r
- this.updateProgress(this.value, this.text);\r
- }else{\r
- this.updateText(this.text);\r
- }\r
- },\r
-\r
- /**\r
- * Updates the progress bar value, and optionally its text. If the text argument is not specified,\r
- * any existing text value will be unchanged. To blank out existing text, pass ''. Note that even\r
- * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for\r
- * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.\r
- * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
- * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
- * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is\r
- * not specified, the default for the class is used (default to false)\r
- * @return {Ext.ProgressBar} this\r
- */\r
- updateProgress : function(value, text, animate){\r
- this.value = value || 0;\r
- if(text){\r
- this.updateText(text);\r
- }\r
- if(this.rendered && !this.isDestroyed){\r
- var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);\r
- this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));\r
- if(this.textTopEl){\r
- //textTopEl should be the same width as the bar so overflow will clip as the bar moves\r
- this.textTopEl.removeClass('x-hidden').setWidth(w);\r
- }\r
- }\r
- this.fireEvent('update', this, value, text);\r
- return this;\r
- },\r
-\r
- /**\r
- * Initiates an auto-updating progress bar. A duration can be specified, in which case the progress\r
- * bar will automatically reset after a fixed amount of time and optionally call a callback function\r
- * if specified. If no duration is passed in, then the progress bar will run indefinitely and must\r
- * be manually cleared by calling {@link #reset}. The wait method accepts a config object with\r
- * the following properties:\r
- * <pre>\r
-Property Type Description\r
----------- ------------ ----------------------------------------------------------------------\r
-duration Number The length of time in milliseconds that the progress bar should\r
- run before resetting itself (defaults to undefined, in which case it\r
- will run indefinitely until reset is called)\r
-interval Number The length of time in milliseconds between each progress update\r
- (defaults to 1000 ms)\r
-animate Boolean Whether to animate the transition of the progress bar. If this value is\r
- not specified, the default for the class is used. \r
-increment Number The number of progress update segments to display within the progress\r
- bar (defaults to 10). If the bar reaches the end and is still\r
- updating, it will automatically wrap back to the beginning.\r
-text String Optional text to display in the progress bar element (defaults to '').\r
-fn Function A callback function to execute after the progress bar finishes auto-\r
- updating. The function will be called with no arguments. This function\r
- will be ignored if duration is not specified since in that case the\r
- progress bar can only be stopped programmatically, so any required function\r
- should be called by the same code after it resets the progress bar.\r
-scope Object The scope that is passed to the callback function (only applies when\r
- duration and fn are both passed).\r
-</pre>\r
- *\r
- * Example usage:\r
- * <pre><code>\r
-var p = new Ext.ProgressBar({\r
- renderTo: 'my-el'\r
-});\r
-\r
-//Wait for 5 seconds, then update the status el (progress bar will auto-reset)\r
-p.wait({\r
- interval: 100, //bar will move fast!\r
- duration: 5000,\r
- increment: 15,\r
- text: 'Updating...',\r
- scope: this,\r
- fn: function(){\r
- Ext.fly('status').update('Done!');\r
- }\r
-});\r
-\r
-//Or update indefinitely until some async action completes, then reset manually\r
-p.wait();\r
-myAction.on('complete', function(){\r
- p.reset();\r
- Ext.fly('status').update('Done!');\r
-});\r
-</code></pre>\r
- * @param {Object} config (optional) Configuration options\r
- * @return {Ext.ProgressBar} this\r
- */\r
- wait : function(o){\r
- if(!this.waitTimer){\r
- var scope = this;\r
- o = o || {};\r
- this.updateText(o.text);\r
- this.waitTimer = Ext.TaskMgr.start({\r
- run: function(i){\r
- var inc = o.increment || 10;\r
- i -= 1;\r
- this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);\r
- },\r
- interval: o.interval || 1000,\r
- duration: o.duration,\r
- onStop: function(){\r
- if(o.fn){\r
- o.fn.apply(o.scope || this);\r
- }\r
- this.reset();\r
- },\r
- scope: scope\r
- });\r
- }\r
- return this;\r
- },\r
-\r
- /**\r
- * Returns true if the progress bar is currently in a {@link #wait} operation\r
- * @return {Boolean} True if waiting, else false\r
- */\r
- isWaiting : function(){\r
- return this.waitTimer !== null;\r
- },\r
-\r
- /**\r
- * Updates the progress bar text. If specified, textEl will be updated, otherwise the progress\r
- * bar itself will display the updated text.\r
- * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
- * @return {Ext.ProgressBar} this\r
- */\r
- updateText : function(text){\r
- this.text = text || ' ';\r
- if(this.rendered){\r
- this.textEl.update(this.text);\r
- }\r
- return this;\r
- },\r
- \r
- /**\r
- * Synchronizes the inner bar width to the proper proportion of the total componet width based\r
- * on the current progress {@link #value}. This will be called automatically when the ProgressBar\r
- * is resized by a layout, but if it is rendered auto width, this method can be called from\r
- * another resize handler to sync the ProgressBar if necessary.\r
- */\r
- syncProgressBar : function(){\r
- if(this.value){\r
- this.updateProgress(this.value, this.text);\r
- }\r
- return this;\r
- },\r
-\r
- /**\r
- * Sets the size of the progress bar.\r
- * @param {Number} width The new width in pixels\r
- * @param {Number} height The new height in pixels\r
- * @return {Ext.ProgressBar} this\r
- */\r
- setSize : function(w, h){\r
- Ext.ProgressBar.superclass.setSize.call(this, w, h);\r
- if(this.textTopEl){\r
- var inner = this.el.dom.firstChild;\r
- this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);\r
- }\r
- this.syncProgressBar();\r
- return this;\r
- },\r
-\r
- /**\r
- * Resets the progress bar value to 0 and text to empty string. If hide = true, the progress\r
- * bar will also be hidden (using the {@link #hideMode} property internally).\r
- * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)\r
- * @return {Ext.ProgressBar} this\r
- */\r
- reset : function(hide){\r
- this.updateProgress(0);\r
- if(this.textTopEl){\r
- this.textTopEl.addClass('x-hidden');\r
- }\r
- this.clearTimer();\r
- if(hide === true){\r
- this.hide();\r
- }\r
- return this;\r
- },\r
- \r
- // private\r
- clearTimer : function(){\r
- if(this.waitTimer){\r
- this.waitTimer.onStop = null; //prevent recursion\r
- Ext.TaskMgr.stop(this.waitTimer);\r
- this.waitTimer = null;\r
- }\r
- },\r
- \r
- onDestroy: function(){\r
- this.clearTimer();\r
- if(this.rendered){\r
- if(this.textEl.isComposite){\r
- this.textEl.clear();\r
- }\r
- Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');\r
- }\r
- Ext.ProgressBar.superclass.onDestroy.call(this);\r
- }\r
-});\r
+};Ext.ns('Ext.slider');
+
+/**
+ * @class Ext.slider.Thumb
+ * @extends Object
+ * Represents a single thumb element on a Slider. This would not usually be created manually and would instead
+ * be created internally by an {@link Ext.slider.MultiSlider Ext.Slider}.
+ */
+Ext.slider.Thumb = Ext.extend(Object, {
+
+ /**
+ * @constructor
+ * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
+ */
+ constructor: function(config) {
+ /**
+ * @property slider
+ * @type Ext.slider.MultiSlider
+ * The slider this thumb is contained within
+ */
+ Ext.apply(this, config || {}, {
+ cls: 'x-slider-thumb',
+
+ /**
+ * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings
+ */
+ constrain: false
+ });
+
+ Ext.slider.Thumb.superclass.constructor.call(this, config);
+
+ if (this.slider.vertical) {
+ Ext.apply(this, Ext.slider.Thumb.Vertical);
+ }
+ },
+
+ /**
+ * Renders the thumb into a slider
+ */
+ render: function() {
+ this.el = this.slider.innerEl.insertFirst({cls: this.cls});
+
+ this.initEvents();
+ },
+
+ /**
+ * Enables the thumb if it is currently disabled
+ */
+ enable: function() {
+ this.disabled = false;
+ this.el.removeClass(this.slider.disabledClass);
+ },
+
+ /**
+ * Disables the thumb if it is currently enabled
+ */
+ disable: function() {
+ this.disabled = true;
+ this.el.addClass(this.slider.disabledClass);
+ },
+
+ /**
+ * Sets up an Ext.dd.DragTracker for this thumb
+ */
+ initEvents: function() {
+ var el = this.el;
+
+ el.addClassOnOver('x-slider-thumb-over');
+
+ this.tracker = new Ext.dd.DragTracker({
+ onBeforeStart: this.onBeforeDragStart.createDelegate(this),
+ onStart : this.onDragStart.createDelegate(this),
+ onDrag : this.onDrag.createDelegate(this),
+ onEnd : this.onDragEnd.createDelegate(this),
+ tolerance : 3,
+ autoStart : 300
+ });
+
+ this.tracker.initEl(el);
+ },
+
+ /**
+ * @private
+ * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled,
+ * this returns false to disable the DragTracker too.
+ * @return {Boolean} False if the slider is currently disabled
+ */
+ onBeforeDragStart : function(e) {
+ if (this.disabled) {
+ return false;
+ } else {
+ this.slider.promoteThumb(this);
+ return true;
+ }
+ },
+
+ /**
+ * @private
+ * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class
+ * to the thumb and fires the 'dragstart' event
+ */
+ onDragStart: function(e){
+ this.el.addClass('x-slider-thumb-drag');
+ this.dragging = true;
+ this.dragStartValue = this.value;
+
+ this.slider.fireEvent('dragstart', this.slider, e, this);
+ },
+
+ /**
+ * @private
+ * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time
+ * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag
+ */
+ onDrag: function(e) {
+ var slider = this.slider,
+ index = this.index,
+ newValue = this.getNewValue();
+
+ if (this.constrain) {
+ var above = slider.thumbs[index + 1],
+ below = slider.thumbs[index - 1];
+
+ if (below != undefined && newValue <= below.value) newValue = below.value;
+ if (above != undefined && newValue >= above.value) newValue = above.value;
+ }
+
+ slider.setValue(index, newValue, false);
+ slider.fireEvent('drag', slider, e, this);
+ },
+
+ getNewValue: function() {
+ var slider = this.slider,
+ pos = slider.innerEl.translatePoints(this.tracker.getXY());
+
+ return Ext.util.Format.round(slider.reverseValue(pos.left), slider.decimalPrecision);
+ },
+
+ /**
+ * @private
+ * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and
+ * fires the 'changecomplete' event with the new value
+ */
+ onDragEnd: function(e) {
+ var slider = this.slider,
+ value = this.value;
+
+ this.el.removeClass('x-slider-thumb-drag');
+
+ this.dragging = false;
+ slider.fireEvent('dragend', slider, e);
+
+ if (this.dragStartValue != value) {
+ slider.fireEvent('changecomplete', slider, value, this);
+ }
+ }
+});
+
+/**
+ * @class Ext.slider.MultiSlider
+ * @extends Ext.BoxComponent
+ * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking and animation. Can be added as an item to any container. Example usage:
+<pre>
+new Ext.Slider({
+ renderTo: Ext.getBody(),
+ width: 200,
+ value: 50,
+ increment: 10,
+ minValue: 0,
+ maxValue: 100
+});
+</pre>
+ * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
+<pre>
+new Ext.Slider({
+ renderTo: Ext.getBody(),
+ width: 200,
+ values: [25, 50, 75],
+ minValue: 0,
+ maxValue: 100,
+
+ //this defaults to true, setting to false allows the thumbs to pass each other
+ {@link #constrainThumbs}: false
+});
+</pre>
+ */
+Ext.slider.MultiSlider = Ext.extend(Ext.BoxComponent, {
+ /**
+ * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.
+ */
+ /**
+ * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
+ */
+ vertical: false,
+ /**
+ * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
+ */
+ minValue: 0,
+ /**
+ * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
+ */
+ maxValue: 100,
+ /**
+ * @cfg {Number/Boolean} decimalPrecision.
+ * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>
+ * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>
+ */
+ decimalPrecision: 0,
+ /**
+ * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.
+ */
+ keyIncrement: 1,
+ /**
+ * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
+ */
+ increment: 0,
+
+ /**
+ * @private
+ * @property clickRange
+ * @type Array
+ * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom],
+ * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top'
+ * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range
+ */
+ clickRange: [5,15],
+
+ /**
+ * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
+ */
+ clickToChange : true,
+ /**
+ * @cfg {Boolean} animate Turn on or off animation. Defaults to true
+ */
+ animate: true,
+
+ /**
+ * True while the thumb is in a drag operation
+ * @type Boolean
+ */
+ dragging: false,
+
+ /**
+ * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
+ */
+ constrainThumbs: true,
+
+ /**
+ * @private
+ * @property topThumbZIndex
+ * @type Number
+ * The number used internally to set the z index of the top thumb (see promoteThumb for details)
+ */
+ topThumbZIndex: 10000,
+
+ // private override
+ initComponent : function(){
+ if(!Ext.isDefined(this.value)){
+ this.value = this.minValue;
+ }
+
+ /**
+ * @property thumbs
+ * @type Array
+ * Array containing references to each thumb
+ */
+ this.thumbs = [];
+
+ Ext.slider.MultiSlider.superclass.initComponent.call(this);
+
+ this.keyIncrement = Math.max(this.increment, this.keyIncrement);
+ this.addEvents(
+ /**
+ * @event beforechange
+ * Fires before the slider value is changed. By returning false from an event handler,
+ * you can cancel the event and prevent the slider from changing.
+ * @param {Ext.Slider} slider The slider
+ * @param {Number} newValue The new value which the slider is being changed to.
+ * @param {Number} oldValue The old value which the slider was previously.
+ */
+ 'beforechange',
+
+ /**
+ * @event change
+ * Fires when the slider value is changed.
+ * @param {Ext.Slider} slider The slider
+ * @param {Number} newValue The new value which the slider has been changed to.
+ * @param {Ext.slider.Thumb} thumb The thumb that was changed
+ */
+ 'change',
+
+ /**
+ * @event changecomplete
+ * Fires when the slider value is changed by the user and any drag operations have completed.
+ * @param {Ext.Slider} slider The slider
+ * @param {Number} newValue The new value which the slider has been changed to.
+ * @param {Ext.slider.Thumb} thumb The thumb that was changed
+ */
+ 'changecomplete',
+
+ /**
+ * @event dragstart
+ * Fires after a drag operation has started.
+ * @param {Ext.Slider} slider The slider
+ * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+ */
+ 'dragstart',
+
+ /**
+ * @event drag
+ * Fires continuously during the drag operation while the mouse is moving.
+ * @param {Ext.Slider} slider The slider
+ * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+ */
+ 'drag',
+
+ /**
+ * @event dragend
+ * Fires after the drag operation has completed.
+ * @param {Ext.Slider} slider The slider
+ * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+ */
+ 'dragend'
+ );
+
+ /**
+ * @property values
+ * @type Array
+ * Array of values to initalize the thumbs with
+ */
+ if (this.values == undefined || Ext.isEmpty(this.values)) this.values = [0];
+
+ var values = this.values;
+
+ for (var i=0; i < values.length; i++) {
+ this.addThumb(values[i]);
+ }
+
+ if(this.vertical){
+ Ext.apply(this, Ext.slider.Vertical);
+ }
+ },
+
+ /**
+ * Creates a new thumb and adds it to the slider
+ * @param {Number} value The initial value to set on the thumb. Defaults to 0
+ */
+ addThumb: function(value) {
+ var thumb = new Ext.slider.Thumb({
+ value : value,
+ slider : this,
+ index : this.thumbs.length,
+ constrain: this.constrainThumbs
+ });
+ this.thumbs.push(thumb);
+
+ //render the thumb now if needed
+ if (this.rendered) thumb.render();
+ },
+
+ /**
+ * @private
+ * Moves the given thumb above all other by increasing its z-index. This is called when as drag
+ * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
+ * required when the thumbs are stacked on top of each other at one of the ends of the slider's
+ * range, which can result in the user not being able to move any of them.
+ * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
+ */
+ promoteThumb: function(topThumb) {
+ var thumbs = this.thumbs,
+ zIndex, thumb;
+
+ for (var i = 0, j = thumbs.length; i < j; i++) {
+ thumb = thumbs[i];
+
+ if (thumb == topThumb) {
+ zIndex = this.topThumbZIndex;
+ } else {
+ zIndex = '';
+ }
+
+ thumb.el.setStyle('zIndex', zIndex);
+ }
+ },
+
+ // private override
+ onRender : function() {
+ this.autoEl = {
+ cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),
+ cn : {
+ cls: 'x-slider-end',
+ cn : {
+ cls:'x-slider-inner',
+ cn : [{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]
+ }
+ }
+ };
+
+ Ext.slider.MultiSlider.superclass.onRender.apply(this, arguments);
+
+ this.endEl = this.el.first();
+ this.innerEl = this.endEl.first();
+ this.focusEl = this.innerEl.child('.x-slider-focus');
+
+ //render each thumb
+ for (var i=0; i < this.thumbs.length; i++) {
+ this.thumbs[i].render();
+ }
+
+ //calculate the size of half a thumb
+ var thumb = this.innerEl.child('.x-slider-thumb');
+ this.halfThumb = (this.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
+
+ this.initEvents();
+ },
+
+ /**
+ * @private
+ * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
+ * Creates a new DragTracker which is used to control what happens when the user drags the thumb around.
+ */
+ initEvents : function(){
+ this.mon(this.el, {
+ scope : this,
+ mousedown: this.onMouseDown,
+ keydown : this.onKeyDown
+ });
+
+ this.focusEl.swallowEvent("click", true);
+ },
+
+ /**
+ * @private
+ * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
+ * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
+ * @param {Ext.EventObject} e The click event
+ */
+ onMouseDown : function(e){
+ if(this.disabled){
+ return;
+ }
+
+ //see if the click was on any of the thumbs
+ var thumbClicked = false;
+ for (var i=0; i < this.thumbs.length; i++) {
+ thumbClicked = thumbClicked || e.target == this.thumbs[i].el.dom;
+ }
+
+ if (this.clickToChange && !thumbClicked) {
+ var local = this.innerEl.translatePoints(e.getXY());
+ this.onClickChange(local);
+ }
+ this.focus();
+ },
+
+ /**
+ * @private
+ * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Vertical.
+ * Only changes the value if the click was within this.clickRange.
+ * @param {Object} local Object containing top and left values for the click event.
+ */
+ onClickChange : function(local) {
+ if (local.top > this.clickRange[0] && local.top < this.clickRange[1]) {
+ //find the nearest thumb to the click event
+ var thumb = this.getNearest(local, 'left'),
+ index = thumb.index;
+
+ this.setValue(index, Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);
+ }
+ },
+
+ /**
+ * @private
+ * Returns the nearest thumb to a click event, along with its distance
+ * @param {Object} local Object containing top and left values from a click event
+ * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
+ * @return {Object} The closest thumb object and its distance from the click event
+ */
+ getNearest: function(local, prop) {
+ var localValue = prop == 'top' ? this.innerEl.getHeight() - local[prop] : local[prop],
+ clickValue = this.reverseValue(localValue),
+ nearestDistance = (this.maxValue - this.minValue) + 5, //add a small fudge for the end of the slider
+ index = 0,
+ nearest = null;
+
+ for (var i=0; i < this.thumbs.length; i++) {
+ var thumb = this.thumbs[i],
+ value = thumb.value,
+ dist = Math.abs(value - clickValue);
+
+ if (Math.abs(dist <= nearestDistance)) {
+ nearest = thumb;
+ index = i;
+ nearestDistance = dist;
+ }
+ }
+ return nearest;
+ },
+
+ /**
+ * @private
+ * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
+ * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
+ * @param {Ext.EventObject} e The Event object
+ */
+ onKeyDown : function(e){
+ /*
+ * The behaviour for keyboard handling with multiple thumbs is currently undefined.
+ * There's no real sane default for it, so leave it like this until we come up
+ * with a better way of doing it.
+ */
+ if(this.disabled || this.thumbs.length !== 1){
+ e.preventDefault();
+ return;
+ }
+ var k = e.getKey(),
+ val;
+ switch(k){
+ case e.UP:
+ case e.RIGHT:
+ e.stopEvent();
+ val = e.ctrlKey ? this.maxValue : this.getValue(0) + this.keyIncrement;
+ this.setValue(0, val, undefined, true);
+ break;
+ case e.DOWN:
+ case e.LEFT:
+ e.stopEvent();
+ val = e.ctrlKey ? this.minValue : this.getValue(0) - this.keyIncrement;
+ this.setValue(0, val, undefined, true);
+ break;
+ default:
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * @private
+ * If using snapping, this takes a desired new value and returns the closest snapped
+ * value to it
+ * @param {Number} value The unsnapped value
+ * @return {Number} The value of the nearest snap target
+ */
+ doSnap : function(value){
+ if (!(this.increment && value)) {
+ return value;
+ }
+ var newValue = value,
+ inc = this.increment,
+ m = value % inc;
+ if (m != 0) {
+ newValue -= m;
+ if (m * 2 >= inc) {
+ newValue += inc;
+ } else if (m * 2 < -inc) {
+ newValue -= inc;
+ }
+ }
+ return newValue.constrain(this.minValue, this.maxValue);
+ },
+
+ // private
+ afterRender : function(){
+ Ext.slider.MultiSlider.superclass.afterRender.apply(this, arguments);
+
+ for (var i=0; i < this.thumbs.length; i++) {
+ var thumb = this.thumbs[i];
+
+ if (thumb.value !== undefined) {
+ var v = this.normalizeValue(thumb.value);
+
+ if (v !== thumb.value) {
+ // delete this.value;
+ this.setValue(i, v, false);
+ } else {
+ this.moveThumb(i, this.translateValue(v), false);
+ }
+ }
+ };
+ },
+
+ /**
+ * @private
+ * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
+ * the ratio is 2
+ * @return {Number} The ratio of pixels to mapped values
+ */
+ getRatio : function(){
+ var w = this.innerEl.getWidth(),
+ v = this.maxValue - this.minValue;
+ return v == 0 ? w : (w/v);
+ },
+
+ /**
+ * @private
+ * Returns a snapped, constrained value when given a desired value
+ * @param {Number} value Raw number value
+ * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
+ */
+ normalizeValue : function(v){
+ v = this.doSnap(v);
+ v = Ext.util.Format.round(v, this.decimalPrecision);
+ v = v.constrain(this.minValue, this.maxValue);
+ return v;
+ },
+
+ /**
+ * Sets the minimum value for the slider instance. If the current value is less than the
+ * minimum value, the current value will be changed.
+ * @param {Number} val The new minimum value
+ */
+ setMinValue : function(val){
+ this.minValue = val;
+ var i = 0,
+ thumbs = this.thumbs,
+ len = thumbs.length,
+ t;
+
+ for(; i < len; ++i){
+ t = thumbs[i];
+ t.value = t.value < val ? val : t.value;
+ }
+ this.syncThumb();
+ },
+
+ /**
+ * Sets the maximum value for the slider instance. If the current value is more than the
+ * maximum value, the current value will be changed.
+ * @param {Number} val The new maximum value
+ */
+ setMaxValue : function(val){
+ this.maxValue = val;
+ var i = 0,
+ thumbs = this.thumbs,
+ len = thumbs.length,
+ t;
+
+ for(; i < len; ++i){
+ t = thumbs[i];
+ t.value = t.value > val ? val : t.value;
+ }
+ this.syncThumb();
+ },
+
+ /**
+ * Programmatically sets the value of the Slider. Ensures that the value is constrained within
+ * the minValue and maxValue.
+ * @param {Number} index Index of the thumb to move
+ * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
+ * @param {Boolean} animate Turn on or off animation, defaults to true
+ */
+ setValue : function(index, v, animate, changeComplete) {
+ var thumb = this.thumbs[index],
+ el = thumb.el;
+
+ v = this.normalizeValue(v);
+
+ if (v !== thumb.value && this.fireEvent('beforechange', this, v, thumb.value, thumb) !== false) {
+ thumb.value = v;
+ if(this.rendered){
+ this.moveThumb(index, this.translateValue(v), animate !== false);
+ this.fireEvent('change', this, v, thumb);
+ if(changeComplete){
+ this.fireEvent('changecomplete', this, v, thumb);
+ }
+ }
+ }
+ },
+
+ /**
+ * @private
+ */
+ translateValue : function(v) {
+ var ratio = this.getRatio();
+ return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
+ },
+
+ /**
+ * @private
+ * Given a pixel location along the slider, returns the mapped slider value for that pixel.
+ * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
+ * returns 200
+ * @param {Number} pos The position along the slider to return a mapped value for
+ * @return {Number} The mapped value for the given position
+ */
+ reverseValue : function(pos){
+ var ratio = this.getRatio();
+ return (pos + (this.minValue * ratio)) / ratio;
+ },
+
+ /**
+ * @private
+ * @param {Number} index Index of the thumb to move
+ */
+ moveThumb: function(index, v, animate){
+ var thumb = this.thumbs[index].el;
+
+ if(!animate || this.animate === false){
+ thumb.setLeft(v);
+ }else{
+ thumb.shift({left: v, stopFx: true, duration:.35});
+ }
+ },
+
+ // private
+ focus : function(){
+ this.focusEl.focus(10);
+ },
+
+ // private
+ onResize : function(w, h){
+ var thumbs = this.thumbs,
+ len = thumbs.length,
+ i = 0;
+
+ /*
+ * If we happen to be animating during a resize, the position of the thumb will likely be off
+ * when the animation stops. As such, just stop any animations before syncing the thumbs.
+ */
+ for(; i < len; ++i){
+ thumbs[i].el.stopFx();
+ }
+ this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));
+ this.syncThumb();
+ Ext.slider.MultiSlider.superclass.onResize.apply(this, arguments);
+ },
+
+ //private
+ onDisable: function(){
+ Ext.slider.MultiSlider.superclass.onDisable.call(this);
+
+ for (var i=0; i < this.thumbs.length; i++) {
+ var thumb = this.thumbs[i],
+ el = thumb.el;
+
+ thumb.disable();
+
+ if(Ext.isIE){
+ //IE breaks when using overflow visible and opacity other than 1.
+ //Create a place holder for the thumb and display it.
+ var xy = el.getXY();
+ el.hide();
+
+ this.innerEl.addClass(this.disabledClass).dom.disabled = true;
+
+ if (!this.thumbHolder) {
+ this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});
+ }
+
+ this.thumbHolder.show().setXY(xy);
+ }
+ }
+ },
+
+ //private
+ onEnable: function(){
+ Ext.slider.MultiSlider.superclass.onEnable.call(this);
+
+ for (var i=0; i < this.thumbs.length; i++) {
+ var thumb = this.thumbs[i],
+ el = thumb.el;
+
+ thumb.enable();
+
+ if (Ext.isIE) {
+ this.innerEl.removeClass(this.disabledClass).dom.disabled = false;
+
+ if (this.thumbHolder) this.thumbHolder.hide();
+
+ el.show();
+ this.syncThumb();
+ }
+ }
+ },
+
+ /**
+ * Synchronizes the thumb position to the proper proportion of the total component width based
+ * on the current slider {@link #value}. This will be called automatically when the Slider
+ * is resized by a layout, but if it is rendered auto width, this method can be called from
+ * another resize handler to sync the Slider if necessary.
+ */
+ syncThumb : function() {
+ if (this.rendered) {
+ for (var i=0; i < this.thumbs.length; i++) {
+ this.moveThumb(i, this.translateValue(this.thumbs[i].value));
+ }
+ }
+ },
+
+ /**
+ * Returns the current value of the slider
+ * @param {Number} index The index of the thumb to return a value for
+ * @return {Number} The current value of the slider
+ */
+ getValue : function(index) {
+ return this.thumbs[index].value;
+ },
+
+ /**
+ * Returns an array of values - one for the location of each thumb
+ * @return {Array} The set of thumb values
+ */
+ getValues: function() {
+ var values = [];
+
+ for (var i=0; i < this.thumbs.length; i++) {
+ values.push(this.thumbs[i].value);
+ }
+
+ return values;
+ },
+
+ // private
+ beforeDestroy : function(){
+ Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder');
+ Ext.slider.MultiSlider.superclass.beforeDestroy.call(this);
+ }
+});
+
+Ext.reg('multislider', Ext.slider.MultiSlider);
+
+/**
+ * @class Ext.slider.SingleSlider
+ * @extends Ext.slider.MultiSlider
+ * Slider which supports vertical or horizontal orientation, keyboard adjustments,
+ * configurable snapping, axis clicking and animation. Can be added as an item to
+ * any container. Example usage:
+<pre><code>
+new Ext.slider.SingleSlider({
+ renderTo: Ext.getBody(),
+ width: 200,
+ value: 50,
+ increment: 10,
+ minValue: 0,
+ maxValue: 100
+});
+</code></pre>
+ * The class Ext.slider.SingleSlider is aliased to Ext.Slider for backwards compatibility.
+ */
+Ext.slider.SingleSlider = Ext.extend(Ext.slider.MultiSlider, {
+ constructor: function(config) {
+ config = config || {};
+
+ Ext.applyIf(config, {
+ values: [config.value || 0]
+ });
+
+ Ext.slider.SingleSlider.superclass.constructor.call(this, config);
+ },
+
+ /**
+ * Returns the current value of the slider
+ * @return {Number} The current value of the slider
+ */
+ getValue: function() {
+ //just returns the value of the first thumb, which should be the only one in a single slider
+ return Ext.slider.SingleSlider.superclass.getValue.call(this, 0);
+ },
+
+ /**
+ * Programmatically sets the value of the Slider. Ensures that the value is constrained within
+ * the minValue and maxValue.
+ * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
+ * @param {Boolean} animate Turn on or off animation, defaults to true
+ */
+ setValue: function(value, animate) {
+ var args = Ext.toArray(arguments),
+ len = args.length;
+
+ //this is to maintain backwards compatiblity for sliders with only one thunb. Usually you must pass the thumb
+ //index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider
+ //signature without the required index. The index will always be 0 for a single slider
+ if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
+ args.unshift(0);
+ }
+
+ return Ext.slider.SingleSlider.superclass.setValue.apply(this, args);
+ },
+
+ /**
+ * Synchronizes the thumb position to the proper proportion of the total component width based
+ * on the current slider {@link #value}. This will be called automatically when the Slider
+ * is resized by a layout, but if it is rendered auto width, this method can be called from
+ * another resize handler to sync the Slider if necessary.
+ */
+ syncThumb : function() {
+ return Ext.slider.SingleSlider.superclass.syncThumb.apply(this, [0].concat(arguments));
+ },
+
+ // private
+ getNearest : function(){
+ // Since there's only 1 thumb, it's always the nearest
+ return this.thumbs[0];
+ }
+});
+
+//backwards compatibility
+Ext.Slider = Ext.slider.SingleSlider;
+
+Ext.reg('slider', Ext.slider.SingleSlider);
+
+// private class to support vertical sliders
+Ext.slider.Vertical = {
+ onResize : function(w, h){
+ this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));
+ this.syncThumb();
+ },
+
+ getRatio : function(){
+ var h = this.innerEl.getHeight(),
+ v = this.maxValue - this.minValue;
+ return h/v;
+ },
+
+ moveThumb: function(index, v, animate) {
+ var thumb = this.thumbs[index],
+ el = thumb.el;
+
+ if (!animate || this.animate === false) {
+ el.setBottom(v);
+ } else {
+ el.shift({bottom: v, stopFx: true, duration:.35});
+ }
+ },
+
+ onClickChange : function(local) {
+ if (local.left > this.clickRange[0] && local.left < this.clickRange[1]) {
+ var thumb = this.getNearest(local, 'top'),
+ index = thumb.index,
+ value = this.minValue + this.reverseValue(this.innerEl.getHeight() - local.top);
+
+ this.setValue(index, Ext.util.Format.round(value, this.decimalPrecision), undefined, true);
+ }
+ }
+};
+
+//private class to support vertical dragging of thumbs within a slider
+Ext.slider.Thumb.Vertical = {
+ getNewValue: function() {
+ var slider = this.slider,
+ innerEl = slider.innerEl,
+ pos = innerEl.translatePoints(this.tracker.getXY()),
+ bottom = innerEl.getHeight() - pos.top;
+
+ return slider.minValue + Ext.util.Format.round(bottom / slider.getRatio(), slider.decimalPrecision);
+ }
+};
+/**
+ * @class Ext.ProgressBar
+ * @extends Ext.BoxComponent
+ * <p>An updateable progress bar component. The progress bar supports two different modes: manual and automatic.</p>
+ * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the
+ * progress bar as needed from your own code. This method is most appropriate when you want to show progress
+ * throughout an operation that has predictable points of interest at which you can update the control.</p>
+ * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it
+ * once the operation is complete. You can optionally have the progress bar wait for a specific amount of time
+ * and then clear itself. Automatic mode is most appropriate for timed operations or asynchronous operations in
+ * which you have no need for indicating intermediate progress.</p>
+ * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)
+ * @cfg {String} text The progress bar text (defaults to '')
+ * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress
+ * bar's internal text element)
+ * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)
+ * @xtype progress
+ */
+Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {
+ /**
+ * @cfg {String} baseCls
+ * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')
+ */
+ baseCls : 'x-progress',
+
+ /**
+ * @cfg {Boolean} animate
+ * True to animate the progress bar during transitions (defaults to false)
+ */
+ animate : false,
+
+ // private
+ waitTimer : null,
+
+ // private
+ initComponent : function(){
+ Ext.ProgressBar.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event update
+ * Fires after each update interval
+ * @param {Ext.ProgressBar} this
+ * @param {Number} The current progress value
+ * @param {String} The current progress text
+ */
+ "update"
+ );
+ },
+
+ // private
+ onRender : function(ct, position){
+ var tpl = new Ext.Template(
+ '<div class="{cls}-wrap">',
+ '<div class="{cls}-inner">',
+ '<div class="{cls}-bar">',
+ '<div class="{cls}-text">',
+ '<div> </div>',
+ '</div>',
+ '</div>',
+ '<div class="{cls}-text {cls}-text-back">',
+ '<div> </div>',
+ '</div>',
+ '</div>',
+ '</div>'
+ );
+
+ this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)
+ : tpl.append(ct, {cls: this.baseCls}, true);
+
+ if(this.id){
+ this.el.dom.id = this.id;
+ }
+ var inner = this.el.dom.firstChild;
+ this.progressBar = Ext.get(inner.firstChild);
+
+ if(this.textEl){
+ //use an external text el
+ this.textEl = Ext.get(this.textEl);
+ delete this.textTopEl;
+ }else{
+ //setup our internal layered text els
+ this.textTopEl = Ext.get(this.progressBar.dom.firstChild);
+ var textBackEl = Ext.get(inner.childNodes[1]);
+ this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');
+ this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);
+ this.textEl.setWidth(inner.offsetWidth);
+ }
+ this.progressBar.setHeight(inner.offsetHeight);
+ },
+
+ // private
+ afterRender : function(){
+ Ext.ProgressBar.superclass.afterRender.call(this);
+ if(this.value){
+ this.updateProgress(this.value, this.text);
+ }else{
+ this.updateText(this.text);
+ }
+ },
+
+ /**
+ * Updates the progress bar value, and optionally its text. If the text argument is not specified,
+ * any existing text value will be unchanged. To blank out existing text, pass ''. Note that even
+ * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
+ * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
+ * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
+ * @param {String} text (optional) The string to display in the progress text element (defaults to '')
+ * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
+ * not specified, the default for the class is used (default to false)
+ * @return {Ext.ProgressBar} this
+ */
+ updateProgress : function(value, text, animate){
+ this.value = value || 0;
+ if(text){
+ this.updateText(text);
+ }
+ if(this.rendered && !this.isDestroyed){
+ var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);
+ this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
+ if(this.textTopEl){
+ //textTopEl should be the same width as the bar so overflow will clip as the bar moves
+ this.textTopEl.removeClass('x-hidden').setWidth(w);
+ }
+ }
+ this.fireEvent('update', this, value, text);
+ return this;
+ },
+
+ /**
+ * Initiates an auto-updating progress bar. A duration can be specified, in which case the progress
+ * bar will automatically reset after a fixed amount of time and optionally call a callback function
+ * if specified. If no duration is passed in, then the progress bar will run indefinitely and must
+ * be manually cleared by calling {@link #reset}. The wait method accepts a config object with
+ * the following properties:
+ * <pre>
+Property Type Description
+---------- ------------ ----------------------------------------------------------------------
+duration Number The length of time in milliseconds that the progress bar should
+ run before resetting itself (defaults to undefined, in which case it
+ will run indefinitely until reset is called)
+interval Number The length of time in milliseconds between each progress update
+ (defaults to 1000 ms)
+animate Boolean Whether to animate the transition of the progress bar. If this value is
+ not specified, the default for the class is used.
+increment Number The number of progress update segments to display within the progress
+ bar (defaults to 10). If the bar reaches the end and is still
+ updating, it will automatically wrap back to the beginning.
+text String Optional text to display in the progress bar element (defaults to '').
+fn Function A callback function to execute after the progress bar finishes auto-
+ updating. The function will be called with no arguments. This function
+ will be ignored if duration is not specified since in that case the
+ progress bar can only be stopped programmatically, so any required function
+ should be called by the same code after it resets the progress bar.
+scope Object The scope that is passed to the callback function (only applies when
+ duration and fn are both passed).
+</pre>
+ *
+ * Example usage:
+ * <pre><code>
+var p = new Ext.ProgressBar({
+ renderTo: 'my-el'
+});
+
+//Wait for 5 seconds, then update the status el (progress bar will auto-reset)
+p.wait({
+ interval: 100, //bar will move fast!
+ duration: 5000,
+ increment: 15,
+ text: 'Updating...',
+ scope: this,
+ fn: function(){
+ Ext.fly('status').update('Done!');
+ }
+});
+
+//Or update indefinitely until some async action completes, then reset manually
+p.wait();
+myAction.on('complete', function(){
+ p.reset();
+ Ext.fly('status').update('Done!');
+});
+</code></pre>
+ * @param {Object} config (optional) Configuration options
+ * @return {Ext.ProgressBar} this
+ */
+ wait : function(o){
+ if(!this.waitTimer){
+ var scope = this;
+ o = o || {};
+ this.updateText(o.text);
+ this.waitTimer = Ext.TaskMgr.start({
+ run: function(i){
+ var inc = o.increment || 10;
+ i -= 1;
+ this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
+ },
+ interval: o.interval || 1000,
+ duration: o.duration,
+ onStop: function(){
+ if(o.fn){
+ o.fn.apply(o.scope || this);
+ }
+ this.reset();
+ },
+ scope: scope
+ });
+ }
+ return this;
+ },
+
+ /**
+ * Returns true if the progress bar is currently in a {@link #wait} operation
+ * @return {Boolean} True if waiting, else false
+ */
+ isWaiting : function(){
+ return this.waitTimer !== null;
+ },
+
+ /**
+ * Updates the progress bar text. If specified, textEl will be updated, otherwise the progress
+ * bar itself will display the updated text.
+ * @param {String} text (optional) The string to display in the progress text element (defaults to '')
+ * @return {Ext.ProgressBar} this
+ */
+ updateText : function(text){
+ this.text = text || ' ';
+ if(this.rendered){
+ this.textEl.update(this.text);
+ }
+ return this;
+ },
+
+ /**
+ * Synchronizes the inner bar width to the proper proportion of the total componet width based
+ * on the current progress {@link #value}. This will be called automatically when the ProgressBar
+ * is resized by a layout, but if it is rendered auto width, this method can be called from
+ * another resize handler to sync the ProgressBar if necessary.
+ */
+ syncProgressBar : function(){
+ if(this.value){
+ this.updateProgress(this.value, this.text);
+ }
+ return this;
+ },
+
+ /**
+ * Sets the size of the progress bar.
+ * @param {Number} width The new width in pixels
+ * @param {Number} height The new height in pixels
+ * @return {Ext.ProgressBar} this
+ */
+ setSize : function(w, h){
+ Ext.ProgressBar.superclass.setSize.call(this, w, h);
+ if(this.textTopEl){
+ var inner = this.el.dom.firstChild;
+ this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);
+ }
+ this.syncProgressBar();
+ return this;
+ },
+
+ /**
+ * Resets the progress bar value to 0 and text to empty string. If hide = true, the progress
+ * bar will also be hidden (using the {@link #hideMode} property internally).
+ * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)
+ * @return {Ext.ProgressBar} this
+ */
+ reset : function(hide){
+ this.updateProgress(0);
+ if(this.textTopEl){
+ this.textTopEl.addClass('x-hidden');
+ }
+ this.clearTimer();
+ if(hide === true){
+ this.hide();
+ }
+ return this;
+ },
+
+ // private
+ clearTimer : function(){
+ if(this.waitTimer){
+ this.waitTimer.onStop = null; //prevent recursion
+ Ext.TaskMgr.stop(this.waitTimer);
+ this.waitTimer = null;
+ }
+ },
+
+ onDestroy: function(){
+ this.clearTimer();
+ if(this.rendered){
+ if(this.textEl.isComposite){
+ this.textEl.clear();
+ }
+ Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');
+ }
+ Ext.ProgressBar.superclass.onDestroy.call(this);
+ }
+});
Ext.reg('progress', Ext.ProgressBar);
\ No newline at end of file