      1 /**
      2  * Copyright (c) 2008, Steven Chim
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
      6  *
      7  *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
      8  *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
      9  *     * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
     10  *
     11  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     12  */
     13
     14 /**
     15   * Ext.ux.form.Spinner Class
     16 	*
     17 	* @author  Steven Chim
     18 	* @version Spinner.js 2008-08-27 v0.35
     19   *
     20   * @class Ext.ux.form.Spinner
     21   * @extends Ext.form.TriggerField
     22   */
     23
     24 Ext.namespace("Ext.ux.form");
     25
     26 Ext.ux.form.Spinner = function(config){
     27 	Ext.ux.form.Spinner.superclass.constructor.call(this, config);
     28 	this.addEvents({
     29 		'spin' : true,
     30 		'spinup' : true,
     31 		'spindown' : true
     32 	});
     33 }
     34
     35 Ext.extend(Ext.ux.form.Spinner, Ext.form.TriggerField, {
     36 	triggerClass : 'x-form-spinner-trigger',
     37 	splitterClass : 'x-form-spinner-splitter',
     38
     39 	alternateKey : Ext.EventObject.shiftKey,
     40 	strategy : undefined,
     41
     42 	//private
     43 	onRender : function(ct, position){
     44 		Ext.ux.form.Spinner.superclass.onRender.call(this, ct, position);
     45
     46 		this.splitter = this.wrap.createChild({tag:'div', cls:this.splitterClass, style:'width:13px; height:2px;'});
     47 		this.splitter.show().setRight( (Ext.isIE) ? 1 : 2 );
     48 		this.splitter.show().setTop(10);
     49
     50 		this.proxy = this.trigger.createProxy('', this.splitter, true);
     51 		this.proxy.addClass("x-form-spinner-proxy");
     52 		this.proxy.setStyle('left','0px');
     53 		this.proxy.setSize(14, 1);
     54 		this.proxy.hide();
     55 		this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {dragElId: this.proxy.id});
     56
     57 		this.initSpinner();
     58 	},
     59
     60 	//private
     61 	initTrigger : function(){
     62 		this.trigger.addClassOnOver('x-form-trigger-over');
     63 		this.trigger.addClassOnClick('x-form-trigger-click');
     64 	},
     65
     66 	//private
     67 	initSpinner : function(){
     68 		this.keyNav = new Ext.KeyNav(this.el, {
     69 			"up" : function(e){
     70 				e.preventDefault();
     71 				this.onSpinUp();
     72 			},
     73
     74 			"down" : function(e){
     75 				e.preventDefault();
     76 				this.onSpinDown();
     77 			},
     78
     79 			"pageUp" : function(e){
     80 				e.preventDefault();
     81 				this.onSpinUpAlternate();
     82 			},
     83
     84 			"pageDown" : function(e){
     85 				e.preventDefault();
     86 				this.onSpinDownAlternate();
     87 			},
     88
     89 			scope : this
     90 		});
     91
     92 		this.repeater = new Ext.util.ClickRepeater(this.trigger);
     93 		this.repeater.on("click", this.onTriggerClick, this, {preventDefault:true});
     94 		this.trigger.on("mouseover", this.onMouseOver, this, {preventDefault:true});
     95 		this.trigger.on("mouseout",  this.onMouseOut,  this, {preventDefault:true});
     96 		this.trigger.on("mousemove", this.onMouseMove, this, {preventDefault:true});
     97 		this.trigger.on("mousedown", this.onMouseDown, this, {preventDefault:true});
     98 		this.trigger.on("mouseup",   this.onMouseUp,   this, {preventDefault:true});
     99 		this.wrap.on("mousewheel",   this.handleMouseWheel, this);
    100
    101 		this.dd.setXConstraint(0, 0, 10)
    102 		this.dd.setYConstraint(1500, 1500, 10);
    103 		this.dd.endDrag = this.endDrag.createDelegate(this);
    104 		this.dd.startDrag = this.startDrag.createDelegate(this);
    105 		this.dd.onDrag = this.onDrag.createDelegate(this);
    106
    107         /*
    108         jsakalos suggestion
    109         http://extjs.com/forum/showthread.php?p=121850#post121850 */
    110         if('object' == typeof this.strategy && this.strategy.xtype) {
    111             switch(this.strategy.xtype) {
    112                 case 'number':
    113                     this.strategy = new Ext.ux.form.Spinner.NumberStrategy(this.strategy);
    114 	                break;
    115
    116                 case 'date':
    117                     this.strategy = new Ext.ux.form.Spinner.DateStrategy(this.strategy);
    118 	                break;
    119
    120                 case 'time':
    121                     this.strategy = new Ext.ux.form.Spinner.TimeStrategy(this.strategy);
    122                 	break;
    123
    124                 default:
    125                     delete(this.strategy);
    126                 	break;
    127             }
    128             delete(this.strategy.xtype);
    129         }
    130
    131 		if(this.strategy == undefined){
    132 			this.strategy = new Ext.ux.form.Spinner.NumberStrategy();
    133 		}
    134 	},
    135
    136 	//private
    137 	onMouseOver : function(){
    138 		if(this.disabled){
    139 			return;
    140 		}
    141 		var middle = this.getMiddle();
    142 		this.__tmphcls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
    143 		this.trigger.addClass(this.__tmphcls);
    144 	},
    145
    146 	//private
    147 	onMouseOut : function(){
    148 		this.trigger.removeClass(this.__tmphcls);
    149 	},
    150
    151 	//private
    152 	onMouseMove : function(){
    153 		if(this.disabled){
    154 			return;
    155 		}
    156 		var middle = this.getMiddle();
    157 		if( ((Ext.EventObject.getPageY() > middle) && this.__tmphcls == "x-form-spinner-overup") ||
    158 			((Ext.EventObject.getPageY() < middle) && this.__tmphcls == "x-form-spinner-overdown")){
    159 		}
    160 	},
    161
    162 	//private
    163 	onMouseDown : function(){
    164 		if(this.disabled){
    165 			return;
    166 		}
    167 		var middle = this.getMiddle();
    168 		this.__tmpccls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
    169 		this.trigger.addClass(this.__tmpccls);
    170 	},
    171
    172 	//private
    173 	onMouseUp : function(){
    174 		this.trigger.removeClass(this.__tmpccls);
    175 	},
    176
    177 	//private
    178 	onTriggerClick : function(){
    179 		if(this.disabled || this.getEl().dom.readOnly){
    180 			return;
    181 		}
    182 		var middle = this.getMiddle();
    183 		var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';
    184 		this['onSpin'+ud]();
    185 	},
    186
    187 	//private
    188 	getMiddle : function(){
    189 		var t = this.trigger.getTop();
    190 		var h = this.trigger.getHeight();
    191 		var middle = t + (h/2);
    192 		return middle;
    193 	},
    194
    195 	//private
    196 	//checks if control is allowed to spin
    197 	isSpinnable : function(){
    198 		if(this.disabled || this.getEl().dom.readOnly){
    199 			Ext.EventObject.preventDefault();	//prevent scrolling when disabled/readonly
    200 			return false;
    201 		}
    202 		return true;
    203 	},
    204
    205 	handleMouseWheel : function(e){
    206 		//disable scrolling when not focused
    207 		if(this.wrap.hasClass('x-trigger-wrap-focus') == false){
    208 			return;
    209 		}
    210
    211 		var delta = e.getWheelDelta();
    212 		if(delta > 0){
    213 			this.onSpinUp();
    214 			e.stopEvent();
    215 		} else if(delta < 0){
    216 			this.onSpinDown();
    217 			e.stopEvent();
    218 		}
    219 	},
    220
    221 	//private
    222 	startDrag : function(){
    223 		this.proxy.show();
    224 		this._previousY = Ext.fly(this.dd.getDragEl()).getTop();
    225 	},
    226
    227 	//private
    228 	endDrag : function(){
    229 		this.proxy.hide();
    230 	},
    231
    232 	//private
    233 	onDrag : function(){
    234 		if(this.disabled){
    235 			return;
    236 		}
    237 		var y = Ext.fly(this.dd.getDragEl()).getTop();
    238 		var ud = '';
    239
    240 		if(this._previousY > y){ud = 'Up';}         //up
    241 		if(this._previousY < y){ud = 'Down';}       //down
    242
    243 		if(ud != ''){
    244 			this['onSpin'+ud]();
    245 		}
    246
    247 		this._previousY = y;
    248 	},
    249
    250 	//private
    251 	onSpinUp : function(){
    252 		if(this.isSpinnable() == false) {
    253 			return;
    254 		}
    255 		if(Ext.EventObject.shiftKey == true){
    256 			this.onSpinUpAlternate();
    257 			return;
    258 		}else{
    259 			this.strategy.onSpinUp(this);
    260 		}
    261 		this.fireEvent("spin", this);
    262 		this.fireEvent("spinup", this);
    263 	},
    264
    265 	//private
    266 	onSpinDown : function(){
    267 		if(this.isSpinnable() == false) {
    268 			return;
    269 		}
    270 		if(Ext.EventObject.shiftKey == true){
    271 			this.onSpinDownAlternate();
    272 			return;
    273 		}else{
    274 			this.strategy.onSpinDown(this);
    275 		}
    276 		this.fireEvent("spin", this);
    277 		this.fireEvent("spindown", this);
    278 	},
    279
    280 	//private
    281 	onSpinUpAlternate : function(){
    282 		if(this.isSpinnable() == false) {
    283 			return;
    284 		}
    285 		this.strategy.onSpinUpAlternate(this);
    286 		this.fireEvent("spin", this);
    287 		this.fireEvent("spinup", this);
    288 	},
    289
    290 	//private
    291 	onSpinDownAlternate : function(){
    292 		if(this.isSpinnable() == false) {
    293 			return;
    294 		}
    295 		this.strategy.onSpinDownAlternate(this);
    296 		this.fireEvent("spin", this);
    297 		this.fireEvent("spindown", this);
    298 	}
    299
    300 });
    301
    302 Ext.reg('uxspinner', Ext.ux.form.Spinner);
    303
    304 /**
    305  * Copyright (c) 2008, Steven Chim
    306  * All rights reserved.
    307  *
    308  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    309  *
    310  *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    311  *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    312  *     * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
    313  *
    314  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    315  */
    316
    317 /***
    318  * Abstract Strategy
    319  */
    320 Ext.ux.form.Spinner.Strategy = function(config){
    321 	Ext.apply(this, config);
    322 };
    323
    324 Ext.extend(Ext.ux.form.Spinner.Strategy, Ext.util.Observable, {
    325 	defaultValue : 0,
    326 	minValue : undefined,
    327 	maxValue : undefined,
    328 	incrementValue : 1,
    329 	alternateIncrementValue : 5,
    330 	validationTask : new Ext.util.DelayedTask(),
    331
    332 	onSpinUp : function(field){
    333 		this.spin(field, false, false);
    334 	},
    335
    336 	onSpinDown : function(field){
    337 		this.spin(field, true, false);
    338 	},
    339
    340 	onSpinUpAlternate : function(field){
    341 		this.spin(field, false, true);
    342 	},
    343
    344 	onSpinDownAlternate : function(field){
    345 		this.spin(field, true, true);
    346 	},
    347
    348 	spin : function(field, down, alternate){
    349 		this.validationTask.delay(500, function(){field.validate()});
    350 		//extend
    351 	},
    352
    353 	fixBoundries : function(value){
    354 		return value;
    355 		//overwrite
    356 	}
    357
    358 });
    359
    360 /***
    361  * Concrete Strategy: Numbers
    362  */
    363 Ext.ux.form.Spinner.NumberStrategy = function(config){
    364 	Ext.ux.form.Spinner.NumberStrategy.superclass.constructor.call(this, config);
    365 };
    366
    367 Ext.extend(Ext.ux.form.Spinner.NumberStrategy, Ext.ux.form.Spinner.Strategy, {
    368
    369     allowDecimals : true,
    370     decimalPrecision : 2,
    371
    372 	spin : function(field, down, alternate){
    373 		Ext.ux.form.Spinner.NumberStrategy.superclass.spin.call(this, field, down, alternate);
    374
    375 		var v = parseFloat(field.getValue());
    376 		var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
    377
    378 		(down == true) ? v -= incr : v += incr ;
    379 		v = (isNaN(v)) ? this.defaultValue : v;
    380 		v = this.fixBoundries(v);
    381 		field.setRawValue(v);
    382 	},
    383
    384 	fixBoundries : function(value){
    385 		var v = value;
    386
    387 		if(this.minValue != undefined && v < this.minValue){
    388 			v = this.minValue;
    389 		}
    390 		if(this.maxValue != undefined && v > this.maxValue){
    391 			v = this.maxValue;
    392 		}
    393
    394 		return this.fixPrecision(v);
    395 	},
    396
    397     // private
    398     fixPrecision : function(value){
    399         var nan = isNaN(value);
    400         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
    401             return nan ? '' : value;
    402         }
    403         return value.toFixed(this.decimalPrecision);
    404     }
    405 });
    406
    407
    408 /***
    409  * Concrete Strategy: Date
    410  */
    411 Ext.ux.form.Spinner.DateStrategy = function(config){
    412 	Ext.ux.form.Spinner.DateStrategy.superclass.constructor.call(this, config);
    413 };
    414
    415 Ext.extend(Ext.ux.form.Spinner.DateStrategy, Ext.ux.form.Spinner.Strategy, {
    416 	defaultValue : new Date(),
    417 	format : "Y-m-d",
    418 	incrementValue : 1,
    419 	incrementConstant : Date.DAY,
    420 	alternateIncrementValue : 1,
    421 	alternateIncrementConstant : Date.MONTH,
    422
    423 	spin : function(field, down, alternate){
    424 		Ext.ux.form.Spinner.DateStrategy.superclass.spin.call(this);
    425
    426 		var v = field.getRawValue();
    427
    428 		v = Date.parseDate(v, this.format);
    429 		var dir = (down == true) ? -1 : 1 ;
    430 		var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
    431 		var dtconst = (alternate == true) ? this.alternateIncrementConstant : this.incrementConstant;
    432
    433 		if(typeof this.defaultValue == 'string'){
    434 			this.defaultValue = Date.parseDate(this.defaultValue, this.format);
    435 		}
    436
    437 		v = (v) ? v.add(dtconst, dir*incr) : this.defaultValue;
    438
    439 		v = this.fixBoundries(v);
    440 		field.setRawValue(Ext.util.Format.date(v,this.format));
    441 	},
    442
    443 	//private
    444 	fixBoundries : function(date){
    445 		var dt = date;
    446 		var min = (typeof this.minValue == 'string') ? Date.parseDate(this.minValue, this.format) : this.minValue ;
    447 		var max = (typeof this.maxValue == 'string') ? Date.parseDate(this.maxValue, this.format) : this.maxValue ;
    448
    449 		if(this.minValue != undefined && dt < min){
    450 			dt = min;
    451 		}
    452 		if(this.maxValue != undefined && dt > max){
    453 			dt = max;
    454 		}
    455
    456 		return dt;
    457 	}
    458
    459 });
    460
    461 /***
    462  * Concrete Strategy: Time
    463  */
    464 Ext.ux.form.Spinner.TimeStrategy = function(config){
    465 	Ext.ux.form.Spinner.TimeStrategy.superclass.constructor.call(this, config);
    466 };
    467
    468 Ext.extend(Ext.ux.form.Spinner.TimeStrategy, Ext.ux.form.Spinner.DateStrategy, {
    469 	format : "H:i",
    470 	incrementValue : 1,
    471 	incrementConstant : Date.MINUTE,
    472 	alternateIncrementValue : 1,
    473 	alternateIncrementConstant : Date.HOUR
    474 });
    475
    476 Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
    477     lines:false,
    478     borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
    479     cls:'x-column-tree',
    480
    481     onRender : function(){
    482         Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
    483         this.headers = this.body.createChild(
    484             {cls:'x-tree-headers'},this.innerCt.dom);
    485
    486         var cols = this.columns, c;
    487         var totalWidth = 0;
    488
    489         for(var i = 0, len = cols.length; i < len; i++){
    490              c = cols[i];
    491              totalWidth += c.width;
    492              this.headers.createChild({
    493                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
    494                  cn: {
    495                      cls:'x-tree-hd-text',
    496                      html: c.header
    497                  },
    498                  style:'width:'+(c.width-this.borderWidth)+'px;'
    499              });
    500         }
    501         this.headers.createChild({cls:'x-clear'});
    502         // prevent floats from wrapping when clipped
    503         this.headers.setWidth(totalWidth);
    504         this.innerCt.setWidth(totalWidth);
    505     }
    506 });
    507
    508 Ext.tree.ColumnTreeNode = Ext.extend(Ext.tree.TreeNode, {
    509
    510 	setColumnValue: function(index, value) {
    511 		var t = this.getOwnerTree();
    512 		var oldValue = this[t.columns[index].dataIndex];
    513 		this[t.columns[index].dataIndex] = value;
    514 		this.attributes[[t.columns[index].dataIndex]] = value;
    515 		if (this.rendered) {
    516 			this.ui.onColumnValueChange(this, index, value, oldValue);
    517 		}
    518 		this.fireEvent('columnvaluechange', this, index, value, oldValue);
    519 	}
    520 });
    521
    522 Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
    523     focus: Ext.emptyFn, // prevent odd scrolling behavior
    524
    525 	onColumnValueChange: function(n, colIndex, value, oldValue) {
    526 		if (this.rendered) {
    527 			var c = n.getOwnerTree().columns[colIndex];
    528 			this.columnNodes[colIndex].innerHTML = (c.renderer ? c.renderer(value, n, null) : value);
    529 		}
    530 	},
    531
    532     renderElements : function(n, a, targetNode, bulkRender){
    533         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
    534
    535         var t = n.getOwnerTree();
    536         var cols = t.columns;
    537         var bw = t.borderWidth;
    538         var c = cols[0];
    539
    540         var cb = typeof a.checked == 'boolean';
    541 		var href = a.href ? a.href : Ext.isGecko ? "" : "#";
    542
    543         var buf = [
    544              '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
    545                 '<div class="x-tree-col" style="width:',c.width-bw,'px;">',
    546                     '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
    547                     '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
    548                     '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
    549                     cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
    550                     '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
    551                     a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
    552                     '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
    553                 "</div>"];
    554          for(var i = 1, len = cols.length; i < len; i++){
    555              c = cols[i];
    556
    557              buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
    558                         '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
    559                       "</div>");
    560          }
    561          buf.push(
    562             '<div class="x-clear"></div></div>',
    563             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
    564             "</li>");
    565
    566         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
    567             this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
    568                                 n.nextSibling.ui.getEl(), buf.join(""));
    569         }else{
    570             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
    571         }
    572
    573         this.elNode = this.wrap.childNodes[0];
    574         this.ctNode = this.wrap.childNodes[1];
    575         var cs = this.elNode.firstChild.childNodes;
    576         this.indentNode = cs[0];
    577         this.ecNode = cs[1];
    578         this.iconNode = cs[2];
    579 		var index = 3;
    580         if(cb){
    581             this.checkbox = cs[3];
    582 			// fix for IE6
    583 			this.checkbox.defaultChecked = this.checkbox.checked;
    584             index++;
    585         }
    586         this.anchor = cs[index];
    587 		this.columnNodes = [cs[index].firstChild];
    588 		for(var i = 1, len = cols.length; i < len; i++){
    589 			this.columnNodes[i] = this.elNode.childNodes[i].firstChild;
    590 		}
    591     }
    592 });
    593
    594 Ext.form.FileUploadField = Ext.extend(Ext.form.TextField,  {
    595     /**
    596      * @cfg {String} buttonText The button text to display on the upload button (defaults to
    597      * 'Browse...').  Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
    598      * value will be used instead if available.
    599      */
    600     buttonText: 'Browse...',
    601     /**
    602      * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
    603      * text field (defaults to false).  If true, all inherited TextField members will still be available.
    604      */
    605     buttonOnly: false,
    606     /**
    607      * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
    608      * (defaults to 3).  Note that this only applies if {@link #buttonOnly} = false.
    609      */
    610     buttonOffset: 3,
    611     /**
    612      * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.
    613      */
    614
    615     // private
    616     readOnly: true,
    617
    618     /**
    619      * @hide
    620      * @method autoSize
    621      */
    622     autoSize: Ext.emptyFn,
    623
    624     // private
    625     initComponent: function(){
    626         Ext.form.FileUploadField.superclass.initComponent.call(this);
    627
    628         this.addEvents(
    629             /**
    630              * @event fileselected
    631              * Fires when the underlying file input field's value has changed from the user
    632              * selecting a new file from the system file selection dialog.
    633              * @param {Ext.form.FileUploadField} this
    634              * @param {String} value The file value returned by the underlying file input field
    635              */
    636             'fileselected'
    637         );
    638     },
    639
    640     // private
    641     onRender : function(ct, position){
    642         Ext.form.FileUploadField.superclass.onRender.call(this, ct, position);
    643
    644         this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});
    645         this.el.addClass('x-form-file-text');
    646         this.el.dom.removeAttribute('name');
    647
    648         this.fileInput = this.wrap.createChild({
    649             id: this.getFileInputId(),
    650             name: this.name||this.getId(),
    651             cls: 'x-form-file',
    652             tag: 'input',
    653             type: 'file',
    654             size: 1
    655         });
    656
    657         var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
    658             text: this.buttonText
    659         });
    660         this.button = new Ext.Button(Ext.apply(btnCfg, {
    661             renderTo: this.wrap
    662         }));
    663
    664         if(this.buttonOnly){
    665             this.el.hide();
    666             this.wrap.setWidth(this.button.getEl().getWidth());
    667         }
    668
    669         this.fileInput.on('change', function(){
    670             var v = this.fileInput.dom.value;
    671             this.setValue(v);
    672             this.fireEvent('fileselected', this, v);
    673         }, this);
    674     },
    675
    676     // private
    677     getFileInputId: function(){
    678         return this.id+'-file';
    679     },
    680
    681     // private
    682     onResize : function(w, h){
    683         Ext.form.FileUploadField.superclass.onResize.call(this, w, h);
    684
    685         this.wrap.setWidth(w);
    686
    687         if(!this.buttonOnly){
    688             var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;
    689             this.el.setWidth(w);
    690         }
    691     },
    692
    693     // private
    694     preFocus : Ext.emptyFn,
    695
    696     // private
    697     getResizeEl : function(){
    698         return this.wrap;
    699     },
    700
    701     // private
    702     getPositionEl : function(){
    703         return this.wrap;
    704     },
    705
    706     // private
    707     alignErrorIcon : function(){
    708         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
    709     }
    710
    711 });
    712 Ext.reg('fileuploadfield', Ext.form.FileUploadField);
    713
    714 /**
    715   * Ext.ux.FullProgressBar Class
    716   *
    717   * @author Damien Churchill <damoxc@gmail.com>
    718   * @version 1.2
    719   *
    720   * @class Ext.deluge.ProgressBar
    721   * @extends Ext.ProgressBar
    722   * @constructor
    723   * @param {Object} config Configuration options
    724   */
    725 Ext.ux.FullProgressBar = Ext.extend(Ext.ProgressBar, {
    726 	initComponent: function() {
    727 		Ext.ux.FullProgressBar.superclass.initComponent.call(this);
    728 	},
    729
    730 	updateProgress: function(value, text, animate) {
    731 		this.value = value || 0;
    732 		if (text) {
    733 			this.updateText(text);
    734 		}
    735
    736 		if (this.rendered) {
    737 			var w = Math.floor(value*this.el.dom.firstChild.offsetWidth / 100.0);
    738 	        this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
    739 	        if (this.textTopEl) {
    740 	            //textTopEl should be the same width as the bar so overflow will clip as the bar moves
    741 	            this.textTopEl.removeClass('x-hidden').setWidth(w);
    742 	        }
    743 		}
    744 		this.fireEvent('update', this, value, text);
    745 		return this;
    746 	}
    747 });
    748 Ext.reg('fullprogressbar', Ext.ux.FullProgressBar);
    749
    750
    751 // Allow radiogroups to be treated as a single form element.
    752 Ext.override(Ext.form.RadioGroup, {
    753
    754 	afterRender: function() {
    755 		var that = this;
    756 		this.items.each(function(i) {
    757 			that.relayEvents(i, ['check']);
    758 		});
    759 		Ext.form.RadioGroup.superclass.afterRender.call(this)
    760 	},
    761
    762 	getName: function() {
    763 		return this.items.first().getName();
    764 	},
    765
    766 	getValue: function() {
    767 		var v;
    768
    769 		this.items.each(function(item) {
    770 			v = item.getRawValue();
    771 			return !item.getValue();
    772 		});
    773
    774 		return v;
    775 	},
    776
    777 	setValue: function(v) {
    778 		if (!this.items.each) return;
    779 		this.items.each(function(item) {
    780 			item.setValue((item.getRawValue() === 'true') == v);
    781 		});
    782 	}
    783 });
    784