/*
* $Id$
*/
Ext.namespace('NGMP.form');

NGMP.form.PlainField = Ext.extend(Ext.form.Field,{
	defaultAutoCreate:{
		tab:'div',
		cls:'x-form-plainfield'
	},
	initComponent: function() {
		NGMP.form.PlainField.superclass.initComponent.call(this);
		this.addEvents({
			'load':true
		});
	},
	initValue:function() {
		if (this.value !== undefined) {
			this.setValue(this.value);
		} else {
			if (this.el.dom.innerHTML.length>0) {
				this.setValue(this.el.dom.value);
			}
		}
	},
	setValue:function(A) {
		this.value = A;
		if (this.rendered) {
			this.el.update(A);
		}
	 }
});

NGMP.form.SelectLink = function(A) {
	if (!A) {
		A = {};
	}
	A.store = new NGMP.data.JsonStore({
		url:'ngmp.php',
		baseParams: {
			query:'',
			func:'link'
		},
		root:'results',
		totalProperty:'total',
		fields:['link_id','link_type','link_and_type','type_name'],
		remoteSort:true
	});

	A.displayField="type_name";
	A.valueField="link_and_type";
	A.hiddenName="link";
	A.typeAhead=true;
	A.triggerAction="all";
	A.width=400;
	A.selectOnFocus=true;
	A.fieldLabel="Link";
	NGMP.form.SelectLink.superclass.constructor.call(this,A);
};
Ext.extend(NGMP.form.SelectLink,Ext.form.ComboBox); 

NGMP.form.DateTimeField = new Ext.extend(Ext.form.Field, {
	defaultAutoCreate:{
		tab:'div',
		cls:'x-form-plainfield'
	},
	initComponent: function() {
		NGMP.form.DateTimeField.superclass.initComponent.call(this);
		this.addEvents({
			'load':true
		});
	},
	initValue:function() {
		if (this.value !== undefined) {
			this.setValue(this.value);
		} else {
			if (this.el.dom.innerHTML.length>0) {
				this.setValue(this.el.dom.value);
			}
		}
	},
	setValue:function(A) {
		var B = new Date(A*1000);
		B = B.format("Y/m/d H:i");
		this.value = B;
		if (this.rendered) {
			this.el.update(B);
		}
	 }
});

Ext.reg('ngmp-plainfield', NGMP.form.PlainField);

NGMP.form.ComboBox = Ext.extend(Ext.form.ComboBox, {
	setRemoteText: function(A) {
		this.lastSelectionText = A;
	}
});

NGMP.form.ComboBoxMulti = function(A) {
	Ext.apply(A);
	this.typeAhead = false;

	this.minChars = 2;
	this.hideTrigger = true;
	this.defaultAutoCreate = {
		tag:'textarea',
		autocomplete:'off'
	};
	NGMP.form.ComboBoxMulti.superclass.constructor.call(this,A);
};

NGMP.form.ComboBoxMulti = Ext.extend(NGMP.form.ComboBoxMulti, Ext.form.ComboBox, {
	getPosition: function() {
		if (document.selection) {
			var A = document.selection.createRange();
			var B = A.duplicate();
			B.moveToElementText(this.el.dom);
			B.setEndPoint("EndToEnd",A);
			return B.text.length;
		} else {
			return this.el.dom.selectionEnd;
		}
	},
	getActiveRange:function() {
		var B = this.sep;
		var D = this.getPosition();
		var A = this.getRawValue();
		var C = D;
		while (C>0&&A.charAt(C)!=B) {
			--C;
		}
		if (C>0) {
			C++;
		}
		return {
			left:C,
			right:D
		};
	},
	getActiveEntry:function() {
		var A = this.getActiveRange();
		return this.getRawValue().substring(A.left,A.right).replace(/^s+|s+$/g,"");
  	},
	replaceActiveEntry:function() {
		var B=this.getActiveRange();
		var A=this.getRawValue();
		if (this.preventDuplicates && A.indexOf(C)>=0) {
			return;
		}
		var E=(this.sep == " "?"":" ");
		this.setValue(A.substring(0,B.left)+(B.left>0?E:"")+C+this.sep+E+A.substring(B.right));

		var D = B.left + C.length + 2 + E.length;
		this.selectText.defer(200, this, [D,D]);
	},
	onSelect:function(A,B) {
		if (this.fireEvent('beforeselect',this,A,B) !== false) {
			var C = Ext.util.Format.htmlDecode(A.data[this.valueField || this.displayField]);
			if (this.sep) {
				this.replaceActiveEntry(C);
			} else {
				this.setValue(C);
			}
			this.collapse();
			this.fireEvent('select', this,A,B);
		}
	},
	initQuery:function() {
		this.doQuery(this.sep?this.getActiveEntry():this.getRawValue());
	}
});

Ext.reg('combomulti', NGMP.form.ComboBoxMulti);

NGMP.form.HtmlComponent = Ext.extend(Ext.BoxComponent, {
	onRender:function(B,A) {
		this.el = B.createChild({
			tag:'div',
			html:this.html
		});
	}
});

NGMP.form.SelectUser = function(A) {
	Ext.apply(this, A);
	this.store = new Ext.data.Store({
		proxy: new Ext.data.HttpProxy({
			url:'ngmp.ph',
			baseParams:{
				func:'authGetUsers'
			},
			reader: new Ext.data.JsonReader({
				root:'results',
				totalProperty:'total',
				id:'id'
			},[{
				name:'id'
			},{
				name:'email'
			},{
				name:'username'
			}]),
			remoteSort:true	
		})
	});
	this.store.setDefaultSort('name','asc');
	this.setRemoteValue(NGMP.preferences.settings.uid,NGMP.preferences.settings.real_name);
	NGMP.form.SelectUser.superclass.constructor.call(this, {
		displayField:'name',
		hiddenName:'user_id',
		value:NGMP.preferences.settings.uid,
		typeAhead:true,
		valueField:'id',
		triggerAction:'all',
		selectOnFocus:true,
		forceSelection:true,
		pageSize:parseInt(NGMP.preferences.settings.rpp,0)
	});
};
Ext.extend(NGMP.form.SelectUser,NGMP.form.ComboBox, {
	setRemoteValue:function(A,B) {
		var D=Ext.data.Record.create([{name:'id'},{name:'name'}]);
		var C = new D({id:A, name:B});
		this.store.add(C);
		this.setValue(A);
		}
});

NGMP.form.AddressField=Ext.extend(Ext.form.TriggerField,{
    type:"To",triggerClass:"x-form-elipsis-trigger",defaultAutoCreate:{
        tag:"input",type:"text",size:"10",autocomplete:"off"
    }
    ,initComponent:function(){
        NGMP.form.AddressField.superclass.initComponent.call(this);
    }
    ,menuListeners:{
        ok:function(A,B){
            this.setValue(B);
        }
        ,show:function(){
            this.onFocus();
        }
        ,hide:function(){
            this.focus.defer(10,this);
            var A=this.menuListeners;
            this.addressWindow.un("ok",A.ok,this);
            this.addressWindow.un("show",A.show,this);
            this.addressWindow.un("hide",A.hide,this);
        }
        
    }
    ,validateBlur:function(){
        return !this.addressWindow||!this.addressWindow.isVisible();
    }
    ,onTriggerClick:function(){
        if(this.disabled){
            return;
        }
        if(this.addressWindow===null){
            this.addressWindow=Ext.getCmp("addressWindow");
            if(!this.addressWindow){
                this.addressWindow=new NGMP.dialog.AddressWindow(this.contactsStore);
            }
            
        }
        this.addressWindow.on(Ext.apply({
            
        }
        ,this.menuListeners,{
            scope:this
        }
        ));
        this.addressWindow.show({
            type:this.type,value:this.getValue()
        });
    }
    
}
);
Ext.reg("addressfield",NGMP.form.AddressField);


NGMP.form.ColorField = function(config){
    NGMP.form.ColorField.superclass.constructor.call(this, config);
	this.on('render', this.handleRender);
};

Ext.extend(NGMP.form.ColorField, Ext.form.TriggerField,  {
    showHexValue : false,
    triggerClass : 'x-form-color-trigger',
    defaultAutoCreate : {tag: "input", type: "text", size: "10",
						 autocomplete: "off", maxlength:"6"},
	lengthText: "Color hex values must be either 3 or 6 characters.",
	blankText: "Must have a hexidecimal value in the format ABCDEF.",
	defaultColor: 'FFFFFF',
	maskRe: /[a-f0-9]/i,
	regex: /[a-f0-9]/i,
	curColor: 'ffffff',
    validateValue : function(value){
		if(!this.showHexValue) {
			return true;
		}
		if(value.length<1) {
			this.el.setStyle({
				'background-color':'#' + this.defaultColor
			});
			if(!this.allowBlank) {
				this.markInvalid(String.format(this.blankText, value));
				return false;
			}
			return true;
		}
		if(value.length!=3 && value.length!=6 ) {
			this.markInvalid(String.format(this.lengthText, value));
			return false;
		}
		this.setColor(value);
        return true;
    },

    // private
    validateBlur : function(){
        return !this.menu || !this.menu.isVisible();
    },
	markInvalid : function( msg ) {
		NGMP.form.ColorField.superclass.markInvalid.call(this, msg);
		this.el.setStyle({
			'background-image': 'url(../lib/resources/images/default/grid/invalid_line.gif)'
		});
	},
    getValue : function(){
		return this.curValue || this.defaultValue || "FFFFFF";
    },
    setValue : function(hex){
		NGMP.form.ColorField.superclass.setValue.call(this, hex);
		this.setColor(hex);
    },
	
	setColor : function(hex) {
		this.curColor = hex;
		
		this.el.setStyle( {
			'background-color': '#' + hex,
			'background-image': 'none'
		});
		if(!this.showHexValue) {
			this.el.setStyle({
				'text-indent': '-100px'
			});
			if(Ext.isIE) {
				this.el.setStyle({
					'margin-left': '100px'
				});
			}
		}
	},
	
	handleRender: function() {
		this.setDefaultColor();
	},
	
	setDefaultColor : function() {
		this.setValue(this.defaultColor);
	},

    // private
    menuListeners : {
        select: function(m, d){
            this.setValue(d);
        },
        show : function(){ // retain focus styling
            this.onFocus();
        },
        hide : function(){
            this.focus();
            var ml = this.menuListeners;
            this.menu.un("select", ml.select,  this);
            this.menu.un("show", ml.show,  this);
            this.menu.un("hide", ml.hide,  this);
        }
    },
	
	//private
	handleSelect : function(palette, selColor) {
		this.setValue(selColor);
	},

    onTriggerClick : function(){
        if(this.disabled){
            return;
        }
        if(this.menu === null){
            this.menu = new Ext.menu.ColorMenu();
			this.menu.palette.on('select', this.handleSelect, this );
        }
        this.menu.on(Ext.apply({}, this.menuListeners, {
            scope:this
        }));
        this.menu.show(this.el, "tl-bl?");
    }
});
Ext.reg('colorfield', NGMP.form.ColorField);


NGMP.form.PasswordField = function(config) {

	// call parent constructor
	NGMP.form.PasswordField.superclass.constructor.call(this, config);

};

Ext.extend(NGMP.form.PasswordField, Ext.form.TextField, {
	    /**
	     * @cfg {String} inputType The type attribute for input fields -- e.g. text, password (defaults to "password").
	     */
		inputType: 'password',
		// private
		onRender: function(ct, position) {
			NGMP.form.PasswordField.superclass.onRender.call(this, ct, position);
			
			var elp = this.el.findParent('.x-form-element', 5, true);
			this.objMeter = ct.createChild({tag: "div", 'class': "strengthMeter"});

			this.objMeter.setWidth(elp.getWidth(true));
			this.scoreBar = this.objMeter.createChild({tag: "div", 'class': "scoreBar"});
			
			if(Ext.isIE && !Ext.isIE7) { // Fix style for IE6
				this.objMeter.setStyle('margin-left', '3px');
			}
		},
		// private
		initEvents: function() {
			NGMP.form.PasswordField.superclass.initEvents.call(this);
			
			this.el.on('keyup', this.updateMeter, this);
		},
		/**
		 * Sets the width of the meter, based on the score
		 * @param {Object} e 
		 * Private function
		 */
		updateMeter: function(e) {
			var score = 0;
		    var p = e.target.value;
			
			var maxWidth = this.objMeter.getWidth() - 2;
			
			var nScore = this.calcStrength(p);
			
    		// Set new width
    		var nRound = Math.round(nScore * 2);

			if (nRound > 100) {
				nRound = 100;
			}

			var scoreWidth = (maxWidth / 100) * nRound;
			this.scoreBar.setWidth(scoreWidth, true);
		},
		/**
		 * Calculates the strength of a password
		 * @param {Object} p The password that needs to be calculated
		 * @return {int} intScore The strength score of the password
		 */
		calcStrength: function(p) {
			var intScore = 0;

			// PASSWORD LENGTH
			intScore += p.length;
			
			if(p.length > 0 && p.length <= 4) {                    // length 4 or less
				intScore += p.length;
			}
			else if (p.length >= 5 && p.length <= 7) {	// length between 5 and 7
				intScore += 6;
			}
			else if (p.length >= 8 && p.length <= 15) {	// length between 8 and 15
				intScore += 12;
				//alert(intScore);
			}
			else if (p.length >= 16) {               // length 16 or more
				intScore += 18;
				//alert(intScore);
			}
			
			// LETTERS (Not exactly implemented as dictacted above because of my limited understanding of Regex)
			if (p.match(/[a-z]/)) {              // [verified] at least one lower case letter
				intScore += 1;
			}
			if (p.match(/[A-Z]/)) {              // [verified] at least one upper case letter
				intScore += 5;
			}
			// NUMBERS
			if (p.match(/\d/)) {             	// [verified] at least one number
				intScore += 5;
			}
			if (p.match(/.*\d.*\d.*\d/)) {            // [verified] at least three numbers
				intScore += 5;
			}
			
			// SPECIAL CHAR
			if (p.match(/[!,@,#,$,%,^,&,*,?,_,~]/)) {           // [verified] at least one special character
				intScore += 5;
			}
			// [verified] at least two special characters
			if (p.match(/.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~]/)) {
				intScore += 5;
			}
			
			// COMBOS
			if (p.match(/(?=.*[a-z])(?=.*[A-Z])/)) {        // [verified] both upper and lower case
				intScore += 2;
			}
			if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)) { // [verified] both letters and numbers
				intScore += 2;
			}
	 		// [verified] letters, numbers, and special characters
			if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&,*,?,_,~])/)) {
				intScore += 2;
			}

			return intScore;
		
		},
		// private
		onFocus: function() {
			NGMP.form.PasswordField.superclass.onFocus.call(this);
			
        	if(!Ext.isOpera) { // don't touch in Opera
            	this.objMeter.addClass('strengthMeter-focus');
       		}
		},
		// private
		onBlur: function() {
			NGMP.form.PasswordField.superclass.onBlur.call(this);
			
        	if(!Ext.isOpera) { // don't touch in Opera
            	this.objMeter.removeClass('strengthMeter-focus');
       		}
		}
});

NGMP.form.SideBarSub = function(A) {
	Ext.apply(this, A);
	this.tpl = new Ext.XTemplate('<tpl for=".">','<div id="ngmp_sidebar_item-{id}" class ="ngmp-sidebar-item {iconCls}">{text}</div>','</tpl>');
	NGMP.form.SideBarSub.superclass.constructor.call(this, {
		tpl: this.tpl,
		singleSelect:true,
		autoHeight:true,
		overClass:'ngmp-sidebar-item-over',
		itemSelector:'div.ngmp-sidebar-item',
		selectedClass:'ngmp-sidebar-item-selected'
	});
};

Ext.extend(NGMP.form.SideBarSub, Ext.DataView, {
	onRender:function(B,A) {
    	this.el = B.createChild({
			tag:'div',
			id:this.id,
			cls:'ngmp-sidebar-sub'
		});
		NGMP.form.SideBarSub.superclass.onRender.apply(this, arguments);
  	}
});

NGMP.form.SideBar = function(A) {
	Ext.apply(this, A);
    NGMP.form.SideBar.superclass.constructor.call(this,{
    	layout:'accordion',
		border:false
    });
};

Ext.extend(NGMP.form.SideBar, Ext.Panel, {
	afterRender:function() {
    	if (this.subs) {
       		this.subs.each(function(f) {
            	this.subItems.filter('sub', f.data.id);
                var d = new Array();
                this.subItems.data.each(function(F){
                	d.push([F.data.id, F.data.text, F.data.iconCls]);
                },this);
                var sub = new NGMP.form.SideBarSub({
                	id:'ngmp_sidebar_sub-'+f.data.id,
                    store:new Ext.data.SimpleStore({
                    	fields:['id','text','iconCls'],
                        data:d
                    })
                });
                sub.on('beforeselect', function() {
                	this.clearSelection();
                },this);
                sub.on('selectionchange', function(a,b) {
                	if (b[0]) {
                   		var id = b[0].id.split('-')[1];
                        if (this.handler)
                        	this.handler.call(this.scope || this, this, id);
                    }
                },this);
                this.add(new Ext.Panel({
                	title: f.data.text,
                    titleCollapse:true,
                    hideCollapseTool:true,
                    items:sub
                }));
                delete d;
             },this);
             delete this.subItems;
		}
        NGMP.form.SideBar.superclass.afterRender.call(this);
    },
	clearSelection:function() {
   		this.subs.each(function(f) {
        	var a = Ext.getCmp('ngmp_sidebar_sub-'+f.data.id);
            a.clearSelections(); 
        },this);
    }
});

NGMP.form.FormPanel = function(A) {
	NGMP.form.FormPanel.superclass.constructor.call(this,A);
};

Ext.extend(NGMP.form.FormPanel, Ext.form.FormPanel, {
	layout:'ngmp_form',
	defaults:{anchor:'90%'},
	cls:'ngmp-form-panel',
	autoScroll:true,
	border:false,
	labelWidth:200,
	initFields: function() {
		var f = this.form;
		var formPanel = this;
		var fn = function(c) {
			if (c.doLayout && c != formPanel) {
				Ext.applyIf(c, {
					labelAlign: c.ownerCt.labelAling,
					labelWidth: c.ownerCt.labelWidth,
				itemCls: c.ownerCt.itemCls
				});
				if (c.items) {
					c.items.each(fn);
				}
			} else if (c.isFormField) {
				c.cls += ' ngmp-form-field';
				f.add(c);
			}
		};
		this.items.each(fn);
	}
});


NGMP.form.Button = function(config){
    NGMP.form.Button.superclass.constructor.call(this, config);  
    
};

Ext.extend(NGMP.form.Button, Ext.form.Field,  {
     
     // private
    autoWidth : function(){
        if(this.el){
            this.el.setWidth("auto");
            if(Ext.isIE7 && Ext.isStrict){
                var ib = this.el.child('button');
                if(ib && ib.getWidth() > 20){
                    ib.clip();
                    ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
                }
            }
            if(this.minWidth){
                if(this.hidden){
                    this.el.beginMeasure();
                }
                if(this.el.getWidth() < this.minWidth){
                    this.el.setWidth(this.minWidth);
                }
                if(this.hidden){
                    this.el.endMeasure();
                }
            }
        }
    },
    // private
    onClick : function(e){
        if(e){
            e.preventDefault();
        }
        if(!this.disabled){
            if(this.enableToggle){
                this.toggle();
            }
            if(this.menu && !this.menu.isVisible()){
                this.menu.show(this.el, this.menuAlign);
            }
            this.fireEvent("click", this, e);
            if(this.handler){
                this.el.removeClass("x-btn-over");
                this.handler.call(this.scope || this, this, e);
            }
        }
    },
    // private
    onMouseOver : function(e){
        if(!this.disabled){
            this.el.addClass("x-btn-over");
            this.fireEvent('mouseover', this, e);
        }
    },
    // private
    onMouseOut : function(e){
        if(!e.within(this.el,  true)){
            this.el.removeClass("x-btn-over");
            this.fireEvent('mouseout', this, e);
        }
    },
    // private
    onMouseDown : function(){
        if(!this.disabled){
            this.el.addClass("x-btn-click");
            Ext.get(document).on('mouseup', this.onMouseUp, this);
        }
    },
    // private
    onMouseUp : function(){
        this.el.removeClass("x-btn-click");
        Ext.get(document).un('mouseup', this.onMouseUp, this);
    },
    // private
    onMenuShow : function(e){
        this.el.addClass("x-btn-menu-active");
    },
    // private
    onMenuHide : function(e){
        this.el.removeClass("x-btn-menu-active");
    }  ,
   onRender: function (ct)
{

        if(!NGMP.form.Button.buttonTemplate){
                    // hideous table template
                    NGMP.form.Button.buttonTemplate = new Ext.Template(
                        '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
                        '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em><button class="x-btn-text">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td><td style="padding-left:4px"></td>',
                        "</tr></tbody></table>");
                }
    this.template = NGMP.form.Button.buttonTemplate;            
    btn = this.template.append(ct, [this.text || '&#160;'], true);      
    this.el =btn;
    var btnEl = btn.child("button:first");
    
            if(this.cls){
                btn.addClass(this.cls);
            }
            
            if(this.icon){
                btnEl.setStyle('background-image', 'url(' +this.icon +')');
            }
            
            if(this.tooltip){
                if(typeof this.tooltip == 'object'){
                    Ext.QuickTips.tips(Ext.apply({
                          target: btnEl.id
                    }, this.tooltip));
                } else {
                    btnEl.dom[this.tooltipType] = this.tooltip;
                }
            }
     if (this.descriptionText)
           btn.insertHtml("beforeBegin",this.descriptionText);
    if(this.id){
            this.el.dom.id = this.el.id = this.id;
        }
    if(this.menu){
            this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
            this.menu.on("show", Ext.Button.onMenuShow, this);
            this.menu.on("hide", Ext.Button.onMenuHide, this);
        }     
        btn.addClass("x-btn");
        if(Ext.isIE && !Ext.isIE7){
            this.autoWidth.defer(1, this);
        }else{
            this.autoWidth();
        }        
        btn.on("click", this.onClick, this);
        btn.on("mouseover", this.onMouseOver, this);
        btn.on("mouseout", this.onMouseOut, this);
        btn.on("mousedown", this.onMouseDown, this);
        //btn.on("mouseup", this.onMouseUp, this);
        if(this.hidden){
            this.hide();
        }
        if(this.disabled){
            this.disable();
        }
        Ext.ButtonToggleMgr.register(this);
        if(this.pressed){
            this.el.addClass("x-btn-pressed");
        }
        if(this.repeat){
            var repeater = new Ext.util.ClickRepeater(btn,
                typeof this.repeat == "object" ? this.repeat : {}
            );
            repeater.on("click", this.onClick,  this);
        }               
} //end render
});


/**
 * @class NGMP.form.BrowseButton
 * @extends Ext.Button
 * Ext.Button that provides a customizable file browse button.
 * Clicking this button, pops up a file dialog box for a user to select the file to upload.
 * This is accomplished by having a transparent <input type="file"> box above the Ext.Button.
 * When a user thinks he or she is clicking the Ext.Button, they're actually clicking the hidden input "Browse..." box.
 * Note: this class can be instantiated explicitly or with xtypes anywhere a regular Ext.Button can be except in 2 scenarios:
 * - Panel.addButton method both as an instantiated object or as an xtype config object.
 * - Panel.buttons config object as an xtype config object.
 * These scenarios fail because Ext explicitly creates an Ext.Button in these cases.
 * Browser compatibility:
 * Internet Explorer 6:
 * - no issues
 * Internet Explorer 7:
 * - no issues
 * Firefox 2 - Windows:
 * - pointer cursor doesn't display when hovering over the button.
 * Safari 3 - Windows:
 * - no issues.
 * @author loeppky - based on the work done by MaximGB in Ext.ux.UploadDialog (http://extjs.com/forum/showthread.php?t=21558)
 * The follow the curosr float div idea also came from MaximGB.
 * @see http://extjs.com/forum/showthread.php?t=29032
 * @constructor
 * Create a new BrowseButton.
 * @param {Object} config Configuration options
 */
NGMP.form.BrowseButton = Ext.extend(Ext.Button, {
	/*
	 * Config options:
	 */
	/**
	 * @cfg {String} inputFileName
	 * Name to use for the hidden input file DOM element.  Deaults to "file".
	 */
	inputFileName: 'file',
	/**
	 * @cfg {Boolean} debug
	 * Toggle for turning on debug mode.
	 * Debug mode doesn't make clipEl transparent so that one can see how effectively it covers the Ext.Button.
	 * In addition, clipEl is given a green background and floatEl a red background to see how well they are positioned.
	 */
	debug: false,
	
	
	/*
	 * Private constants:
	 */
	/**
	 * @property FLOAT_EL_WIDTH
	 * @type Number
	 * The width (in pixels) of floatEl.
	 * It should be less than the width of the IE "Browse" button's width (65 pixels), since IE doesn't let you resize it.
	 * We define this width so we can quickly center floatEl at the mouse cursor without having to make any function calls.
	 * @private
	 */
	FLOAT_EL_WIDTH: 60,
	
	/**
	 * @property FLOAT_EL_HEIGHT
	 * @type Number
	 * The heigh (in pixels) of floatEl.
	 * It should be less than the height of the "Browse" button's height.
	 * We define this height so we can quickly center floatEl at the mouse cursor without having to make any function calls.
	 * @private
	 */
	FLOAT_EL_HEIGHT: 18,
	
	
	/*
	 * Private properties:
	 */
	/**
	 * @property buttonCt
	 * @type Ext.Element
	 * Element that contains the actual Button DOM element.
	 * We store a reference to it, so we can easily grab its size for sizing the clipEl.
	 * @private
	 */
	buttonCt: null,
	/**
	 * @property clipEl
	 * @type Ext.Element
	 * Element that contains the floatEl.
	 * This element is positioned to fill the area of Ext.Button and has overflow turned off.
	 * This keeps floadEl tight to the Ext.Button, and prevents it from masking surrounding elements.
	 * @private
	 */
	clipEl: null,
	/**
	 * @property floatEl
	 * @type Ext.Element
	 * Element that contains the inputFileEl.
	 * This element is size to be less than or equal to the size of the input file "Browse" button.
	 * It is then positioned wherever the user moves the cursor, so that their click always clicks the input file "Browse" button.
	 * Overflow is turned off to preven inputFileEl from masking surrounding elements.
	 * @private
	 */
	floatEl: null,
	/**
	 * @property inputFileEl
	 * @type Ext.Element
	 * Element for the hiden file input.
	 * @private
	 */
	inputFileEl: null,
	/**
	 * @property originalHandler
	 * @type Function
	 * The handler originally defined for the Ext.Button during construction using the "handler" config option.
	 * We need to null out the "handler" property so that it is only called when a file is selected.
	 * @private
	 */
	originalHandler: null,
	/**
	 * @property originalScope
	 * @type Object
	 * The scope originally defined for the Ext.Button during construction using the "scope" config option.
	 * While the "scope" property doesn't need to be nulled, to be consistent with originalHandler, we do.
	 * @private
	 */
	originalScope: null,
	
	
	/*
	 * Protected Ext.Button overrides
	 */
	/**
	 * @see Ext.Button.initComponent
	 */
	initComponent: function(){
		NGMP.form.BrowseButton.superclass.initComponent.call(this);
		// Store references to the original handler and scope before nulling them.
		// This is done so that this class can control when the handler is called.
		// There are some cases where the hidden file input browse button doesn't completely cover the Ext.Button.
		// The handler shouldn't be called in these cases.  It should only be called if a new file is selected on the file system.  
		this.originalHandler = this.handler;
		this.originalScope = this.scope;
		this.handler = null;
		this.scope = null;
	},
	
	/**
	 * @see Ext.Button.onRender
	 */
	onRender: function(ct, position){
		NGMP.form.BrowseButton.superclass.onRender.call(this, ct, position); // render the Ext.Button
		this.buttonCt = this.el.child('.x-btn-center em');
		this.buttonCt.position('relative'); // this is important!
		var styleCfg = {
			position: 'absolute',
			overflow: 'hidden',
			top: '0px', // default
			left: '0px' // default
		};
		// browser specifics for better overlay tightness
		if (Ext.isIE) {
			Ext.apply(styleCfg, {
				left: '-3px',
				top: '-3px'
			});
		} else if (Ext.isGecko) {
			Ext.apply(styleCfg, {
				left: '-3px',
				top: '-3px'
			});
		} else if (Ext.isSafari) {
			Ext.apply(styleCfg, {
				left: '-4px',
				top: '-2px'
			});
		}
		this.clipEl = this.buttonCt.createChild({
			tag: 'div',
			style: styleCfg
		});
		this.setClipSize();
		this.clipEl.on({
			'mousemove': this.onButtonMouseMove,
			'mouseover': this.onButtonMouseMove,
			scope: this
		});
		
		this.floatEl = this.clipEl.createChild({
			tag: 'div',
			style: {
				position: 'absolute',
				width: this.FLOAT_EL_WIDTH + 'px',
				height: this.FLOAT_EL_HEIGHT + 'px',
				overflow: 'hidden'
			}
		});
		
		
		if (this.debug) {
			this.clipEl.applyStyles({
				'background-color': 'green'
			});
			this.floatEl.applyStyles({
				'background-color': 'red'
			});
		} else {
			// We don't set the clipEl to be transparent, because IE 6/7 occassionaly looses mouse events for transparent elements.
			// We have listeners on the clipEl that can't be lost as they're needed for realligning the input file element.
			this.floatEl.setOpacity(0.0);
		}
		
		// Cover cases where someone tabs to the button:
		// Listen to focus of the button so we can translate the focus to the input file el.
		var buttonEl = this.el.child(this.buttonSelector);
		buttonEl.on('focus', this.onButtonFocus, this);
		// In IE, it's possible to tab to the text portion of the input file el.  
		// We want to listen to keyevents so that if a space is pressed, we "click" the input file el.
		if (Ext.isIE) {
			this.el.on('keydown', this.onButtonKeyDown, this);
		}
		
		this.createInputFile();
	},
	
	
	/*
	 * Private helper methods:
	 */
	/**
	 * Sets the size of clipEl so that is covering as much of the button as possible.
	 * @private
	 */
	setClipSize: function(){
		if (this.clipEl) {
			var width = this.buttonCt.getWidth();
			var height = this.buttonCt.getHeight();
			// The button container can have a width and height of zero when it's rendered in a hidden panel.
			// This is most noticable when using a card layout, as the items are all rendered but hidden,
			// (unless deferredRender is set to true). 
			// In this case, the clip size can't be determined, so we attempt to set it later.
			// This check repeats until the button container has a size. 
			if (width === 0 || height === 0) {
				this.setClipSize.defer(100, this);
			} else {
				if (Ext.isIE) {
					width = width + 5;
					height = height + 5;
				} else if (Ext.isGecko) {
					width = width + 6;
					height = height + 6;
				} else if (Ext.isSafari) {
					width = width + 6;
					height = height + 6;
				}
				this.clipEl.setSize(width, height);
			}
		}
	},
	
	/**
	 * Creates the input file element and adds it to inputFileCt.
	 * The created input file elementis sized, positioned, and styled appropriately.
	 * Event handlers for the element are set up, and a tooltip is applied if defined in the original config.
	 * @private
	 */
	createInputFile: function(){
		// When an input file gets detached and set as the child of a different DOM element,
		// straggling <em> elements get left behind.  
		// I don't know why this happens but we delete any <em> elements we can find under the floatEl to prevent a memory leak.
		this.floatEl.select('em').each(function(el){
			el.remove();
		});
		this.inputFileEl = this.floatEl.createChild({
			tag: 'input',
			type: 'file',
			size: 1, // must be > 0. It's value doesn't really matter due to our masking div (inputFileCt).  
			name: this.inputFileName || Ext.id(this.el),
			tabindex: this.tabIndex,
			// Use the same pointer as an Ext.Button would use.  This doesn't work in Firefox.
			// This positioning right-aligns the input file to ensure that the "Browse" button is visible.
			style: {
				position: 'absolute',
				cursor: 'pointer',
				right: '0px',
				top: '0px'
			}
		});
		this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;
		
		// setup events
		this.inputFileEl.on({
			'click': this.onInputFileClick,
			'change': this.onInputFileChange,
			'focus': this.onInputFileFocus,
			'select': this.onInputFileFocus,
			'blur': this.onInputFileBlur,
			scope: this
		});
		
		// add a tooltip
		if (this.tooltip) {
			if (typeof this.tooltip == 'object') {
				Ext.QuickTips.register(Ext.apply({
					target: this.inputFileEl
				}, this.tooltip));
			} else {
				this.inputFileEl.dom[this.tooltipType] = this.tooltip;
			}
		}
	},
	
	/**
	 * Redirecting focus to the input file element so the user can press space and select files.
	 * @param {Event} e focus event.
	 * @private
	 */
	onButtonFocus: function(e){
		if (this.inputFileEl) {
			this.inputFileEl.focus();
			e.stopEvent();
		}
	},
	
	/**
	 * Handler for the IE case where once can tab to the text box of an input file el.
	 * If the key is a space, we simply "click" the inputFileEl.
	 * @param {Event} e key event.
	 * @private
	 */
	onButtonKeyDown: function(e){
		if (this.inputFileEl && e.getKey() == Ext.EventObject.SPACE) {
			this.inputFileEl.dom.click();
			e.stopEvent();
		}
	},
	
	/**
	 * Handler when the cursor moves over the clipEl.
	 * The floatEl gets centered to the cursor location.
	 * @param {Event} e mouse event.
	 * @private
	 */
	onButtonMouseMove: function(e){
		var xy = e.getXY();
		xy[0] -= this.FLOAT_EL_WIDTH / 2;
		xy[1] -= this.FLOAT_EL_HEIGHT / 2;
		this.floatEl.setXY(xy);
	},
	
	/**
	 * Add the visual enhancement to the button when the input file recieves focus. 
	 * This is the tip for the user that now he/she can press space to select the file.
	 * @private
	 */
	onInputFileFocus: function(e){
		if (!this.isDisabled) {
			this.el.addClass("x-btn-over");
		}
	},
	
	/**
	 * Removes the visual enhancement from the button.
	 * @private
	 */
	onInputFileBlur: function(e){
		this.el.removeClass("x-btn-over");
	},
	
	/**
	 * Handler when inputFileEl's "Browse..." button is clicked.
	 * @param {Event} e click event.
	 * @private
	 */
	onInputFileClick: function(e){
		e.stopPropagation();
	},
	
	/**
	 * Handler when inputFileEl changes value (i.e. a new file is selected).
	 * @private
	 */
	onInputFileChange: function(){
		if (this.originalHandler) {
			this.originalHandler.call(this.originalScope, this);
		}
	},
	
	
	/*
	 * Public methods:
	 */
	/**
	 * Detaches the input file associated with this BrowseButton so that it can be used for other purposed (e.g. uplaoding).
	 * The returned input file has all listeners and tooltips applied to it by this class removed.
	 * @param {Boolean} whether to create a new input file element for this BrowseButton after detaching.
	 * True will prevent creation.  Defaults to false.
	 * @return {Ext.Element} the detached input file element.
	 */
	detachInputFile: function(noCreate){
		var result = this.inputFileEl;
		
		if (typeof this.tooltip == 'object') {
			Ext.QuickTips.unregister(this.inputFileEl);
		} else {
			this.inputFileEl.dom[this.tooltipType] = null;
		}
		this.inputFileEl.removeAllListeners();
		this.inputFileEl = null;
		
		if (!noCreate) {
			this.createInputFile();
		}
		return result;
	},
	
	/**
	 * @return {Ext.Element} the input file element attached to this BrowseButton.
	 */
	getInputFile: function(){
		return this.inputFileEl;
	},
	
	/**
	 * @see Ext.Button.disable
	 */
	disable: function(){
		NGMP.form.BrowseButton.superclass.disable.call(this);
		this.inputFileEl.dom.disabled = true;
	},
	
	/**
	 * @see Ext.Button.enable
	 */
	enable: function(){
		NGMP.form.BrowseButton.superclass.enable.call(this);
		this.inputFileEl.dom.disabled = false;
	}
});

Ext.reg('browsebutton', NGMP.form.BrowseButton);
