X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/2e847cf21b8ab9d15fa167b315ca5b2fa92638fc..6b044c28b5f26fb99c86c237ffad19741c0f7f3d:/pkgs/cmp-foundation-debug.js?ds=inline
diff --git a/pkgs/cmp-foundation-debug.js b/pkgs/cmp-foundation-debug.js
index 2e29ba62..f6bcd75b 100644
--- a/pkgs/cmp-foundation-debug.js
+++ b/pkgs/cmp-foundation-debug.js
@@ -1,8 +1,8 @@
/*!
- * Ext JS Library 3.1.1
- * Copyright(c) 2006-2010 Ext JS, LLC
- * licensing@extjs.com
- * http://www.extjs.com/license
+ * Ext JS Library 3.3.1
+ * Copyright(c) 2006-2010 Sencha Inc.
+ * licensing@sencha.com
+ * http://www.sencha.com/license
*/
/**
* @class Ext.ComponentMgr
@@ -226,10 +226,11 @@ editorgrid {@link Ext.grid.EditorGridPanel}
flash {@link Ext.FlashComponent}
grid {@link Ext.grid.GridPanel}
listview {@link Ext.ListView}
+multislider {@link Ext.slider.MultiSlider}
panel {@link Ext.Panel}
progress {@link Ext.ProgressBar}
propertygrid {@link Ext.grid.PropertyGrid}
-slider {@link Ext.Slider}
+slider {@link Ext.slider.SingleSlider}
spacer {@link Ext.Spacer}
splitbutton {@link Ext.SplitButton}
tabpanel {@link Ext.TabPanel}
@@ -266,6 +267,7 @@ form {@link Ext.form.FormPanel}
checkbox {@link Ext.form.Checkbox}
checkboxgroup {@link Ext.form.CheckboxGroup}
combo {@link Ext.form.ComboBox}
+compositefield {@link Ext.form.CompositeField}
datefield {@link Ext.form.DateField}
displayfield {@link Ext.form.DisplayField}
field {@link Ext.form.Field}
@@ -1025,6 +1027,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 +1089,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 +1479,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 +1565,11 @@ new Ext.Panel({ */ focus : function(selectText, delay){ if(delay){ - this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]); - return; + this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]); + this.focusTask.delay(Ext.isNumber(delay) ? delay : 10); + return this; } - if(this.rendered){ + if(this.rendered && !this.isDestroyed){ this.el.focus(); if(selectText === true){ this.el.dom.select(); @@ -1720,7 +1750,13 @@ var isText = t.isXType('textfield'); // true var isBoxSubclass = t.isXType('box'); // true, descended from BoxComponent var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance - * @param {String} xtype The xtype to check for this Component + * @param {String/Ext.Component/Class} xtype The xtype to check for this Component. Note that the the component can either be an instance + * or a component class: + *
+var c = new Ext.Component();
+console.log(c.isXType(c));
+console.log(c.isXType(Ext.Component));
+
* @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
* the default), or true to check whether this Component is directly of the specified xtype.
* @return {Boolean} True if this component descends from the specified xtype, false otherwise.
@@ -1775,17 +1811,37 @@ alert(t.getXTypes()); // alerts 'component/box/field/textfield'
/**
* Find a container above this component at any level by xtype or class
- * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
+ * @param {String/Ext.Component/Class} xtype The xtype to check for this Component. Note that the the component can either be an instance
+ * or a component class:
+ * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
+ * the default), or true to check whether this Component is directly of the specified xtype.
* @return {Ext.Container} The first Container which matches the given xtype or class
*/
- findParentByType : function(xtype) {
- return Ext.isFunction(xtype) ?
- this.findParentBy(function(p){
- return p.constructor === xtype;
- }) :
- this.findParentBy(function(p){
- return p.constructor.xtype === xtype;
- });
+ findParentByType : function(xtype, shallow){
+ return this.findParentBy(function(c){
+ return c.isXType(xtype, shallow);
+ });
+ },
+
+ /**
+ * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (this) of
+ * function call will be the scope provided or the current component. The arguments to the function
+ * will be the args provided or the current component. If the function returns false at any point,
+ * the bubble is stopped.
+ * @param {Function} fn The function to call
+ * @param {Object} scope (optional) The scope of the function (defaults to current node)
+ * @param {Array} args (optional) The args to call the function with (default to passing the current component)
+ * @return {Ext.Component} this
+ */
+ bubble : function(fn, scope, args){
+ var p = this;
+ while(p){
+ if(fn.apply(scope || p, args || [p]) === false){
+ break;
+ }
+ p = p.ownerCt;
+ }
+ return this;
},
// protected
@@ -1931,253 +1987,254 @@ 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
@@ -2201,9 +2258,10 @@ Ext.Action = Ext.extend(Object, {
(function(){
Ext.Layer = function(config, existingEl){
config = config || {};
- var dh = Ext.DomHelper;
- var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
- if(existingEl){
+ var dh = Ext.DomHelper,
+ cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
+
+ if (existingEl) {
this.dom = Ext.getDom(existingEl);
}
if(!this.dom){
@@ -2302,41 +2360,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);
}
-
}
},
@@ -2420,6 +2476,10 @@ Ext.extend(Ext.Layer, Ext.Element, {
}
return this;
},
+
+ getConstrainOffset : function(){
+ return this.shadowOffset;
+ },
isVisible : function(){
return this.visible;
@@ -2647,19 +2707,23 @@ Ext.extend(Ext.Layer, Ext.Element, {
* Create a new Shadow
* @param {Object} config The config object
*/
-Ext.Shadow = function(config){
+Ext.Shadow = function(config) {
Ext.apply(this, config);
- if(typeof this.mode != "string"){
+ if (typeof this.mode != "string") {
this.mode = this.defaultMode;
}
- var o = this.offset, a = {h: 0};
- var rad = Math.floor(this.offset/2);
- switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
+ var o = this.offset,
+ a = {
+ h: 0
+ },
+ rad = Math.floor(this.offset / 2);
+ switch (this.mode.toLowerCase()) {
+ // all this hideous nonsense calculates the various offsets for shadows
case "drop":
a.w = 0;
a.l = a.t = o;
a.t -= 1;
- if(Ext.isIE){
+ if (Ext.isIE) {
a.l -= this.offset + rad;
a.t -= this.offset + rad;
a.w -= rad;
@@ -2668,24 +2732,24 @@ Ext.Shadow = function(config){
}
break;
case "sides":
- a.w = (o*2);
+ a.w = (o * 2);
a.l = -o;
- a.t = o-1;
- if(Ext.isIE){
+ a.t = o - 1;
+ if (Ext.isIE) {
a.l -= (this.offset - rad);
a.t -= this.offset + rad;
a.l += 1;
- a.w -= (this.offset - rad)*2;
+ a.w -= (this.offset - rad) * 2;
a.w -= rad + 1;
a.h -= 1;
}
break;
case "frame":
- a.w = a.h = (o*2);
+ a.w = a.h = (o * 2);
a.l = a.t = -o;
a.t += 1;
a.h -= 2;
- if(Ext.isIE){
+ if (Ext.isIE) {
a.l -= (this.offset - rad);
a.t -= (this.offset - rad);
a.l += 1;
@@ -2721,23 +2785,23 @@ Ext.Shadow.prototype = {
* Displays the shadow under the target element
* @param {Mixed} targetEl The id or element under which the shadow should display
*/
- show : function(target){
+ show: function(target) {
target = Ext.get(target);
- if(!this.el){
+ if (!this.el) {
this.el = Ext.Shadow.Pool.pull();
- if(this.el.dom.nextSibling != target.dom){
+ if (this.el.dom.nextSibling != target.dom) {
this.el.insertBefore(target);
}
}
- this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
- if(Ext.isIE){
- this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
+ this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10) - 1);
+ if (Ext.isIE) {
+ this.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (this.offset) + ")";
}
this.realign(
- target.getLeft(true),
- target.getTop(true),
- target.getWidth(),
- target.getHeight()
+ target.getLeft(true),
+ target.getTop(true),
+ target.getWidth(),
+ target.getHeight()
);
this.el.dom.style.display = "block";
},
@@ -2745,8 +2809,8 @@ Ext.Shadow.prototype = {
/**
* Returns true if the shadow is visible, else false
*/
- isVisible : function(){
- return this.el ? true : false;
+ isVisible: function() {
+ return this.el ? true: false;
},
/**
@@ -2757,25 +2821,32 @@ Ext.Shadow.prototype = {
* @param {Number} width The target element width
* @param {Number} height The target element height
*/
- realign : function(l, t, w, h){
- if(!this.el){
+ realign: function(l, t, w, h) {
+ if (!this.el) {
return;
}
- var a = this.adjusts, d = this.el.dom, s = d.style;
- var iea = 0;
- s.left = (l+a.l)+"px";
- s.top = (t+a.t)+"px";
- var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
- if(s.width != sws || s.height != shs){
+ var a = this.adjusts,
+ d = this.el.dom,
+ s = d.style,
+ iea = 0,
+ sw = (w + a.w),
+ sh = (h + a.h),
+ sws = sw + "px",
+ shs = sh + "px",
+ cn,
+ sww;
+ s.left = (l + a.l) + "px";
+ s.top = (t + a.t) + "px";
+ if (s.width != sws || s.height != shs) {
s.width = sws;
s.height = shs;
- if(!Ext.isIE){
- var cn = d.childNodes;
- var sww = Math.max(0, (sw-12))+"px";
+ if (!Ext.isIE) {
+ cn = d.childNodes;
+ sww = Math.max(0, (sw - 12)) + "px";
cn[0].childNodes[1].style.width = sww;
cn[1].childNodes[1].style.width = sww;
cn[2].childNodes[1].style.width = sww;
- cn[1].style.height = Math.max(0, (sh-12))+"px";
+ cn[1].style.height = Math.max(0, (sh - 12)) + "px";
}
}
},
@@ -2783,8 +2854,8 @@ Ext.Shadow.prototype = {
/**
* Hides this shadow
*/
- hide : function(){
- if(this.el){
+ hide: function() {
+ if (this.el) {
this.el.dom.style.display = "none";
Ext.Shadow.Pool.push(this.el);
delete this.el;
@@ -2795,31 +2866,31 @@ Ext.Shadow.prototype = {
* Adjust the z-index of this shadow
* @param {Number} zindex The new z-index
*/
- setZIndex : function(z){
+ setZIndex: function(z) {
this.zIndex = z;
- if(this.el){
+ if (this.el) {
this.el.setStyle("z-index", z);
}
}
};
// Private utility class that manages the internal Shadow cache
-Ext.Shadow.Pool = function(){
- var p = [];
- var markup = Ext.isIE ?
- '' :
- '
-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'
- }
- },
-// 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
+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);
+ },
+
+ /**
+ * @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'
+ }
+ },
+// 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
@@ -4107,7 +4180,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 +4344,6 @@ items: [
'remove'
);
- this.enableBubble(this.bubbleEvents);
-
/**
* The collection of components in this container as a {@link Ext.util.MixedCollection}
* @type MixedCollection
@@ -4299,8 +4369,8 @@ items: [
if(this.layout && this.layout != layout){
this.layout.setContainer(null);
}
- this.initItems();
this.layout = layout;
+ this.initItems();
layout.setContainer(this);
},
@@ -4321,7 +4391,7 @@ items: [
this.setLayout(this.layout);
// If a CardLayout, the active item set
- if(this.activeItem !== undefined){
+ if(this.activeItem !== undefined && this.layout.setActiveItem){
var item = this.activeItem;
delete this.activeItem;
this.layout.setActiveItem(item);
@@ -4381,12 +4451,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.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',
+
+ /**
+ * 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. @@ -5424,95 +5509,157 @@ 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(); - if (!target) { - return {}; + 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'); } - // Style Sized (scrollbars not included) - return target.getStyleSize(); + return ret; }, // private - onLayout : function(ct, target){ - Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target); - var size = this.getLayoutTargetSize(); - - var w = size.width, h = size.height; - - if(w < 20 && h < 20){ + onLayout : function(container, target) { + Ext.layout.AnchorLayout.superclass.onLayout.call(this, container, target); + + var size = this.getLayoutTargetSize(), + containerWidth = size.width, + containerHeight = size.height, + overflow = target.getStyle('overflow'), + components = this.getRenderedItems(container), + len = components.length, + boxes = [], + box, + anchorWidth, + anchorHeight, + component, + anchorSpec, + calcWidth, + calcHeight, + anchorsArray, + totalHeight = 0, + i, + el; + + if(containerWidth < 20 && containerHeight < 20){ return; } // find the container anchoring size - var aw, ah; - if(ct.anchorSize){ - if(typeof ct.anchorSize == 'number'){ - aw = ct.anchorSize; - }else{ - aw = ct.anchorSize.width; - ah = ct.anchorSize.height; + if(container.anchorSize) { + if(typeof container.anchorSize == 'number') { + anchorWidth = container.anchorSize; + } else { + anchorWidth = container.anchorSize.width; + anchorHeight = container.anchorSize.height; } - }else{ - aw = ct.initialConfig.width; - ah = ct.initialConfig.height; + } else { + anchorWidth = container.initialConfig.width; + anchorHeight = container.initialConfig.height; } - var cs = this.getRenderedItems(ct), len = cs.length, i, c, a, cw, ch, el, vs; - for(i = 0; i < len; i++){ - c = cs[i]; - el = c.getPositionEl(); - if(c.anchor){ - a = c.anchorSpec; - if(!a){ // cache all anchor values - vs = c.anchor.split(' '); - c.anchorSpec = a = { - right: this.parseAnchor(vs[0], c.initialConfig.width, aw), - bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah) + for(i = 0; i < len; i++) { + component = components[i]; + el = component.getPositionEl(); + + // If a child container item has no anchor and no specific width, set the child to the default anchor size + if (!component.anchor && component.items && !Ext.isNumber(component.width) && !(Ext.isIE6 && Ext.isStrict)){ + component.anchor = this.defaultAnchor; + } + + if(component.anchor) { + anchorSpec = component.anchorSpec; + // cache all anchor values + if(!anchorSpec){ + anchorsArray = component.anchor.split(' '); + component.anchorSpec = anchorSpec = { + right: this.parseAnchor(anchorsArray[0], component.initialConfig.width, anchorWidth), + bottom: this.parseAnchor(anchorsArray[1], component.initialConfig.height, anchorHeight) }; } - cw = a.right ? this.adjustWidthAnchor(a.right(w) - el.getMargins('lr'), c) : undefined; - ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h) - el.getMargins('tb'), c) : undefined; - - if(cw || ch){ - c.setSize(cw || undefined, ch || undefined); + calcWidth = anchorSpec.right ? this.adjustWidthAnchor(anchorSpec.right(containerWidth) - el.getMargins('lr'), component) : undefined; + calcHeight = anchorSpec.bottom ? this.adjustHeightAnchor(anchorSpec.bottom(containerHeight) - el.getMargins('tb'), component) : undefined; + + if(calcWidth || calcHeight) { + boxes.push({ + component: component, + width: calcWidth || undefined, + height: calcHeight || undefined + }); } } } + for (i = 0, len = boxes.length; i < len; i++) { + box = boxes[i]; + box.component.setSize(box.width, box.height); + } + + if (overflow && overflow != 'hidden' && !this.adjustmentPass) { + var newTargetSize = this.getLayoutTargetSize(); + if (newTargetSize.width != size.width || newTargetSize.height != size.height){ + this.adjustmentPass = true; + this.onLayout(container, target); + } + } + + delete this.adjustmentPass; }, // private - parseAnchor : function(a, start, cstart){ - if(a && a != 'none'){ + 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){ last = v; return v - diff; } - } - }else if(a.indexOf('%') != -1){ - var ratio = parseFloat(a.replace('%', ''))*.01; // percentage + }; + // percentage + } else if(a.indexOf('%') != -1) { + var ratio = parseFloat(a.replace('%', ''))*.01; return function(v){ if(v !== last){ last = v; return Math.floor(v*ratio); } - } - }else{ + }; + // simple offset adjustment + } else { a = parseInt(a, 10); - if(!isNaN(a)){ // simple offset adjustment - return function(v){ - if(v !== last){ + if (!isNaN(a)) { + return function(v) { + if (v !== last) { last = v; return v + a; } - } + }; } } } @@ -5535,223 +5682,240 @@ 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.BorderLayout
+/**
+ * @class Ext.layout.ColumnLayout
* @extends Ext.layout.ContainerLayout
- * This is a multi-pane, application-oriented UI layout style that supports multiple - * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between - * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in - * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.
- *This class is intended to be extended or created via the layout:'border' - * {@link Ext.Container#layout} config, and should generally not need to be created directly - * via the new keyword.
- *BorderLayout does not have any direct config options (other than inherited ones). - * All configuration options available for customizing the BorderLayout are at the - * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion} - * levels.
- *Example usage:
+ *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:
*
-var myBorderPanel = new Ext.Panel({
- {@link Ext.Component#renderTo renderTo}: document.body,
- {@link Ext.BoxComponent#width width}: 700,
- {@link Ext.BoxComponent#height height}: 500,
- {@link Ext.Panel#title title}: 'Border Layout',
- {@link Ext.Container#layout layout}: 'border',
- {@link Ext.Container#items items}: [{
- {@link Ext.Panel#title title}: 'South Region is resizable',
- {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south', // position for region
- {@link Ext.BoxComponent#height height}: 100,
- {@link Ext.layout.BorderLayout.Region#split split}: true, // enable resizing
- {@link Ext.SplitBar#minSize minSize}: 75, // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
- {@link Ext.SplitBar#maxSize maxSize}: 150,
- {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
+// 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
},{
- // xtype: 'panel' implied by default
- {@link Ext.Panel#title title}: 'West Region is collapsible',
- {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
- {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
- {@link Ext.BoxComponent#width width}: 200,
- {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true, // make collapsible
- {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
- {@link Ext.Component#id id}: 'west-region-container',
- {@link Ext.Container#layout layout}: 'fit',
- {@link Ext.Panel#unstyled unstyled}: true
+ title: 'Column 2',
+ columnWidth: .6
},{
- {@link Ext.Panel#title title}: 'Center Region',
- {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center', // center region is required, no width/height specified
- {@link Ext.Component#xtype xtype}: 'container',
- {@link Ext.Container#layout layout}: 'fit',
- {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
+ 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
}]
});
- * Notes:
-wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
+ */
+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
+ * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
+ * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
+ * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.
+ * This class is intended to be extended or created via the layout:'border'
+ * {@link Ext.Container#layout} config, and should generally not need to be created directly
+ * via the new keyword.
+ * BorderLayout does not have any direct config options (other than inherited ones).
+ * All configuration options available for customizing the BorderLayout are at the
+ * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
+ * levels.
+ * Example usage:
+ *
+var myBorderPanel = new Ext.Panel({
+ {@link Ext.Component#renderTo renderTo}: document.body,
+ {@link Ext.BoxComponent#width width}: 700,
+ {@link Ext.BoxComponent#height height}: 500,
+ {@link Ext.Panel#title title}: 'Border Layout',
+ {@link Ext.Container#layout layout}: 'border',
+ {@link Ext.Container#items items}: [{
+ {@link Ext.Panel#title title}: 'South Region is resizable',
+ {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south', // position for region
+ {@link Ext.BoxComponent#height height}: 100,
+ {@link Ext.layout.BorderLayout.Region#split split}: true, // enable resizing
+ {@link Ext.SplitBar#minSize minSize}: 75, // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
+ {@link Ext.SplitBar#maxSize maxSize}: 150,
+ {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
+ },{
+ // xtype: 'panel' implied by default
+ {@link Ext.Panel#title title}: 'West Region is collapsible',
+ {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
+ {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
+ {@link Ext.BoxComponent#width width}: 200,
+ {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true, // make collapsible
+ {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
+ {@link Ext.Component#id id}: 'west-region-container',
+ {@link Ext.Container#layout layout}: 'fit',
+ {@link Ext.Panel#unstyled unstyled}: true
+ },{
+ {@link Ext.Panel#title title}: 'Center Region',
+ {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center', // center region is required, no width/height specified
+ {@link Ext.Component#xtype xtype}: 'container',
+ {@link Ext.Container#layout layout}: 'fit',
+ {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
+ }]
+});
+
+ * Notes:
+ * - Any container using the BorderLayout must have a child item with region:'center'.
+ * The child item in the center region will always be resized to fill the remaining space not used by
+ * the other regions in the layout.
+ * - Any child items with a region of west or east must have width defined
+ * (an integer representing the number of pixels that the region should take up).
+ * - Any child items with a region of north or south must have height defined.
+ * - The regions of a BorderLayout are fixed at render time and thereafter, its child Components may not be removed or added. To add/remove
+ * Components within a BorderLayout, have them wrapped by an additional Container which is directly
+ * managed by the BorderLayout. If the region is to be collapsible, the Container used directly
+ * by the BorderLayout manager should be a Panel. In the following example a Container (an Ext.Panel)
+ * is added to the west region:
+ *
+wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
wrc.{@link Ext.Panel#removeAll removeAll}();
wrc.{@link Ext.Container#add add}({
title: 'Added Panel',
@@ -6175,7 +6339,7 @@ Ext.layout.BorderLayout.Region.prototype = {
this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
}else {
if(this.collapsible !== false && !this.hideCollapseTool) {
- var t = this.toolTemplate.append(
+ var t = this.expandToolEl = this.toolTemplate.append(
this.collapsedEl.dom,
{id:'expand-'+this.position}, true);
t.addClassOnOver('x-tool-expand-'+this.position+'-over');
@@ -6372,15 +6536,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,7 +6755,10 @@ Ext.layout.BorderLayout.Region.prototype = {
},
destroy : function(){
- Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
+ if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
+ this.autoHideSlideTask.cancel();
+ }
+ Ext.destroyMembers(this, 'miniCollapsedEl', 'collapsedEl', 'expandToolEl');
}
};
@@ -6829,7 +6996,8 @@ Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region,
}
});
-Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/**
+Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;
+/**
* @class Ext.layout.FormLayout
* @extends Ext.layout.AnchorLayout
* This layout manager is specifically designed for rendering and managing child Components of
@@ -6942,13 +7110,12 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
/**
* @cfg {Boolean} trackLabels
- * True to show/hide the field label when the field is hidden. Defaults to false.
+ * True to show/hide the field label when the field is hidden. Defaults to true.
*/
- trackLabels: false,
+ trackLabels: true,
type: 'form',
-
onRemove: function(c){
Ext.layout.FormLayout.superclass.onRemove.call(this, c);
if(this.trackLabels){
@@ -6957,14 +7124,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');
}
}
@@ -6984,7 +7151,7 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
labelAdjust: 0
});
}else{
- this.labelSeparator = ct.labelSeparator || this.labelSeparator;
+ this.labelSeparator = Ext.isDefined(ct.labelSeparator) ? ct.labelSeparator : this.labelSeparator;
ct.labelWidth = ct.labelWidth || 100;
if(Ext.isNumber(ct.labelWidth)){
var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
@@ -7011,6 +7178,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 +7196,7 @@ Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
if (items[i]){
ls += items[i];
if (ls.substr(-1, 1) != ';'){
- ls += ';'
+ ls += ';';
}
}
}
@@ -7065,7 +7237,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 +7298,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}).
* - label : StringThe text to display as the label for this
- * field (defaults to '')
+ * field (defaults to the field's configured fieldLabel property)
* - {@link #labelSeparator} : StringThe separator to display after
* the text of the label for this field (defaults to a colon ':' or the
* {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.
@@ -7132,18 +7307,19 @@ new Ext.Template(
* rendered directly after each form field wrapper (defaults to 'x-form-clear-left')
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:'
- },{
- x: 60,
- y: 0,
- name: 'to',
- anchor:'100%' // anchor width by percentage
+ title: 'Panel 1',
+ html: '<p>Panel content!</p>'
},{
- x: 0,
- y: 35,
- xtype:'label',
- text: 'Subject:'
- },{
- x: 60,
- y: 30,
- name: 'subject',
- anchor: '100%' // anchor width by percentage
+ title: 'Panel 2',
+ html: '<p>Panel content!</p>'
},{
- x:0,
- y: 60,
- xtype: 'textarea',
- name: 'msg',
- anchor: '100% 100%' // anchor width and height
+ title: 'Panel 3',
+ html: '<p>Panel content!</p>'
}]
});
*/
-Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
+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,
- extraCls: 'x-abs-layout-item',
+ type: 'accordion',
- type: 'anchor',
+ 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);
+ },
- onLayout : function(ct, target){
- target.position();
- this.paddingLeft = target.getPadding('l');
- this.paddingTop = target.getPadding('t');
- Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
+ 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
- adjustWidthAnchor : function(value, comp){
- return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
+ 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
- adjustHeightAnchor : function(value, comp){
- return value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
- }
+ 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);
+ }
+ },
+
/**
- * @property activeItem
- * @hide
+ * 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['absolute'] = Ext.layout.AbsoluteLayout;
-/**
- * @class Ext.layout.BoxLayout
+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);
+ }else if(c && !this.isValidParent(c, target)){
+ var container = this.getNextCell(c);
+ container.insertBefore(c.getPositionEl().dom, null);
+ c.container = Ext.get(container);
+ this.configureItem(c);
+ }
+ },
+
+ // private
+ isValidParent : function(c, target){
+ return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);
+ },
+
+ destroy: function(){
+ delete this.table;
+ Ext.layout.TableLayout.superclass.destroy.call(this);
+ }
+
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+
+Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**
+ * @class Ext.layout.AbsoluteLayout
+ * @extends Ext.layout.AnchorLayout
+ * 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
+ },{
+ x: 0,
+ y: 35,
+ xtype:'label',
+ text: 'Subject:'
+ },{
+ x: 60,
+ y: 30,
+ name: 'subject',
+ anchor: '100%' // anchor width by percentage
+ },{
+ x:0,
+ y: 60,
+ xtype: 'textarea',
+ name: 'msg',
+ anchor: '100% 100%' // anchor width and height
+ }]
+});
+
+ */
+Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
+
+ extraCls: 'x-abs-layout-item',
+
+ type: 'absolute',
+
+ onLayout : function(ct, target){
+ target.position();
+ this.paddingLeft = target.getPadding('l');
+ this.paddingTop = target.getPadding('t');
+ Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
+ },
+
+ // private
+ adjustWidthAnchor : function(value, comp){
+ return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
+ },
+
+ // private
+ adjustHeightAnchor : function(value, comp){
+ return value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
+ }
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
+/**
+ * @class Ext.layout.BoxLayout
* @extends Ext.layout.ContainerLayout
* Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.
*/ @@ -7712,1013 +7893,2478 @@ 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); } + + var handler = this.overflowHandler; + + if (typeof handler == 'string') { + handler = { + type: handler + }; + } + + var handlerType = 'none'; + if (handler && handler.type != undefined) { + handlerType = handler.type; + } + + var constructor = Ext.layout.boxOverflow[handlerType]; + if (constructor[this.type]) { + constructor = constructor[this.type]; + } + + this.overflowHandler = new constructor(this, handler); + }, + + /** + * @private + * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values + * when laying out + */ + onLayout: function(container, target) { + Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target); + + var tSize = this.getLayoutTargetSize(), + items = this.getVisibleItems(container), + calcs = this.calculateChildBoxes(items, tSize), + boxes = calcs.boxes, + meta = calcs.meta; + + //invoke the overflow handler, if one is configured + if (tSize.width > 0) { + var handler = this.overflowHandler, + method = meta.tooNarrow ? 'handleOverflow' : 'clearOverflow'; + + var results = handler[method](calcs, tSize); + + if (results) { + if (results.targetSize) { + tSize = results.targetSize; + } + + if (results.recalculate) { + items = this.getVisibleItems(container); + calcs = this.calculateChildBoxes(items, tSize); + boxes = calcs.boxes; + } + } + } + + /** + * @private + * @property layoutTargetLastSize + * @type Object + * Private cache of the last measured size of the layout target. This should never be used except by + * BoxLayout subclasses during their onLayout run. + */ + this.layoutTargetLastSize = tSize; + + /** + * @private + * @property childBoxCache + * @type Array + * Array of the last calculated height, width, top and left positions of each visible rendered component + * within the Box layout. + */ + this.childBoxCache = calcs; + + this.updateInnerCtSize(tSize, calcs); + this.updateChildBoxes(boxes); + + // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary. + this.handleTargetOverflow(tSize, container, target); + }, + + /** + * Resizes and repositions each child component + * @param {Array} boxes The box measurements + */ + updateChildBoxes: function(boxes) { + for (var i = 0, length = boxes.length; i < length; i++) { + var box = boxes[i], + comp = box.component; + + if (box.dirtySize) { + comp.setSize(box.width, box.height); + } + // Don't set positions to NaN + if (isNaN(box.left) || isNaN(box.top)) { + continue; + } + + comp.setPosition(box.left, box.top); + } + }, + + /** + * @private + * Called by onRender just before the child components are sized and positioned. This resizes the innerCt + * to make sure all child items fit within it. We call this before sizing the children because if our child + * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them + * again immediately afterwards, giving a performance hit. + * Subclasses should provide an implementation. + * @param {Object} currentSize The current height and width of the innerCt + * @param {Array} calculations The new box calculations of all items to be laid out + */ + updateInnerCtSize: function(tSize, calcs) { + var align = this.align, + padding = this.padding, + width = tSize.width, + height = tSize.height; + + if (this.type == 'hbox') { + var innerCtWidth = width, + innerCtHeight = calcs.meta.maxHeight + padding.top + padding.bottom; + + if (align == 'stretch') { + innerCtHeight = height; + } else if (align == 'middle') { + innerCtHeight = Math.max(height, innerCtHeight); + } + } else { + var innerCtHeight = height, + innerCtWidth = calcs.meta.maxWidth + padding.left + padding.right; + + if (align == 'stretch') { + innerCtWidth = width; + } else if (align == 'center') { + innerCtWidth = Math.max(width, innerCtWidth); + } + } + + this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined); + }, + + /** + * @private + * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden', + * we need to lay out a second time because the scrollbars may have modified the height and width of the layout + * target. Having a Box layout inside such a target is therefore not recommended. + * @param {Object} previousTargetSize The size and height of the layout target before we just laid out + * @param {Ext.Container} container The container + * @param {Ext.Element} target The target element + */ + handleTargetOverflow: function(previousTargetSize, container, target) { + var overflow = target.getStyle('overflow'); + + if (overflow && overflow != 'hidden' &&!this.adjustmentPass) { + var newTargetSize = this.getLayoutTargetSize(); + if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){ + this.adjustmentPass = true; + this.onLayout(container, target); + } + } + + delete this.adjustmentPass; }, // private - isValidParent : function(c, target){ + isValidParent : function(c, target) { return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom; }, + /** + * @private + * Returns all items that are both rendered and visible + * @return {Array} All matching items + */ + getVisibleItems: function(ct) { + var ct = ct || this.container, + t = ct.getLayoutTarget(), + cti = ct.items.items, + len = cti.length, + + i, c, items = []; + + for (i = 0; i < len; i++) { + if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true && c.collapsed !== true && c.shouldLayout !== false){ + items.push(c); + } + } + + return items; + }, + // private - renderAll : function(ct, target){ - if(!this.innerCt){ - // the innerCt prevents wrapping and shuffling while - // the container is resizing + renderAll : function(ct, target) { + if (!this.innerCt) { + // the innerCt prevents wrapping and shuffling while the container is resizing this.innerCt = target.createChild({cls:this.innerCls}); this.padding = this.parseMargins(this.padding); } Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt); }, - onLayout : function(ct, target){ - this.renderAll(ct, target); - }, - - getLayoutTargetSize : function(){ + getLayoutTargetSize : function() { var target = this.container.getLayoutTarget(), ret; + if (target) { ret = target.getViewSize(); - ret.width -= target.getPadding('lr'); - ret.height -= target.getPadding('tb'); - } + + // IE in strict mode will return a width of 0 on the 1st pass of getViewSize. + // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly + // with getViewSize + if (Ext.isIE && Ext.isStrict && ret.width == 0){ + ret = target.getStyleSize(); + } + + ret.width -= target.getPadding('lr'); + ret.height -= target.getPadding('tb'); + } + return ret; }, // private - renderItem : function(c){ + renderItem : function(c) { if(Ext.isString(c.margins)){ c.margins = this.parseMargins(c.margins); }else if(!c.margins){ c.margins = this.defaultMargins; } Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments); + }, + + /** + * @private + */ + destroy: function() { + Ext.destroy(this.overflowHandler); + + Ext.layout.BoxLayout.superclass.destroy.apply(this, arguments); } }); + + +Ext.ns('Ext.layout.boxOverflow'); + /** - * @class Ext.layout.VBoxLayout - * @extends Ext.layout.BoxLayout - *A layout that arranges items vertically down a Container. This layout optionally divides available vertical
- * space between child items containing a numeric flex
configuration.
A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
- * space between child items containing a numeric flex
configuration.
A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
+ * space between child items containing a numeric flex
configuration.
Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.
+Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;/** + * @class Ext.layout.VBoxLayout + * @extends Ext.layout.BoxLayout + *A layout that arranges items vertically down a Container. This layout optionally divides available vertical
+ * space between child items containing a numeric flex
configuration.
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.vbox = Ext.layout.VBoxLayout;
/**
- * @class Ext.Panel
- * @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}.
- * @constructor - * @param {Object} config The config object - * @xtype panel + * @class Ext.layout.ToolbarLayout + * @extends Ext.layout.ContainerLayout + * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not + * usually be used by any other class. */ -Ext.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.
If this is used to load visible HTML elements in either way, then - * the Panel may not be used as a Layout for hosting nested Panels.
- *If this Panel is intended to be used as the host of a Layout (See {@link #layout}
- * then the body Element must not be loaded or changed - it is under the control
- * of the Panel's Layout.
- *
Note: see the Note for {@link Ext.Component#el el}
also.
A {@link Ext.DomHelper DomHelper} element specification object may be specified for any - * Panel Element.
- *By default, the Default element in the table below will be used for the html markup to
- * create a child element with the commensurate Default class name (baseCls
will be
- * replaced by {@link #baseCls}
):
- * Panel Default Default Custom Additional Additional - * Element element class element class style - * ======== ========================== ========= ============== =========== - * {@link #header} div {@link #baseCls}+'-header' {@link #headerCfg} headerCssClass headerStyle - * {@link #bwrap} div {@link #baseCls}+'-bwrap' {@link #bwrapCfg} bwrapCssClass bwrapStyle - * + tbar div {@link #baseCls}+'-tbar' {@link #tbarCfg} tbarCssClass tbarStyle - * + {@link #body} div {@link #baseCls}+'-body' {@link #bodyCfg} {@link #bodyCssClass} {@link #bodyStyle} - * + bbar div {@link #baseCls}+'-bbar' {@link #bbarCfg} bbarCssClass bbarStyle - * + {@link #footer} div {@link #baseCls}+'-footer' {@link #footerCfg} footerCssClass footerStyle - *- *
Configuring a Custom element may be used, for example, to force the {@link #body} Element - * to use a different form of markup than is created by default. An example of this might be - * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as - * a header, or forcing centering of all Panel content by having the body be a <center> - * element:
- *
-new Ext.Panel({
- title: 'Message Title',
- renderTo: Ext.getBody(),
- width: 200, height: 130,
- bodyCfg: {
- tag: 'center',
- cls: 'x-panel-body', // Default class not applied if Custom element specified
- html: 'Message'
- },
- footerCfg: {
- tag: 'h2',
- cls: 'x-panel-footer' // same as the Default class
- html: 'footer html'
- },
- footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
- footerStyle: 'background-color:red' // see {@link #bodyStyle}
-});
- *
- * The example above also explicitly creates a {@link #footer}
with custom markup and
- * styling applied.
',
+ ' | ',
+ '',
+ '
| ',
+ '
A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
- * of this Panel's {@link #header} Element. See {@link #bodyCfg}
also.
', Date.getShortMonthName(i), ' | ', + '', Date.getShortMonthName(i + 6), ' | ', + i === 0 ? + '' + ); } - } - this.fireEvent('specialkey', field, e); - }, + buf.push( + ' | |
-var cp = new Ext.ColorPalette({value:'993300'}); // initial selected color
-cp.render('my-div');
-cp.on('select', function(palette, selColor){
- // do something with selColor
+ /**
+ * @cfg {String} autoEl @hide
+ */
});
+
+Ext.reg('datepicker', Ext.DatePicker);
+/**
+ * @class Ext.LoadMask
+ * A simple utility class for generically masking elements while loading data. If the {@link #store}
+ * config option is specified, the masking will be automatically synchronized with the store's loading
+ * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
+ * element's Updater load indicator and will be destroyed after the initial load.
+ * Example usage:
+ *
+// Basic mask:
+var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
+myMask.show();
* @constructor
- * Create a new ColorPalette
+ * Create a new LoadMask
+ * @param {Mixed} el The element or DOM node, or its id
* @param {Object} config The config object
- * @xtype colorpalette
*/
-Ext.ColorPalette = Ext.extend(Ext.Component, {
- /**
- * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
- */
- /**
- * @cfg {String} itemCls
- * The CSS class to apply to the containing element (defaults to 'x-color-palette')
- */
- itemCls : 'x-color-palette',
+Ext.LoadMask = function(el, config){
+ this.el = Ext.get(el);
+ Ext.apply(this, config);
+ if(this.store){
+ this.store.on({
+ scope: this,
+ beforeload: this.onBeforeLoad,
+ load: this.onLoad,
+ exception: this.onLoad
+ });
+ this.removeMask = Ext.value(this.removeMask, false);
+ }else{
+ var um = this.el.getUpdater();
+ um.showLoadIndicator = false; // disable the default indicator
+ um.on({
+ scope: this,
+ beforeupdate: this.onBeforeLoad,
+ update: this.onLoad,
+ failure: this.onLoad
+ });
+ this.removeMask = Ext.value(this.removeMask, true);
+ }
+};
+
+Ext.LoadMask.prototype = {
/**
- * @cfg {String} value
- * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
- * the hex codes are case-sensitive.
+ * @cfg {Ext.data.Store} store
+ * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
+ * hidden on either load sucess, or load fail.
*/
- value : null,
/**
- * @cfg {String} clickEvent
- * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu).
- * Defaults to 'click'.
+ * @cfg {Boolean} removeMask
+ * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
+ * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
*/
- clickEvent :'click',
- // private
- ctype : 'Ext.ColorPalette',
-
/**
- * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
+ * @cfg {String} msg
+ * The text to display in a centered loading message box (defaults to 'Loading...')
*/
- allowReselect : false,
-
+ msg : 'Loading...',
/**
- * An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
- * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
- * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
- * of colors with the width setting until the box is symmetrical.
- * You can override individual colors if needed:
- *
-var cp = new Ext.ColorPalette();
-cp.colors[0] = 'FF0000'; // change the first box to red
-
-
-Or you can provide a custom array of your own for complete control:
-
-var cp = new Ext.ColorPalette();
-cp.colors = ['000000', '993300', '333300'];
-
- * @type Array
+ * @cfg {String} msgCls
+ * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
*/
- colors : [
- '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
- '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
- 'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
- 'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
- 'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
- ],
+ msgCls : 'x-mask-loading',
/**
- * @cfg {Function} handler
- * Optional. A function that will handle the select event of this palette.
- * The handler is passed the following parameters:
- * palette
: ColorPaletteThe {@link #palette Ext.ColorPalette}.
- * color
: StringThe 6-digit color hex code (without the # symbol).
- *
+ * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
+ * @type Boolean
*/
+ disabled: false,
+
/**
- * @cfg {Object} scope
- * The scope (this reference) in which the {@link #handler}
- * function will be called. Defaults to this ColorPalette instance.
+ * Disables the mask to prevent it from being displayed
*/
-
- // private
- initComponent : function(){
- Ext.ColorPalette.superclass.initComponent.call(this);
- this.addEvents(
- /**
- * @event select
- * Fires when a color is selected
- * @param {ColorPalette} this
- * @param {String} color The 6-digit color hex code (without the # symbol)
- */
- 'select'
- );
-
- if(this.handler){
- this.on('select', this.handler, this.scope, true);
- }
+ disable : function(){
+ this.disabled = true;
},
- // private
- onRender : function(container, position){
- this.autoEl = {
- tag: 'div',
- cls: this.itemCls
- };
- Ext.ColorPalette.superclass.onRender.call(this, container, position);
- var t = this.tpl || new Ext.XTemplate(
- ' '
- );
- t.overwrite(this.el, this.colors);
- this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
- if(this.clickEvent != 'click'){
- this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
- }
+ /**
+ * Enables the mask so that it can be displayed
+ */
+ enable : function(){
+ this.disabled = false;
},
// private
- afterRender : function(){
- Ext.ColorPalette.superclass.afterRender.call(this);
- if(this.value){
- var s = this.value;
- this.value = null;
- this.select(s);
- }
+ onLoad : function(){
+ this.el.unmask(this.removeMask);
},
// private
- handleClick : function(e, t){
- e.preventDefault();
+ onBeforeLoad : function(){
if(!this.disabled){
- var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
- this.select(c.toUpperCase());
+ this.el.mask(this.msg, this.msgCls);
}
},
/**
- * 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)
+ * Show this LoadMask over the configured Element.
*/
- select : function(color){
- color = color.replace('#', '');
- if(color != this.value || this.allowReselect){
- var el = this.el;
- if(this.value){
- el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
- }
- el.child('a.color-'+color).addClass('x-color-palette-sel');
- this.value = color;
- this.fireEvent('select', this, color);
- }
- }
+ show: function(){
+ this.onBeforeLoad();
+ },
/**
- * @cfg {String} autoEl @hide
+ * Hide this LoadMask.
*/
-});
-Ext.reg('colorpalette', Ext.ColorPalette);
+ hide: function(){
+ this.onLoad();
+ },
+
+ // private
+ destroy : function(){
+ if(this.store){
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('exception', this.onLoad, this);
+ }else{
+ var um = this.el.getUpdater();
+ um.un('beforeupdate', this.onBeforeLoad, this);
+ um.un('update', this.onLoad, this);
+ um.un('failure', this.onLoad, this);
+ }
+ }
+};Ext.ns('Ext.slider');
+
/**
- * @class Ext.DatePicker
- * @extends Ext.Component
- * A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
- * to allow browsing and selection of valid dates.
- * All the string values documented below may be overridden by including an Ext locale file in
- * your page.
- * @constructor
- * Create a new DatePicker
- * @param {Object} config The config object
- * @xtype datepicker
+ * @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.DatePicker = Ext.extend(Ext.BoxComponent, {
+Ext.slider.Thumb = Ext.extend(Object, {
+
/**
- * @cfg {String} todayText
- * The text to display on the button that selects the current date (defaults to 'Today'
)
+ * True while the thumb is in a drag operation
+ * @type Boolean
*/
- todayText : 'Today',
+ dragging: false,
+
/**
- * @cfg {String} okText
- * The text to display on the ok button (defaults to ' OK '
to give the user extra clicking room)
+ * @constructor
+ * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
*/
- okText : ' OK ',
+ 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);
+ }
+ },
+
/**
- * @cfg {String} cancelText
- * The text to display on the cancel button (defaults to 'Cancel'
)
+ * Renders the thumb into a slider
*/
- cancelText : 'Cancel',
+ render: function() {
+ this.el = this.slider.innerEl.insertFirst({cls: this.cls});
+
+ this.initEvents();
+ },
+
/**
- * @cfg {Function} handler
- * Optional. A function that will handle the select event of this picker.
- * The handler is passed the following parameters:
- * picker
: DatePickerThis DatePicker.
- * date
: DateThe selected date.
- *
+ * Enables the thumb if it is currently disabled
*/
+ enable: function() {
+ this.disabled = false;
+ this.el.removeClass(this.slider.disabledClass);
+ },
+
/**
- * @cfg {Object} scope
- * The scope (this
reference) in which the {@link #handler}
- * function will be called. Defaults to this DatePicker instance.
- */
+ * Disables the thumb if it is currently enabled
+ */
+ disable: function() {
+ this.disabled = true;
+ this.el.addClass(this.slider.disabledClass);
+ },
+
/**
- * @cfg {String} todayTip
- * A string used to format the message for displaying in a tooltip over the button that
- * selects the current date. Defaults to '{0} (Spacebar)'
where
- * the {0}
token is replaced by today's date.
+ * Sets up an Ext.dd.DragTracker for this thumb
*/
- todayTip : '{0} (Spacebar)',
+ 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);
+ },
+
/**
- * @cfg {String} minText
- * The error text to display if the minDate validation fails (defaults to 'This date is before the minimum date'
)
+ * @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
*/
- minText : 'This date is before the minimum date',
+ onBeforeDragStart : function(e) {
+ if (this.disabled) {
+ return false;
+ } else {
+ this.slider.promoteThumb(this);
+ return true;
+ }
+ },
+
/**
- * @cfg {String} maxText
- * The error text to display if the maxDate validation fails (defaults to 'This date is after the maximum date'
)
+ * @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
*/
- maxText : 'This date is after the maximum date',
+ 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);
+ },
+
/**
- * @cfg {String} format
- * The default date format string which can be overriden for localization support. The format must be
- * valid according to {@link Date#parseDate} (defaults to 'm/d/y'
).
+ * @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
*/
- format : 'm/d/y',
+ 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);
+ },
+
/**
- * @cfg {String} disabledDaysText
- * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled'
)
+ * @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
*/
- disabledDaysText : 'Disabled',
+ 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);
+ }
+ },
+
/**
- * @cfg {String} disabledDatesText
- * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled'
)
+ * @private
+ * Destroys the thumb
*/
- disabledDatesText : 'Disabled',
+ destroy: function(){
+ Ext.destroyMembers(this, 'tracker', 'el');
+ }
+});
+
+/**
+ * @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 {Array} monthNames
- * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
+ * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.
*/
- monthNames : Date.monthNames,
/**
- * @cfg {Array} dayNames
- * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
+ * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
*/
- dayNames : Date.dayNames,
+ vertical: false,
/**
- * @cfg {String} nextText
- * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)'
)
+ * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
*/
- nextText : 'Next Month (Control+Right)',
+ minValue: 0,
/**
- * @cfg {String} prevText
- * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)'
)
+ * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
*/
- prevText : 'Previous Month (Control+Left)',
+ maxValue: 100,
/**
- * @cfg {String} monthYearText
- * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)'
)
+ * @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.
*/
- monthYearText : 'Choose a month (Control+Up/Down to move years)',
+ decimalPrecision: 0,
/**
- * @cfg {Number} startDay
- * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+ * @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.
*/
- startDay : 0,
+ keyIncrement: 1,
/**
- * @cfg {Boolean} showToday
- * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
- * that selects the current date (defaults to true
).
+ * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
*/
- showToday : true,
+ increment: 0,
+
/**
- * @cfg {Date} minDate
- * Minimum allowable date (JavaScript date object, defaults to null)
+ * @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 {Date} maxDate
- * Maximum allowable date (JavaScript date object, defaults to null)
+ * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
*/
+ clickToChange : true,
/**
- * @cfg {Array} disabledDays
- * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
+ * @cfg {Boolean} animate Turn on or off animation. Defaults to true
*/
+ animate: true,
/**
- * @cfg {RegExp} disabledDatesRE
- * JavaScript regular expression used to disable a pattern of dates (defaults to null). The {@link #disabledDates}
- * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
- * disabledDates value.
+ * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
*/
+ constrainThumbs: true,
+
/**
- * @cfg {Array} disabledDates
- * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
- * expression so they are very powerful. Some examples:
- *
- * - ['03/08/2003', '09/16/2003'] would disable those exact dates
- * - ['03/08', '09/16'] would disable those days for every year
- * - ['^03/08'] would only match the beginning (useful if you are using short years)
- * - ['03/../2006'] would disable every day in March 2006
- * - ['^03'] would disable every day in every March
- *
- * Note that the format of the dates included in the array should exactly match the {@link #format} config.
- * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
- * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
+ * @private
+ * @property topThumbZIndex
+ * @type Number
+ * The number used internally to set the z index of the top thumb (see promoteThumb for details)
*/
-
- // private
- // Set by other components to stop the picker focus being updated when the value changes.
- focusOnSelect: true,
+ topThumbZIndex: 10000,
- // private
+ // private override
initComponent : function(){
- Ext.DatePicker.superclass.initComponent.call(this);
+ if(!Ext.isDefined(this.value)){
+ this.value = this.minValue;
+ }
- this.value = this.value ?
- this.value.clearTime(true) : new Date().clearTime();
+ /**
+ * @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 select
- * Fires when a date is selected
- * @param {DatePicker} this DatePicker
- * @param {Date} date The selected date
+ * @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.MultiSlider} 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.
*/
- 'select'
+ 'beforechange',
+
+ /**
+ * @event change
+ * Fires when the slider value is changed.
+ * @param {Ext.slider.MultiSlider} 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.MultiSlider} 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.MultiSlider} 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.MultiSlider} 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.MultiSlider} slider The slider
+ * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+ */
+ 'dragend'
);
- if(this.handler){
- this.on('select', this.handler, this.scope || this);
- }
+ /**
+ * @property values
+ * @type Array
+ * Array of values to initalize the thumbs with
+ */
+ if (this.values == undefined || Ext.isEmpty(this.values)) this.values = [0];
- this.initDisabledDays();
- },
+ var values = this.values;
- // private
- initDisabledDays : function(){
- if(!this.disabledDatesRE && this.disabledDates){
- var dd = this.disabledDates,
- len = dd.length - 1,
- re = '(?:';
-
- Ext.each(dd, function(d, i){
- re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
- if(i != len){
- re += '|';
- }
- }, this);
- this.disabledDatesRE = new RegExp(re + ')');
+ for (var i=0; i < values.length; i++) {
+ this.addThumb(values[i]);
+ }
+
+ if(this.vertical){
+ Ext.apply(this, Ext.slider.Vertical);
}
},
/**
- * Replaces any existing disabled dates with new values and refreshes the DatePicker.
- * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
- * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
+ * Creates a new thumb and adds it to the slider
+ * @param {Number} value The initial value to set on the thumb. Defaults to 0
*/
- setDisabledDates : function(dd){
- if(Ext.isArray(dd)){
- this.disabledDates = dd;
- this.disabledDatesRE = null;
- }else{
- this.disabledDatesRE = dd;
- }
- this.initDisabledDays();
- this.update(this.value, true);
+ 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();
},
/**
- * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
- * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
- * for details on supported values.
+ * @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
*/
- setDisabledDays : function(dd){
- this.disabledDays = dd;
- this.update(this.value, true);
+ 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();
},
/**
- * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
- * @param {Date} value The minimum date that can be selected
+ * @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.
*/
- setMinDate : function(dt){
- this.minDate = dt;
- this.update(this.value, true);
+ initEvents : function(){
+ this.mon(this.el, {
+ scope : this,
+ mousedown: this.onMouseDown,
+ keydown : this.onKeyDown
+ });
+
+ this.focusEl.swallowEvent("click", true);
},
/**
- * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
- * @param {Date} value The maximum date that can be selected
+ * @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
*/
- setMaxDate : function(dt){
- this.maxDate = dt;
- this.update(this.value, true);
+ 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();
},
/**
- * Sets the value of the date field
- * @param {Date} value The date to set
+ * @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.
*/
- setValue : function(value){
- this.value = value.clearTime(true);
- this.update(this.value);
+ 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);
+ }
},
/**
- * Gets the current selected value of the date field
- * @return {Date} The selected date
+ * @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
*/
- getValue : function(){
- return this.value;
- },
+ 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;
- // private
- focus : function(){
- this.update(this.activeDate);
- },
-
- // private
- onEnable: function(initial){
- Ext.DatePicker.superclass.onEnable.call(this);
- this.doDisabled(false);
- this.update(initial ? this.value : this.activeDate);
- if(Ext.isIE){
- this.el.repaint();
+ 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
- onDisable : function(){
- Ext.DatePicker.superclass.onDisable.call(this);
- this.doDisabled(true);
- if(Ext.isIE && !Ext.isIE8){
- /* Really strange problem in IE6/7, when disabled, have to explicitly
- * repaint each of the nodes to get them to display correctly, simply
- * calling repaint on the main element doesn't appear to be enough.
- */
- Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){
- Ext.fly(el).repaint();
- });
+
+ /**
+ * @private
+ * 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;
}
- },
-
- // private
- doDisabled : function(disabled){
- this.keyNav.setDisabled(disabled);
- this.prevRepeater.setDisabled(disabled);
- this.nextRepeater.setDisabled(disabled);
- if(this.showToday){
- this.todayKeyListener.setDisabled(disabled);
- this.todayBtn.setDisabled(disabled);
+ 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
- onRender : function(container, position){
- var m = [
- '',
- ' ',
- ''],
- dn = this.dayNames,
- i;
- for(i = 0; i < 7; i++){
- var d = this.startDay+i;
- if(d > 6){
- d = d-7;
- }
- m.push('', dn[d].substr(0,1), ' ');
+ /**
+ * @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;
}
- m[m.length] = ' ';
- for(i = 0; i < 42; i++) {
- if(i % 7 === 0 && i !== 0){
- m[m.length] = ' ';
+ 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;
}
- m[m.length] = ' ';
}
- m.push('
',
- this.showToday ? ' ' : '',
- '
');
-
- var el = document.createElement('div');
- el.className = 'x-date-picker';
- el.innerHTML = m.join('');
-
- container.dom.insertBefore(el, position);
-
- this.el = Ext.get(el);
- this.eventEl = Ext.get(el.firstChild);
-
- this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
- handler: this.showPrevMonth,
- scope: this,
- preventDefault:true,
- stopDefault:true
- });
+ return newValue.constrain(this.minValue, this.maxValue);
+ },
- this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
- handler: this.showNextMonth,
- scope: this,
- preventDefault:true,
- stopDefault:true
- });
+ // private
+ afterRender : function(){
+ Ext.slider.MultiSlider.superclass.afterRender.apply(this, arguments);
- this.monthPicker = this.el.down('div.x-date-mp');
- this.monthPicker.enableDisplayMode('block');
+ for (var i=0; i < this.thumbs.length; i++) {
+ var thumb = this.thumbs[i];
- this.keyNav = new Ext.KeyNav(this.eventEl, {
- 'left' : function(e){
- if(e.ctrlKey){
- this.showPrevMonth();
- }else{
- this.update(this.activeDate.add('d', -1));
- }
- },
+ if (thumb.value !== undefined) {
+ var v = this.normalizeValue(thumb.value);
- 'right' : function(e){
- if(e.ctrlKey){
- this.showNextMonth();
- }else{
- this.update(this.activeDate.add('d', 1));
+ if (v !== thumb.value) {
+ // delete this.value;
+ this.setValue(i, v, false);
+ } else {
+ this.moveThumb(i, this.translateValue(v), false);
}
- },
+ }
+ };
+ },
- 'up' : function(e){
- if(e.ctrlKey){
- this.showNextYear();
- }else{
- this.update(this.activeDate.add('d', -7));
- }
- },
+ /**
+ * @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);
+ },
- 'down' : function(e){
- if(e.ctrlKey){
- this.showPrevYear();
- }else{
- this.update(this.activeDate.add('d', 7));
- }
- },
+ /**
+ * @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;
+ },
- 'pageUp' : function(e){
- this.showNextMonth();
- },
+ /**
+ * 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();
+ },
- 'pageDown' : function(e){
- this.showPrevMonth();
- },
+ /**
+ * 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();
+ },
- 'enter' : function(e){
- e.stopPropagation();
- return true;
- },
+ /**
+ * 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;
- scope : this
- });
+ v = this.normalizeValue(v);
- this.el.unselectable();
+ 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);
+ }
+ }
+ }
+ },
- this.cells = this.el.select('table.x-date-inner tbody td');
- this.textNodes = this.el.query('table.x-date-inner tbody span');
+ /**
+ * @private
+ */
+ translateValue : function(v) {
+ var ratio = this.getRatio();
+ return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
+ },
- this.mbtn = new Ext.Button({
- text: ' ',
- tooltip: this.monthYearText,
- renderTo: this.el.child('td.x-date-middle', true)
- });
- this.mbtn.el.child('em').addClass('x-btn-arrow');
+ /**
+ * @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;
+ },
- if(this.showToday){
- this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday, this);
- var today = (new Date()).dateFormat(this.format);
- this.todayBtn = new Ext.Button({
- renderTo: this.el.child('td.x-date-bottom', true),
- text: String.format(this.todayText, today),
- tooltip: String.format(this.todayTip, today),
- handler: this.selectToday,
- scope: this
- });
+ /**
+ * @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});
}
- this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
- this.mon(this.eventEl, 'click', this.handleDateClick, this, {delegate: 'a.x-date-date'});
- this.mon(this.mbtn, 'click', this.showMonthPicker, this);
- this.onEnable(true);
},
// private
- createMonthPicker : function(){
- if(!this.monthPicker.dom.firstChild){
- var buf = [''];
- for(var i = 0; i < 6; i++){
- buf.push(
- '', Date.getShortMonthName(i), ' ',
- '', Date.getShortMonthName(i + 6), ' ',
- i === 0 ?
- ' ' :
- ' '
- );
- }
- buf.push(
- ' ',
- '
'
- );
- this.monthPicker.update(buf.join(''));
+ focus : function(){
+ this.focusEl.focus(10);
+ },
- this.mon(this.monthPicker, 'click', this.onMonthClick, this);
- this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);
+ // 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();
+ }
+ // check to see if we're using an auto width
+ if(Ext.isNumber(w)){
+ this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));
+ }
+ this.syncThumb();
+ Ext.slider.MultiSlider.superclass.onResize.apply(this, arguments);
+ },
- this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
- this.mpYears = this.monthPicker.select('td.x-date-mp-year');
+ //private
+ onDisable: function(){
+ Ext.slider.MultiSlider.superclass.onDisable.call(this);
- this.mpMonths.each(function(m, a, i){
- i += 1;
- if((i%2) === 0){
- m.dom.xmonth = 5 + Math.round(i * 0.5);
- }else{
- m.dom.xmonth = Math.round((i-1) * 0.5);
+ 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
- showMonthPicker : function(){
- if(!this.disabled){
- this.createMonthPicker();
- var size = this.el.getSize();
- this.monthPicker.setSize(size);
- this.monthPicker.child('table').setSize(size);
+ //private
+ onEnable: function(){
+ Ext.slider.MultiSlider.superclass.onEnable.call(this);
- this.mpSelMonth = (this.activeDate || this.value).getMonth();
- this.updateMPMonth(this.mpSelMonth);
- this.mpSelYear = (this.activeDate || this.value).getFullYear();
- this.updateMPYear(this.mpSelYear);
+ for (var i=0; i < this.thumbs.length; i++) {
+ var thumb = this.thumbs[i],
+ el = thumb.el;
- this.monthPicker.slideIn('t', {duration:0.2});
+ thumb.enable();
+
+ if (Ext.isIE) {
+ this.innerEl.removeClass(this.disabledClass).dom.disabled = false;
+
+ if (this.thumbHolder) this.thumbHolder.hide();
+
+ el.show();
+ this.syncThumb();
+ }
}
},
- // private
- updateMPYear : function(y){
- this.mpyear = y;
- var ys = this.mpYears.elements;
- for(var i = 1; i <= 10; i++){
- var td = ys[i-1], y2;
- if((i%2) === 0){
- y2 = y + Math.round(i * 0.5);
- td.firstChild.innerHTML = y2;
- td.xyear = y2;
- }else{
- y2 = y - (5-Math.round(i * 0.5));
- td.firstChild.innerHTML = y2;
- td.xyear = y2;
+ /**
+ * 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));
}
- this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
}
},
- // private
- updateMPMonth : function(sm){
- this.mpMonths.each(function(m, a, i){
- m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
- });
+ /**
+ * 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;
},
- // private
- selectMPMonth : function(m){
-
- },
+ /**
+ * Returns an array of values - one for the location of each thumb
+ * @return {Array} The set of thumb values
+ */
+ getValues: function() {
+ var values = [];
- // private
- onMonthClick : function(e, t){
- e.stopEvent();
- var el = new Ext.Element(t), pn;
- if(el.is('button.x-date-mp-cancel')){
- this.hideMonthPicker();
- }
- else if(el.is('button.x-date-mp-ok')){
- var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
- if(d.getMonth() != this.mpSelMonth){
- // 'fix' the JS rolling date conversion if needed
- d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
- }
- this.update(d);
- this.hideMonthPicker();
- }
- else if((pn = el.up('td.x-date-mp-month', 2))){
- this.mpMonths.removeClass('x-date-mp-sel');
- pn.addClass('x-date-mp-sel');
- this.mpSelMonth = pn.dom.xmonth;
- }
- else if((pn = el.up('td.x-date-mp-year', 2))){
- this.mpYears.removeClass('x-date-mp-sel');
- pn.addClass('x-date-mp-sel');
- this.mpSelYear = pn.dom.xyear;
- }
- else if(el.is('a.x-date-mp-prev')){
- this.updateMPYear(this.mpyear-10);
- }
- else if(el.is('a.x-date-mp-next')){
- this.updateMPYear(this.mpyear+10);
+ for (var i=0; i < this.thumbs.length; i++) {
+ values.push(this.thumbs[i].value);
}
+
+ return values;
},
// private
- onMonthDblClick : function(e, t){
- e.stopEvent();
- var el = new Ext.Element(t), pn;
- if((pn = el.up('td.x-date-mp-month', 2))){
- this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
- this.hideMonthPicker();
- }
- else if((pn = el.up('td.x-date-mp-year', 2))){
- this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
- this.hideMonthPicker();
+ beforeDestroy : function(){
+ var thumbs = this.thumbs;
+ for(var i = 0, len = thumbs.length; i < len; ++i){
+ thumbs[i].destroy();
+ thumbs[i] = null;
}
+ Ext.destroyMembers(this, 'endEl', 'innerEl', 'focusEl', '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);
},
- // private
- hideMonthPicker : function(disableAnim){
- if(this.monthPicker){
- if(disableAnim === true){
- this.monthPicker.hide();
- }else{
- this.monthPicker.slideOut('t', {duration:0.2});
- }
+ /**
+ * 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
- showPrevMonth : function(e){
- this.update(this.activeDate.add('mo', -1));
+ 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();
},
- // private
- showNextMonth : function(e){
- this.update(this.activeDate.add('mo', 1));
+ getRatio : function(){
+ var h = this.innerEl.getHeight(),
+ v = this.maxValue - this.minValue;
+ return h/v;
},
- // private
- showPrevYear : function(){
- this.update(this.activeDate.add('y', -1));
+ 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
- showNextYear : function(){
- this.update(this.activeDate.add('y', 1));
- },
+ waitTimer : null,
// private
- handleMouseWheel : function(e){
- e.stopEvent();
- if(!this.disabled){
- var delta = e.getWheelDelta();
- if(delta > 0){
- this.showPrevMonth();
- } else if(delta < 0){
- this.showNextMonth();
- }
- }
+ 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
- handleDateClick : function(e, t){
- e.stopEvent();
- if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
- this.cancelFocus = this.focusOnSelect === false;
- this.setValue(new Date(t.dateValue));
- delete this.cancelFocus;
- this.fireEvent('select', this, this.value);
+ onRender : function(ct, position){
+ var tpl = new Ext.Template(
+ '',
+ '',
+ '',
+ ' ',
+ '',
+ '',
+ ' ',
+ ''
+ );
+
+ this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)
+ : tpl.append(ct, {cls: this.baseCls}, true);
+
+ if(this.id){
+ this.el.dom.id = this.id;
}
- },
+ var inner = this.el.dom.firstChild;
+ this.progressBar = Ext.get(inner.firstChild);
- // private
- selectToday : function(){
- if(this.todayBtn && !this.todayBtn.disabled){
- this.setValue(new Date().clearTime());
- this.fireEvent('select', this, this.value);
+ if(this.textEl){
+ //use an external text el
+ this.textEl = Ext.get(this.textEl);
+ delete this.textTopEl;
+ }else{
+ //setup our internal layered text els
+ this.textTopEl = Ext.get(this.progressBar.dom.firstChild);
+ var textBackEl = Ext.get(inner.childNodes[1]);
+ this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');
+ this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);
+ this.textEl.setWidth(inner.offsetWidth);
}
+ this.progressBar.setHeight(inner.offsetHeight);
},
-
+
// private
- 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]);
- }
- }
+ afterRender : function(){
+ Ext.ProgressBar.superclass.afterRender.call(this);
+ if(this.value){
+ this.updateProgress(this.value, this.text);
+ }else{
+ this.updateText(this.text);
}
},
- // private
- beforeDestroy : function() {
- if(this.rendered){
- Ext.destroy(
- this.keyNav,
- this.monthPicker,
- this.eventEl,
- this.mbtn,
- this.nextRepeater,
- this.prevRepeater,
- this.cells.el,
- this.todayBtn
- );
- delete this.textNodes;
- delete this.cells.elements;
+ /**
+ * Updates the progress bar value, and optionally its text. If the text argument is not specified,
+ * any existing text value will be unchanged. To blank out existing text, pass ''. Note that even
+ * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
+ * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
+ * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
+ * @param {String} text (optional) The string to display in the progress text element (defaults to '')
+ * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
+ * not specified, the default for the class is used (default to false)
+ * @return {Ext.ProgressBar} this
+ */
+ updateProgress : function(value, text, animate){
+ this.value = value || 0;
+ if(text){
+ this.updateText(text);
}
- }
+ if(this.rendered && !this.isDestroyed){
+ var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);
+ this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
+ if(this.textTopEl){
+ //textTopEl should be the same width as the bar so overflow will clip as the bar moves
+ this.textTopEl.removeClass('x-hidden').setWidth(w);
+ }
+ }
+ this.fireEvent('update', this, value, text);
+ return this;
+ },
/**
- * @cfg {String} autoEl @hide
- */
+ * Initiates an auto-updating progress bar. A duration can be specified, in which case the progress
+ * bar will automatically reset after a fixed amount of time and optionally call a callback function
+ * if specified. If no duration is passed in, then the progress bar will run indefinitely and must
+ * be manually cleared by calling {@link #reset}. The wait method accepts a config object with
+ * the following properties:
+ *
+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'
});
-Ext.reg('datepicker', Ext.DatePicker);
-/**
- * @class Ext.LoadMask
- * A simple utility class for generically masking elements while loading data. If the {@link #store}
- * config option is specified, the masking will be automatically synchronized with the store's loading
- * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
- * element's Updater load indicator and will be destroyed after the initial load.
- * Example usage:
- *
-// Basic mask:
-var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
-myMask.show();
-
- * @constructor
- * Create a new LoadMask
- * @param {Mixed} el The element or DOM node, or its id
- * @param {Object} config The config object
- */
-Ext.LoadMask = function(el, config){
- this.el = Ext.get(el);
- Ext.apply(this, config);
- if(this.store){
- this.store.on({
- scope: this,
- beforeload: this.onBeforeLoad,
- load: this.onLoad,
- exception: this.onLoad
- });
- this.removeMask = Ext.value(this.removeMask, false);
- }else{
- var um = this.el.getUpdater();
- um.showLoadIndicator = false; // disable the default indicator
- um.on({
- scope: this,
- beforeupdate: this.onBeforeLoad,
- update: this.onLoad,
- failure: this.onLoad
- });
- this.removeMask = Ext.value(this.removeMask, true);
- }
-};
+//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!');
+ }
+});
-Ext.LoadMask.prototype = {
- /**
- * @cfg {Ext.data.Store} store
- * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
- * hidden on either load sucess, or load fail.
- */
- /**
- * @cfg {Boolean} removeMask
- * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
- * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
- */
- /**
- * @cfg {String} msg
- * The text to display in a centered loading message box (defaults to 'Loading...')
- */
- msg : 'Loading...',
- /**
- * @cfg {String} msgCls
- * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
- */
- msgCls : 'x-mask-loading',
+//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;
+ },
/**
- * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
- * @type Boolean
+ * Returns true if the progress bar is currently in a {@link #wait} operation
+ * @return {Boolean} True if waiting, else false
*/
- disabled: false,
+ isWaiting : function(){
+ return this.waitTimer !== null;
+ },
/**
- * Disables the mask to prevent it from being displayed
+ * 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
*/
- disable : function(){
- this.disabled = true;
+ updateText : function(text){
+ this.text = text || ' ';
+ if(this.rendered){
+ this.textEl.update(this.text);
+ }
+ return this;
},
-
+
/**
- * Enables the mask so that it can be displayed
+ * 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.
*/
- enable : function(){
- this.disabled = false;
- },
-
- // private
- onLoad : function(){
- this.el.unmask(this.removeMask);
- },
-
- // private
- onBeforeLoad : function(){
- if(!this.disabled){
- this.el.mask(this.msg, this.msgCls);
+ syncProgressBar : function(){
+ if(this.value){
+ this.updateProgress(this.value, this.text);
}
+ return this;
},
/**
- * Show this LoadMask over the configured Element.
+ * 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
*/
- show: function(){
- this.onBeforeLoad();
+ 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;
},
/**
- * Hide this LoadMask.
+ * 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
*/
- hide: function(){
- this.onLoad();
+ reset : function(hide){
+ this.updateProgress(0);
+ if(this.textTopEl){
+ this.textTopEl.addClass('x-hidden');
+ }
+ this.clearTimer();
+ if(hide === true){
+ this.hide();
+ }
+ return this;
},
-
+
// private
- destroy : function(){
- if(this.store){
- this.store.un('beforeload', this.onBeforeLoad, this);
- this.store.un('load', this.onLoad, this);
- this.store.un('exception', this.onLoad, this);
- }else{
- var um = this.el.getUpdater();
- um.un('beforeupdate', this.onBeforeLoad, this);
- um.un('update', this.onLoad, this);
- um.un('failure', this.onLoad, this);
+ 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);
}
-};/**
- * @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(
- '',
- '',
- '',
- ' ',
- '',
- '',
- ' ',
- ''
- );
-
- this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)
- : tpl.append(ct, {cls: this.baseCls}, true);
-
- if(this.id){
- this.el.dom.id = this.id;
- }
- var inner = this.el.dom.firstChild;
- this.progressBar = Ext.get(inner.firstChild);
-
- if(this.textEl){
- //use an external text el
- this.textEl = Ext.get(this.textEl);
- delete this.textTopEl;
- }else{
- //setup our internal layered text els
- this.textTopEl = Ext.get(this.progressBar.dom.firstChild);
- var textBackEl = Ext.get(inner.childNodes[1]);
- this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');
- this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);
- this.textEl.setWidth(inner.offsetWidth);
- }
- this.progressBar.setHeight(inner.offsetHeight);
- },
-
- // private
- afterRender : function(){
- Ext.ProgressBar.superclass.afterRender.call(this);
- if(this.value){
- this.updateProgress(this.value, this.text);
- }else{
- this.updateText(this.text);
- }
- },
-
- /**
- * Updates the progress bar value, and optionally its text. If the text argument is not specified,
- * any existing text value will be unchanged. To blank out existing text, pass ''. Note that even
- * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
- * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
- * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
- * @param {String} text (optional) The string to display in the progress text element (defaults to '')
- * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
- * not specified, the default for the class is used (default to false)
- * @return {Ext.ProgressBar} this
- */
- updateProgress : function(value, text, animate){
- this.value = value || 0;
- if(text){
- this.updateText(text);
- }
- if(this.rendered && !this.isDestroyed){
- var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);
- this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
- if(this.textTopEl){
- //textTopEl should be the same width as the bar so overflow will clip as the bar moves
- this.textTopEl.removeClass('x-hidden').setWidth(w);
- }
- }
- this.fireEvent('update', this, value, text);
- return this;
- },
-
- /**
- * Initiates an auto-updating progress bar. A duration can be specified, in which case the progress
- * bar will automatically reset after a fixed amount of time and optionally call a callback function
- * if specified. If no duration is passed in, then the progress bar will run indefinitely and must
- * be manually cleared by calling {@link #reset}. The wait method accepts a config object with
- * the following properties:
- *
-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