X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/2e847cf21b8ab9d15fa167b315ca5b2fa92638fc..c8256059947f3aa8f5b0a9a2acf55e2142bb4742:/pkgs/cmp-foundation-debug.js?ds=sidebyside
diff --git a/pkgs/cmp-foundation-debug.js b/pkgs/cmp-foundation-debug.js
index 2e29ba62..9f5b6932 100644
--- a/pkgs/cmp-foundation-debug.js
+++ b/pkgs/cmp-foundation-debug.js
@@ -1,6 +1,6 @@
/*!
- * 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
*/
@@ -1025,6 +1025,14 @@ new Ext.Panel({
* The initial set of data to apply to the {@link #tpl}
to
* update the content area of the Component.
*/
+
+ /**
+ * @cfg {Array} bubbleEvents
+ *
An array of events that, when fired, should be bubbled to any parent container. + * See {@link Ext.util.Observable#enableBubble}. + * Defaults to []. + */ + bubbleEvents: [], // private @@ -1079,7 +1087,22 @@ Ext.Foo = Ext.extend(Ext.Bar, { } */ - 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); + }, /** *
Render this Component into the passed HTML element.
@@ -1454,6 +1477,10 @@ var myGrid = new Ext.grid.EditorGridPanel({ 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); @@ -1536,10 +1563,11 @@ new Ext.Panel({ */ 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(); @@ -1931,253 +1959,253 @@ myGridPanel.mon(myGridPanel.getSelectionModel(), { } }); -Ext.reg('component', Ext.Component);/** - * @class Ext.Action - *An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it - * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI - * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button} - * and {@link Ext.menu.Menu} components).
- *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).
- * Example usage:
-// 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');
-
- * @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 '').
- * An example of specifying a custom icon class would be something like: - *
-// 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; }
-
- */
- /**
- * @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 (this reference) in which the
- * {@link #handler}
is executed. Defaults to this Button.
- */
-
- constructor : function(config){
- this.initialConfig = config;
- this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
- this.items = [];
- },
-
- // private
- isAction : true,
-
- /**
- * 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 {@link #hide}
and {@link #show}
.
- * @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 (this
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 (this
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 {@link #setHandler}
. 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);
- }
-});
+Ext.reg('component', Ext.Component);/**
+ * @class Ext.Action
+ * An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it + * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI + * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button} + * and {@link Ext.menu.Menu} components).
+ *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).
+ * Example usage:
+// 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');
+
+ * @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 '').
+ * An example of specifying a custom icon class would be something like: + *
+// 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; }
+
+ */
+ /**
+ * @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 (this reference) in which the
+ * {@link #handler}
is executed. Defaults to this Button.
+ */
+
+ constructor : function(config){
+ this.initialConfig = config;
+ this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
+ this.items = [];
+ },
+
+ // private
+ isAction : true,
+
+ /**
+ * 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 {@link #hide}
and {@link #show}
.
+ * @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 (this
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 (this
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 {@link #setHandler}
. 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
@@ -2302,41 +2330,39 @@ Ext.extend(Ext.Layer, 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);
}
-
}
},
@@ -3098,7 +3124,8 @@ var myPanel = new Ext.Panel({
// 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;
@@ -3114,7 +3141,8 @@ var myPanel = new Ext.Panel({
}
// not rendered
if(!this.boxReady){
- this.width = w, this.height = h;
+ this.width = w;
+ this.height = h;
return this;
}
@@ -3144,8 +3172,8 @@ var myPanel = new Ext.Panel({
/**
* 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:
-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);
-
- * @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 undefined, 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);
- },
-
- /**
- * @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;
- }
- 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]
- );
- }
- 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
- * Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the - * basic behavior of containing items, namely adding, inserting and removing items.
- * - *The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
- * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
- * Container to be encapsulated by an HTML element to your specifications by using the
- * {@link Ext.Component#autoEl autoEl}
config option. This is a useful technique when creating
- * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
- * for example.
The code below illustrates both how to explicitly create a Container, and how to implicitly
- * create one using the 'container'
xtype:
-// explicitly create a Container
-var embeddedColumns = new Ext.Container({
- autoEl: 'div', // This is the default
- layout: 'column',
- defaults: {
- // implicitly create Container by specifying xtype
- xtype: 'container',
- autoEl: 'div', // This is the default.
- layout: 'form',
- columnWidth: 0.5,
- style: {
- padding: '10px'
- }
+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).
+ *
+ * Usage:
+ *
+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);
+
+ * @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 undefined, 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]
+ );
}
- }]
-});
- *
- * Layout
- *Container classes delegate the rendering of child Components to a layout
- * manager class which must be configured into the Container using the
- * {@link #layout}
configuration property.
When either specifying child {@link #items}
of a Container,
- * or dynamically {@link #add adding} Components to a Container, remember to
- * consider how you wish the Container to arrange those child elements, and
- * whether those child elements need to be sized using one of Ext's built-in
- * {@link #layout}
schemes. By default, Containers use the
- * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
- * renders child components, appending them one after the other inside the
- * Container, and does not apply any sizing at all.
A common mistake is when a developer neglects to specify a + 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 + *
Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the + * basic behavior of containing items, namely adding, inserting and removing items.
+ * + *The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
+ * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
+ * Container to be encapsulated by an HTML element to your specifications by using the
+ * {@link Ext.Component#autoEl autoEl}
config option. This is a useful technique when creating
+ * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
+ * for example.
The code below illustrates both how to explicitly create a Container, and how to implicitly
+ * create one using the 'container'
xtype:
+// explicitly create a Container
+var embeddedColumns = new Ext.Container({
+ autoEl: 'div', // This is the default
+ layout: 'column',
+ defaults: {
+ // implicitly create Container by specifying xtype
+ xtype: 'container',
+ autoEl: 'div', // This is the default.
+ layout: 'form',
+ columnWidth: 0.5,
+ style: {
+ padding: '10px'
+ }
+ },
+// The two items below will be Ext.Containers, each encapsulated by a <DIV> element.
+ items: [{
+ items: {
+ xtype: 'datefield',
+ name: 'startDate',
+ fieldLabel: 'Start date'
+ }
+ }, {
+ items: {
+ xtype: 'datefield',
+ name: 'endDate',
+ fieldLabel: 'End date'
+ }
+ }]
+});
+ *
+ * Layout
+ *Container classes delegate the rendering of child Components to a layout
+ * manager class which must be configured into the Container using the
+ * {@link #layout}
configuration property.
When either specifying child {@link #items}
of a Container,
+ * or dynamically {@link #add adding} Components to a Container, remember to
+ * consider how you wish the Container to arrange those child elements, and
+ * whether those child elements need to be sized using one of Ext's built-in
+ * {@link #layout}
schemes. By default, Containers use the
+ * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
+ * renders child components, appending them one after the other inside the
+ * Container, and does not apply any sizing at all.
A common mistake is when a developer neglects to specify a
* {@link #layout}
(e.g. widgets like GridPanels or
* TreePanels are added to Containers for which no {@link #layout}
* has been specified). If a Container is left to use the default
@@ -4107,7 +4135,6 @@ layoutConfig: {
* the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
* with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to 50
.
*/
- // Deprecated - will be removed in 3.2.x
bufferResize: 50,
/**
@@ -4272,8 +4299,6 @@ items: [
'remove'
);
- this.enableBubble(this.bubbleEvents);
-
/**
* The collection of components in this container as a {@link Ext.util.MixedCollection}
* @type MixedCollection
@@ -4299,8 +4324,8 @@ items: [
if(this.layout && this.layout != layout){
this.layout.setContainer(null);
}
- this.initItems();
this.layout = layout;
+ this.initItems();
layout.setContainer(this);
},
@@ -4381,12 +4406,10 @@ tb.{@link #doLayout}(); // refresh the layout
* may not be removed or added. See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
* for more details.
*
Either a single component or an Array of components to add. See + * @param {...Object/Array} component + *
Either one or more Components to add or an Array of Components to add. See
* {@link #items}
for additional information.
The AutoLayout is the default layout manager delegated by {@link Ext.Container} to * render any child Components when no {@link Ext.Container#layout layout} is configured into - * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout - * classes in Ext. It simply renders all child Components into the Container, performing no sizing or - * positioning services. To utilize a layout that provides sizing and positioning of child Components, - * specify an appropriate {@link Ext.Container#layout layout}.
+ * a {@link Ext.Container Container}.. AutoLayout provides only a passthrough of any layout calls + * to any child containers. */ 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; -/** - * @class Ext.layout.FitLayout - * @extends Ext.layout.ContainerLayout - *This is a base class for layouts that contain a single item that automatically expands to fill the layout's - * container. This class is intended to be extended or created via the layout:'fit' {@link Ext.Container#layout} - * config, and should generally not need to be created directly via the new keyword.
- *FitLayout does not have any direct config options (other than inherited ones). To fit a panel to a container - * using FitLayout, simply set layout:'fit' on the container and add a single panel to it. If the container has - * multiple panels, only the first one will be displayed. Example usage:
- *
-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
- }
-});
-
- */
-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
- * 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.
- *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.
- *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:
- *
-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>'
- }]
-});
-
- */
-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',
-
- constructor: function(config){
- Ext.layout.CardLayout.superclass.constructor.call(this, config);
- },
-
- /**
- * 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);
- }
- // 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(item.doLayout){
- 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
+/**
+ * @class Ext.layout.FitLayout
* @extends Ext.layout.ContainerLayout
- * This is a layout that enables anchoring of contained elements relative to the container's dimensions. - * If the container is resized, all anchored items are automatically rerendered according to their - * {@link #anchor} rules.
- *This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout} + *
This is a base class for layouts that contain a single item that automatically expands to fill the layout's + * container. This class is intended to be extended or created via the layout:'fit' {@link Ext.Container#layout} * config, and should generally not need to be created directly via the new keyword.
- *AnchorLayout does not have any direct config options (other than inherited ones). By default, - * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the - * container using the AnchorLayout can supply an anchoring-specific config property of anchorSize. - * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating - * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring - * logic if necessary. For example:
+ *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:
*
-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
+ }
});
- *
+
*/
-Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
- /**
- * @cfg {String} anchor
- * This configuation option is to be applied to child items of a container managed by - * this layout (ie. configured with layout:'anchor').
This value is what tells the layout how an item should be anchored to the container. items - * added to an AnchorLayout accept an anchoring-specific config property of anchor which is a string - * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%'). - * The following types of anchor values are supported:
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.
+ *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.
+ *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:
+ *
+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>'
+ }]
+});
+
+ */
+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
+ * This is a layout that enables anchoring of contained elements relative to the container's dimensions. + * If the container is resized, all anchored items are automatically rerendered according to their + * {@link #anchor} rules.
+ *This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.
+ *AnchorLayout does not have any direct config options (other than inherited ones). By default, + * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the + * container using the AnchorLayout can supply an anchoring-specific config property of anchorSize. + * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating + * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring + * logic if necessary. For example:
+ *
+var viewport = new Ext.Viewport({
+ layout:'anchor',
+ anchorSize: {width:800, height:600},
+ items:[{
+ title:'Item 1',
+ html:'Content 1',
+ width:800,
+ anchor:'right 20%'
+ },{
+ title:'Item 2',
+ html:'Content 2',
+ width:300,
+ anchor:'50% 30%'
+ },{
+ title:'Item 3',
+ html:'Content 3',
+ width:600,
+ anchor:'-100 50%'
+ }]
+});
+ *
+ */
+Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ /**
+ * @cfg {String} anchor
+ * This configuation option is to be applied to child items of a container managed by + * this layout (ie. configured with layout:'anchor').
This value is what tells the layout how an item should be anchored to the container. items + * added to an AnchorLayout accept an anchoring-specific config property of anchor which is a string + * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%'). + * The following types of anchor values are supported:
// two values specified
@@ -5424,8 +5471,19 @@ anchor: '-50 75%'
*/
// 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();
@@ -5461,10 +5519,16 @@ anchor: '-50 75%'
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
@@ -5478,17 +5542,26 @@ anchor: '-50 75%'
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){
@@ -5496,17 +5569,19 @@ anchor: '-50 75%'
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;
@@ -5535,160 +5610,177 @@ anchor: '-50 75%'
*/
});
Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;
-/**
- * @class Ext.layout.ColumnLayout
- * @extends Ext.layout.ContainerLayout
- * This is the layout style of choice for creating structural layouts in a multi-column format where the width of
- * 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.
- * ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
- * specific config property of columnWidth 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).
- * 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).
- * 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 remaining 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:
- *
-// 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
- }]
-});
-
- */
-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();
- 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;
-
- 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];
- if(!c.columnWidth){
- pw -= (c.getWidth() + c.getPositionEl().getMargins('lr'));
- }
- }
-
- pw = pw < 0 ? 0 : pw;
-
- for(i = 0; i < len; i++){
- c = cs[i];
- if(c.columnWidth){
- c.setSize(Math.floor(c.columnWidth * pw) - c.getPositionEl().getMargins('lr'));
- }
- }
-
- // 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.ColumnLayout
+ * @extends Ext.layout.ContainerLayout
+ * This is the layout style of choice for creating structural layouts in a multi-column format where the width of
+ * 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.
+ * ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
+ * specific config property of columnWidth 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).
+ * 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).
+ * 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 remaining 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:
+ *
+// 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
+ }]
+});
+
+ */
+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
* This is a multi-pane, application-oriented UI layout style that supports multiple
@@ -6372,15 +6464,15 @@ Ext.layout.BorderLayout.Region.prototype = {
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
};
@@ -6591,6 +6683,9 @@ Ext.layout.BorderLayout.Region.prototype = {
},
destroy : function(){
+ if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
+ this.autoHideSlideTask.cancel();
+ }
Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
}
};
@@ -6948,7 +7043,6 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
type: 'form',
-
onRemove: function(c){
Ext.layout.FormLayout.superclass.onRemove.call(this, c);
if(this.trackLabels){
@@ -6957,14 +7051,14 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
}
// 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');
}
}
@@ -7011,6 +7105,11 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
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){
@@ -7024,7 +7123,7 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
if (items[i]){
ls += items[i];
if (ls.substr(-1, 1) != ';'){
- ls += ';'
+ ls += ';';
}
}
}
@@ -7065,7 +7164,10 @@ new Ext.Template(
*
Also see {@link #getTemplateArgs}
*/
- // private
+ /**
+ * @private
+ *
+ */
renderItem : function(c, position, target){
if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
var args = this.getTemplateArgs(c);
@@ -7123,7 +7225,7 @@ new Ext.Template(
* A CSS style specification string to add to the field label for this field (defaults to '' or the
* {@link #labelStyle layout's value for labelStyle}).
This is a layout that manages multiple Panels in an expandable accordion style such that only - * one Panel can be expanded at any given time. Each Panel has built-in support for expanding and collapsing.
- *Note: Only Ext.Panels and all subclasses of Ext.Panel may be used in an accordion layout Container.
- *This class is intended to be extended or created via the {@link Ext.Container#layout layout} - * configuration property. See {@link Ext.Container#layout} for additional details.
- *Example usage:
- *
-var accordion = new Ext.Panel({
- title: 'Accordion Layout',
- layout:'accordion',
- defaults: {
- // applied to each contained panel
- bodyStyle: 'padding:15px'
- },
- layoutConfig: {
- // layout-specific configs go here
- titleCollapse: false,
- animate: true,
- activeOnTop: true
- },
- items: [{
- title: 'Panel 1',
- html: '<p>Panel content!</p>'
- },{
- title: 'Panel 2',
- html: '<p>Panel content!</p>'
- },{
- title: 'Panel 3',
- html: '<p>Panel content!</p>'
- }]
-});
-
- */
-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
- * Experimental. 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. This is NOT compatible with "animate:true" (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){
- 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
- * 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.
- *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:
- *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:
- *
-// 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>'
- }]
-});
-
- */
-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
- * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification - * used to create the layout's <table> element. Example:
-{
- xtype: 'panel',
- layout: 'table',
- layoutConfig: {
- tableAttrs: {
- style: {
- width: '100%'
- }
- },
- columns: 3
- }
-}
- */
- 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
- * This is a layout that inherits the anchoring of {@link Ext.layout.AnchorLayout} and adds the - * ability for x/y positioning using the standard x and y component config options.
+/** + * @class Ext.layout.AccordionLayout + * @extends Ext.layout.FitLayout + *This is a layout that manages multiple Panels in an expandable accordion style such that only + * one Panel can be expanded at any given time. Each Panel has built-in support for expanding and collapsing.
+ *Note: Only Ext.Panels and all subclasses of Ext.Panel may be used in an accordion layout Container.
*This class is intended to be extended or created via the {@link Ext.Container#layout layout} * configuration property. See {@link Ext.Container#layout} for additional details.
*Example usage:
*
-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>'
+ }]
+});
+
+ */
+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
+ * Experimental. 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. This is NOT compatible with "animate:true" (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
+ * 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.
+ *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:
+ *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:
+ *
+// 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>'
+ }]
+});
+
+ */
+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
+ * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification + * used to create the layout's <table> element. Example:
+{
+ xtype: 'panel',
+ layout: 'table',
+ layoutConfig: {
+ tableAttrs: {
+ style: {
+ width: '100%'
+ }
+ },
+ columns: 3
+ }
+}
+ */
+ 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
+ * This is a layout that inherits the anchoring of {@link Ext.layout.AnchorLayout} and adds the + * ability for x/y positioning using the standard x and y component config options.
+ *This class is intended to be extended or created via the {@link Ext.Container#layout layout} + * configuration property. See {@link Ext.Container#layout} for additional details.
+ *Example usage:
+ *
+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
@@ -7621,7 +7724,7 @@ Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
extraCls: 'x-abs-layout-item',
- type: 'anchor',
+ type: 'absolute',
onLayout : function(ct, target){
target.position();
@@ -7712,16 +7815,130 @@ Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
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){
@@ -7733,14 +7950,18 @@ Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
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');
}
@@ -7783,6 +8004,7 @@ Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
*/
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
@@ -7796,6 +8018,7 @@ Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
* side of container
',
+ ' | ',
+ '',
+ '
| ',
+ '
Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.
@@ -8380,6 +8858,8 @@ Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;/** 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 @@ -8462,156 +8942,157 @@ Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;/** } } }); -Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;/** - * @class Ext.Viewport - * @extends Ext.Container - *A specialized container representing the viewable application area (the browser viewport).
- *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.
- *The Viewport does not provide scrolling, so child Panels within the Viewport should provide - * for scrolling if needed using the {@link #autoScroll} config.
- *An example showing a classic application border layout:
-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'
- }
- }]
-});
-
- * @constructor
- * Create a new Viewport
- * @param {Object} config The config object
- * @xtype viewport
- */
-Ext.Viewport = Ext.extend(Ext.Container, {
- /*
- * Privatize config options which, if used, would interfere with the
- * correct operation of the Viewport as the sole manager of the
- * layout of the document body.
- */
- /**
- * @cfg {Mixed} applyTo @hide
- */
- /**
- * @cfg {Boolean} allowDomMove @hide
- */
- /**
- * @cfg {Boolean} hideParent @hide
- */
- /**
- * @cfg {Mixed} renderTo @hide
- */
- /**
- * @cfg {Boolean} hideParent @hide
- */
- /**
- * @cfg {Number} height @hide
- */
- /**
- * @cfg {Number} width @hide
- */
- /**
- * @cfg {Boolean} autoHeight @hide
- */
- /**
- * @cfg {Boolean} autoWidth @hide
- */
- /**
- * @cfg {Boolean} deferHeight @hide
- */
- /**
- * @cfg {Boolean} monitorResize @hide
- */
-
- initComponent : function() {
- Ext.Viewport.superclass.initComponent.call(this);
- document.getElementsByTagName('html')[0].className += ' x-viewport';
- this.el = Ext.getBody();
- this.el.setHeight = Ext.emptyFn;
- this.el.setWidth = Ext.emptyFn;
- this.el.setSize = Ext.emptyFn;
- this.el.dom.scroll = 'no';
- this.allowDomMove = false;
- this.autoWidth = true;
- this.autoHeight = true;
- Ext.EventManager.onWindowResize(this.fireResize, this);
- this.renderTo = this.el;
- },
-
- fireResize : function(w, h){
- this.fireEvent('resize', this, w, h, w, h);
- }
-});
-Ext.reg('viewport', Ext.Viewport);
+Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;
/**
- * @class Ext.Panel
+ * @class Ext.Viewport
* @extends Ext.Container
- * Panel is a container that has specific functionality and structural components that make - * it the perfect building block for application-oriented user interfaces.
- *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.
- *When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
- * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
- * those child elements need to be sized using one of Ext's built-in {@link Ext.Container#layout layout}
schemes. By
- * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
- * child components, appending them one after the other inside the Container, and does not apply any sizing
- * at all.
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).
- *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}.
+ *A specialized container representing the viewable application area (the browser viewport).
+ *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.
+ *The Viewport does not provide scrolling, so child Panels within the Viewport should provide + * for scrolling if needed using the {@link #autoScroll} config.
+ *An example showing a classic application border layout:
+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'
+ }
+ }]
+});
+
* @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.
- * This Element is used to house the {@link #title} and {@link #tools}
- *Note: see the Note for {@link Ext.Component#el el}
also.
Panel is a container that has specific functionality and structural components that make + * it the perfect building block for application-oriented user interfaces.
+ *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.
+ *When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
+ * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
+ * those child elements need to be sized using one of Ext's built-in {@link Ext.Container#layout layout}
schemes. By
+ * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
+ * child components, appending them one after the other inside the Container, and does not apply any sizing
+ * at all.
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).
+ *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}.
+ * @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. + *This Element is used to house the {@link #title} and {@link #tools}
+ *Note: see the Note for {@link Ext.Component#el el}
also.
{@link #buttons}
* config property. Read only.
@@ -9645,9 +10126,12 @@ new Ext.Panel({
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
+ });
+ }
}
}
}
@@ -9681,6 +10165,14 @@ new Ext.Panel({
return this.bottomToolbar;
},
+ /**
+ * Returns the {@link Ext.Toolbar toolbar} from the footer ({@link #fbar}
) 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.
@@ -9701,7 +10193,7 @@ new Ext.Panel({
config = Ext.apply({
handler: handler,
scope: scope
- }, config)
+ }, config);
}
return this.fbar.add(config);
},
@@ -9713,7 +10205,7 @@ new Ext.Panel({
this.tools = [];
}
Ext.each(arguments, function(arg){
- this.tools.push(arg)
+ this.tools.push(arg);
}, this);
return;
}
@@ -9780,7 +10272,7 @@ new Ext.Panel({
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();
@@ -9889,9 +10381,7 @@ new Ext.Panel({
// 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
@@ -9938,7 +10428,7 @@ new Ext.Panel({
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);
}
},
@@ -9947,7 +10437,17 @@ new Ext.Panel({
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);
},
@@ -9976,7 +10476,7 @@ new Ext.Panel({
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);
}
},
@@ -9984,6 +10484,9 @@ new Ext.Panel({
// 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;
@@ -10020,7 +10523,10 @@ new Ext.Panel({
},
// 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.
@@ -10061,7 +10567,8 @@ new Ext.Panel({
// 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);
@@ -10084,7 +10591,8 @@ new Ext.Panel({
this.onBodyResize(w, h);
}
this.syncShadow();
- Ext.Panel.superclass.onResize.call(this);
+ Ext.Panel.superclass.onResize.call(this, adjWidth, adjHeight, rawWidth, rawHeight);
+
},
// private
@@ -10103,7 +10611,7 @@ new Ext.Panel({
return h;
},
- // private
+ // deprecate
adjustBodyHeight : function(h){
return h;
},
@@ -10139,18 +10647,27 @@ new Ext.Panel({
* 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;
+ */
},
/**
@@ -10168,7 +10685,10 @@ new Ext.Panel({
* @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
@@ -10270,12 +10790,12 @@ panel.load({
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(
@@ -10283,12 +10803,8 @@ panel.load({
this.fbar.el
);
}
- }else{
- Ext.destroy(
- this.topToolbar,
- this.bottomToolbar
- );
}
+ Ext.destroy(this.toolbars);
},
// private
@@ -10376,8 +10892,9 @@ Ext.extend(Ext.Editor, Ext.Component, {
/**
* @cfg {Boolean} allowBlur
* True to {@link #completeEdit complete the editing process} if in edit mode when the
- * field is blurred. Defaults to false.
+ * field is blurred. Defaults to true.
*/
+ 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,
@@ -10635,6 +11152,10 @@ Ext.extend(Ext.Editor, Ext.Component, {
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){
@@ -10692,7 +11213,7 @@ Ext.extend(Ext.Editor, Ext.Component, {
// 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();
}
},
@@ -10865,7 +11386,7 @@ cp.colors = ['000000', '993300', '333300'];
if(this.value){
var s = this.value;
this.value = null;
- this.select(s);
+ this.select(s, true);
}
},
@@ -10881,8 +11402,9 @@ cp.colors = ['000000', '993300', '333300'];
/**
* 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 false.
*/
- select : function(color){
+ select : function(color, suppressEvent){
color = color.replace('#', '');
if(color != this.value || this.allowReselect){
var el = this.el;
@@ -10891,7 +11413,9 @@ cp.colors = ['000000', '993300', '333300'];
}
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);
+ }
}
}
@@ -10899,8 +11423,7 @@ cp.colors = ['000000', '993300', '333300'];
* @cfg {String} autoEl @hide
*/
});
-Ext.reg('colorpalette', Ext.ColorPalette);
-/**
+Ext.reg('colorpalette', Ext.ColorPalette);/**
* @class Ext.DatePicker
* @extends Ext.Component
* A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
@@ -10940,7 +11463,7 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
* @cfg {Object} scope
* The scope (this
reference) in which the {@link #handler}
* function will be called. Defaults to this DatePicker instance.
- */
+ */
/**
* @cfg {String} todayTip
* A string used to format the message for displaying in a tooltip over the button that
@@ -11043,11 +11566,15 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
* 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);
@@ -11078,7 +11605,7 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
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){
@@ -11154,21 +11681,21 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
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
@@ -11180,7 +11707,7 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
});
}
},
-
+
// private
doDisabled : function(disabled){
this.keyNav.setDisabled(disabled);
@@ -11249,7 +11776,7 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
if(e.ctrlKey){
this.showPrevMonth();
}else{
- this.update(this.activeDate.add('d', -1));
+ this.update(this.activeDate.add('d', -1));
}
},
@@ -11257,7 +11784,7 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
if(e.ctrlKey){
this.showNextMonth();
}else{
- this.update(this.activeDate.add('d', 1));
+ this.update(this.activeDate.add('d', 1));
}
},
@@ -11524,141 +12051,140 @@ Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
// 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]);
+ }
+ }
}
},
@@ -11808,757 +12334,1249 @@ Ext.LoadMask.prototype = {
um.un('failure', this.onLoad, this);
}
}
-};/**
- * @class Ext.Slider
- * @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:
-
-new Ext.Slider({
- renderTo: Ext.getBody(),
- width: 200,
- value: 50,
- increment: 10,
- minValue: 0,
- maxValue: 100
-});
-
- */
-Ext.Slider = 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.
- * The number of decimal places to which to round the Slider's value. Defaults to 0.
- *To disable rounding, configure as false.
- */ - 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 - 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, - - // private override - initComponent : function(){ - if(!Ext.isDefined(this.value)){ - this.value = this.minValue; - } - Ext.Slider.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. - */ - '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. - */ - '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' - ); - - if(this.vertical){ - Ext.apply(this, Ext.Slider.Vertical); - } - }, - - // 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:[{cls:'x-slider-thumb'},{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]}} - }; - Ext.Slider.superclass.onRender.apply(this, arguments); - this.endEl = this.el.first(); - this.innerEl = this.endEl.first(); - this.thumb = this.innerEl.first(); - this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2; - this.focusEl = this.thumb.next(); - this.initEvents(); - }, - - // private override - initEvents : function(){ - this.thumb.addClassOnOver('x-slider-thumb-over'); - this.mon(this.el, { - scope: this, - mousedown: this.onMouseDown, - keydown: this.onKeyDown - }); - - this.focusEl.swallowEvent("click", true); - - 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(this.thumb); - }, - - // private override - onMouseDown : function(e){ - if(this.disabled){ - return; - } - if(this.clickToChange && e.target != this.thumb.dom){ - var local = this.innerEl.translatePoints(e.getXY()); - this.onClickChange(local); - } - this.focus(); - }, - - // private - onClickChange : function(local){ - if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){ - this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true); - } - }, - - // private - onKeyDown : function(e){ - if(this.disabled){e.preventDefault();return;} - var k = e.getKey(); - switch(k){ - case e.UP: - case e.RIGHT: - e.stopEvent(); - if(e.ctrlKey){ - this.setValue(this.maxValue, undefined, true); - }else{ - this.setValue(this.value+this.keyIncrement, undefined, true); - } - break; - case e.DOWN: - case e.LEFT: - e.stopEvent(); - if(e.ctrlKey){ - this.setValue(this.minValue, undefined, true); - }else{ - this.setValue(this.value-this.keyIncrement, undefined, true); - } - break; - default: - e.preventDefault(); - } - }, - - // private - 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.superclass.afterRender.apply(this, arguments); - if(this.value !== undefined){ - var v = this.normalizeValue(this.value); - if(v !== this.value){ - delete this.value; - this.setValue(v, false); - }else{ - this.moveThumb(this.translateValue(v), false); - } - } - }, - - // private - getRatio : function(){ - var w = this.innerEl.getWidth(), - v = this.maxValue - this.minValue; - return v == 0 ? w : (w/v); - }, - - // private - 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; - this.syncThumb(); - if(this.value < val){ - this.setValue(val); - } - }, - - /** - * 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; - this.syncThumb(); - if(this.value > val){ - this.setValue(val); - } - }, - - /** - * 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(v, animate, changeComplete){ - v = this.normalizeValue(v); - if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){ - this.value = v; - this.moveThumb(this.translateValue(v), animate !== false); - this.fireEvent('change', this, v); - if(changeComplete){ - this.fireEvent('changecomplete', this, v); - } - } - }, - - // private - translateValue : function(v){ - var ratio = this.getRatio(); - return (v * ratio) - (this.minValue * ratio) - this.halfThumb; - }, - - reverseValue : function(pos){ - var ratio = this.getRatio(); - return (pos + (this.minValue * ratio)) / ratio; - }, - - // private - moveThumb: function(v, animate){ - if(!animate || this.animate === false){ - this.thumb.setLeft(v); - }else{ - this.thumb.shift({left: v, stopFx: true, duration:.35}); - } - }, - - // private - focus : function(){ - this.focusEl.focus(10); - }, - - // private - onBeforeDragStart : function(e){ - return !this.disabled; - }, - - // private - onDragStart: function(e){ - this.thumb.addClass('x-slider-thumb-drag'); - this.dragging = true; - this.dragStartValue = this.value; - this.fireEvent('dragstart', this, e); - }, - - // private - onDrag: function(e){ - var pos = this.innerEl.translatePoints(this.tracker.getXY()); - this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false); - this.fireEvent('drag', this, e); - }, - - // private - onDragEnd: function(e){ - this.thumb.removeClass('x-slider-thumb-drag'); - this.dragging = false; - this.fireEvent('dragend', this, e); - if(this.dragStartValue != this.value){ - this.fireEvent('changecomplete', this, this.value); - } - }, - - // private - onResize : function(w, h){ - this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r'))); - this.syncThumb(); - Ext.Slider.superclass.onResize.apply(this, arguments); - }, - - //private - onDisable: function(){ - Ext.Slider.superclass.onDisable.call(this); - this.thumb.addClass(this.disabledClass); - 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 = this.thumb.getXY(); - this.thumb.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.superclass.onEnable.call(this); - this.thumb.removeClass(this.disabledClass); - if(Ext.isIE){ - this.innerEl.removeClass(this.disabledClass).dom.disabled = false; - if(this.thumbHolder){ - this.thumbHolder.hide(); - } - this.thumb.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){ - this.moveThumb(this.translateValue(this.value)); - } - }, - - /** - * Returns the current value of the slider - * @return {Number} The current value of the slider - */ - getValue : function(){ - return this.value; - }, - - // private - beforeDestroy : function(){ - Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder'); - Ext.Slider.superclass.beforeDestroy.call(this); - } -}); -Ext.reg('slider', Ext.Slider); - -// 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(v, animate){ - if(!animate || this.animate === false){ - this.thumb.setBottom(v); - }else{ - this.thumb.shift({bottom: v, stopFx: true, duration:.35}); - } - }, - - onDrag: function(e){ - var pos = this.innerEl.translatePoints(this.tracker.getXY()), - bottom = this.innerEl.getHeight()-pos.top; - this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false); - this.fireEvent('drag', this, e); - }, - - onClickChange : function(local){ - if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){ - var bottom = this.innerEl.getHeight() - local.top; - this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true); - } - } -};/** - * @class Ext.ProgressBar - * @extends Ext.BoxComponent - *An updateable progress bar component. The progress bar supports two different modes: manual and automatic.
- *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.
- *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.
- * @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( - '-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). -- * - * Example usage: - *
-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!');
-});
-
- * @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.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:
++new Ext.Slider({ + renderTo: Ext.getBody(), + width: 200, + value: 50, + increment: 10, + minValue: 0, + maxValue: 100 +}); ++ * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one: +
+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 +}); ++ */ +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. + *
The number of decimal places to which to round the Slider's value. Defaults to 0.
+ *To disable rounding, configure as false.
+ */ + 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: +
+new Ext.slider.SingleSlider({
+ renderTo: Ext.getBody(),
+ width: 200,
+ value: 50,
+ increment: 10,
+ minValue: 0,
+ maxValue: 100
+});
+
+ * 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
+ * An updateable progress bar component. The progress bar supports two different modes: manual and automatic.
+ *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.
+ *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.
+ * @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( + '+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). ++ * + * Example usage: + *
+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!');
+});
+
+ * @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