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