// Add the additional 'advanced' VTypes, for date form field for from to dates
Ext.apply(Ext.form.VTypes, {
	daterange: function(val, field) {
		var date = field.parseDate(val);

		// We need to force the picker to update values to recaluate the disabled dates display
		var dispUpd = function(picker) {
			var ad = picker.activeDate;
			picker.activeDate = null;
			picker.update(ad);
		};

		if (field.startDateField) {
			var sd = Ext.getCmp(field.startDateField);
			sd.maxValue = date;
			if (sd.menu && sd.menu.picker) {
				sd.menu.picker.maxDate = date;
				dispUpd(sd.menu.picker);
			}
		} else if (field.endDateField) {
			var ed = Ext.getCmp(field.endDateField);
			ed.minValue = date;
			if (ed.menu && ed.menu.picker) {
				ed.menu.picker.minDate = date;
				dispUpd(ed.menu.picker);
			}
		}
		/* Always return true since we're only using this vtype
		 * to set the min/max allowed values (these are tested
		 * for after the vtype test)
		 */
		return true;
	}
});Ext.namespace("Ext.ux.form");

/**
  * Ext.ux.form.Spinner Class
	*
	* @author  Steven Chim
	* @version Spinner.js 2008-01-10 v0.21
  *
  * @class Ext.ux.form.Spinner
  * @extends Ext.form.TriggerField
  */

Ext.ux.form.Spinner = function(config){
	Ext.ux.form.Spinner.superclass.constructor.call(this, config);
	this.addEvents({
		'spinup' : true,
		'spindown' : true
	});
}

Ext.extend(Ext.ux.form.Spinner, Ext.form.TriggerField, {
	triggerClass : 'x-form-spinner-trigger',
	splitterClass : 'x-form-spinner-splitter',

	alternateKey : Ext.EventObject.shiftKey,
	strategy : undefined,

	//private
	onRender : function(ct, position){
		Ext.ux.form.Spinner.superclass.onRender.call(this, ct, position);

		this.splitter = this.wrap.createChild({tag:'div', cls:this.splitterClass, style:'width:13px; height:2px;'});
		this.splitter.show().setRight( (Ext.isIE) ? 1 : 2 );
		this.splitter.show().setTop(10);

		this.proxy = this.trigger.createProxy('', this.splitter, true);
		this.proxy.addClass("x-form-spinner-proxy");
		this.proxy.setStyle('left','0px');  
		this.proxy.setSize(14, 1);
		this.proxy.hide();
		this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {dragElId: this.proxy.id});

		this.initSpinner();
	},

	//private
	initSpinner : function(){
		this.keyNav = new Ext.KeyNav(this.el, {
			"up" : function(e){
				this.onSpinUp();
			},

			"down" : function(e){
				this.onSpinDown();
			},

			"pageUp" : function(e){
				this.onSpinUpAlternate();
			},

			"pageDown" : function(e){
				this.onSpinDownAlternate();
			},

			scope : this
		});

		this.trigger.un("click", this.onTriggerClick);
		this.repeater = new Ext.util.ClickRepeater(this.trigger);
		this.repeater.on("click", this.onTriggerClick, this, {preventDefault:true});
		this.trigger.on("mouseover", this.onMouseOver, this, {preventDefault:true});
		this.trigger.on("mouseout",  this.onMouseOut,  this, {preventDefault:true});
		this.trigger.on("mousemove", this.onMouseMove, this, {preventDefault:true});
		this.trigger.on("mousedown", this.onMouseDown, this, {preventDefault:true});
		this.trigger.on("mouseup",   this.onMouseUp,   this, {preventDefault:true});
		this.wrap.on("mousewheel",   this.handleMouseWheel, this);

		this.dd.setXConstraint(0, 0, 10)
		this.dd.setYConstraint(1500, 1500, 10);
		this.dd.endDrag = this.endDrag.createDelegate(this);
		this.dd.startDrag = this.startDrag.createDelegate(this);
		this.dd.onDrag = this.onDrag.createDelegate(this);

		if(this.strategy == undefined){
			this.strategy = new Ext.ux.form.Spinner.NumberStrategy();
		}
	},

	//private
	onMouseOver : function(){
		if(this.disabled){
			return;
		}
		var middle = this.getMiddle();
		this.__tmphcls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
		this.trigger.addClass(this.__tmphcls);
	},

	//private
	onMouseOut : function(){
		this.trigger.removeClass(this.__tmphcls);
	},

	//private
	onMouseMove : function(){
		if(this.disabled){
			return;
		}
		var middle = this.getMiddle();
		if( ((Ext.EventObject.getPageY() > middle) && this.__tmphcls == "x-form-spinner-overup") ||
			((Ext.EventObject.getPageY() < middle) && this.__tmphcls == "x-form-spinner-overdown")){
		}
	},

	//private
	onMouseDown : function(){
		if(this.disabled){
			return;
		}
		var middle = this.getMiddle();
		this.__tmpccls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
		this.trigger.addClass(this.__tmpccls);
	},

	//private
	onMouseUp : function(){
		this.trigger.removeClass(this.__tmpccls);
	},

	//private
	onTriggerClick : function(){
		if(this.disabled){
			return;
		}
		var middle = this.getMiddle();
		var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';
		this['onSpin'+ud]();
	},

	//private
	getMiddle : function(){
		var t = this.trigger.getTop();
		var h = this.trigger.getHeight();
		var middle = t + (h/2);
		return middle;
	},

	handleMouseWheel : function(e){
		var delta = e.getWheelDelta();
		if(delta > 0){
			this.onSpinUp();
			e.stopEvent();
		} else if(delta < 0){
			this.onSpinDown();
			e.stopEvent();
		}
	},

	//private
	startDrag : function(){
		this.proxy.show();
		this._previousY = Ext.fly(this.dd.getDragEl()).getTop();
	},

	//private
	endDrag : function(){
		this.proxy.hide();
	},

	//private
	onDrag : function(){
		if(this.disabled){
			return;
		}
		var y = Ext.fly(this.dd.getDragEl()).getTop();
		var ud = '';

		if(this._previousY > y){ud = 'Up';}         //up
		if(this._previousY < y){ud = 'Down';}       //down

		if(ud != ''){
			this['onSpin'+ud]();
		}

		this._previousY = y;
	},

	//private
	onSpinUp : function(){
		if(Ext.EventObject.shiftKey == true){
			this.onSpinUpAlternate();
			return;
		}else{
			this.strategy.onSpinUp(this);
		}
		this.fireEvent("spinup", this);
	},

	//private
	onSpinDown : function(){
		if(Ext.EventObject.shiftKey == true){
			this.onSpinDownAlternate();
			return;
		}else{
			this.strategy.onSpinDown(this);
		}
		this.fireEvent("spindown", this);
	},

	//private
	onSpinUpAlternate : function(){
		this.strategy.onSpinUpAlternate(this);
		this.fireEvent("spinup", this);
	},

	//private
	onSpinDownAlternate : function(){
		this.strategy.onSpinDownAlternate(this);
		this.fireEvent("spindown", this);
	}

});

Ext.reg('uxspinner', Ext.ux.form.Spinner);
/**
  * Ext.ux.SpinnerPlugin
  *
  * @author  Steven Chim
  * @version SpinnerPlugin.js 2008-01-10 v0.1
  *
  * @class Ext.ux.SpinnerPlugin
  * @description: Spinner plugin for textfield component (textfield, numberfield, datefield, timefield)
  */

Ext.ux.SpinnerPlugin = function(config) {
    Ext.apply(this, config);
};

Ext.ux.SpinnerPlugin.prototype = {

    init : function(field) {
        this.field = field;
        
        if(field.rendered){
            this.initSpinner();
        } else {
            field.on('render', this.initSpinner, this);
        }

		if(this.strategy == undefined){
		    var xtype = field.getXType();
		    var config = Ext.apply({});
		    
            switch(xtype){
                case "datefield":
                    if(field.format) config.format = field.format;

                    this.strategy = new Ext.ux.form.Spinner.DateStrategy(config);
                    break;
                case "timefield":
                    if(field.format) config.format = field.format;

                    this.strategy = new Ext.ux.form.Spinner.TimeStrategy(config);
                    break;
                case "numberfield":
                    if(field.maxValue) config.maxValue = field.maxValue;
                    if(field.minValue) config.minValue = field.minValue;
                    
                    this.strategy = new Ext.ux.form.Spinner.NumberStrategy(config);
                    break;
                default:
                    this.strategy = new Ext.ux.form.Spinner.NumberStrategy();
            }
		}
    },
    
	//private
	initSpinner : function(){
		this.keyNav = new Ext.KeyNav(this.field.getEl(), {
			"up" : function(e){
				this.onSpinUp(this.field);
			},

			"down" : function(e){
				this.onSpinDown(this.field);
			},

			"pageUp" : function(e){
				this.onSpinUpAlternate(this.field);
			},

			"pageDown" : function(e){
				this.onSpinDownAlternate(this.field);
			},

			scope : this
		});

		if(this.strategy == undefined){
			this.strategy = new Ext.ux.form.Spinner.NumberStrategy();
		}
	},
	
	//private
	onSpinUp : function(){
		if(Ext.EventObject.shiftKey == true){
			this.onSpinUpAlternate();
			return;
		}else{
			this.strategy.onSpinUp(this.field);
		}
	},

	//private
	onSpinDown : function(){
		if(Ext.EventObject.shiftKey == true){
			this.onSpinDownAlternate();
			return;
		}else{
			this.strategy.onSpinDown(this.field);
		}
	},

	//private
	onSpinUpAlternate : function(){
		this.strategy.onSpinUpAlternate(this.field);
	},

	//private
	onSpinDownAlternate : function(){
		this.strategy.onSpinDownAlternate(this.field);
	}

};/***
 * Abstract Strategy
 */
Ext.ux.form.Spinner.Strategy = function(config){
	Ext.apply(this, config);
};

Ext.extend(Ext.ux.form.Spinner.Strategy, Ext.util.Observable, {
	defaultValue : 0,
	minValue : undefined,
	maxValue : undefined,
	incrementValue : 1,
	alternateIncrementValue : 5,
	
	onSpinUp : function(field){
		this.spin(field, false, false);
	},

	onSpinDown : function(field){
		this.spin(field, true, false);
	},

	onSpinUpAlternate : function(field){
		this.spin(field, false, true);
	},

	onSpinDownAlternate : function(field){
		this.spin(field, true, true);
	},

	spin : function(field, down, alternate){
		//extend
	},

	fixBoundries : function(value){
		return value;
		//overwrite
	}
	
});

/***
 * Concrete Strategy: Numbers
 */
Ext.ux.form.Spinner.NumberStrategy = function(config){
	Ext.ux.form.Spinner.NumberStrategy.superclass.constructor.call(this, config);
};

Ext.extend(Ext.ux.form.Spinner.NumberStrategy, Ext.ux.form.Spinner.Strategy, {

    allowDecimals : true,
    decimalPrecision : 2,
    
	spin : function(field, down, alternate){
		Ext.ux.form.Spinner.NumberStrategy.superclass.spin.call(this, field, down, alternate);

		var v = parseFloat(field.getValue());
		var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;

		(down == true) ? v -= incr : v += incr ;
		v = (isNaN(v)) ? this.defaultValue : v;
		v = this.fixBoundries(v);
		field.setRawValue(v);
	},

	fixBoundries : function(value){
		var v = value;

		if(this.minValue != undefined && v < this.minValue){
			v = this.minValue;
		}
		if(this.maxValue != undefined && v > this.maxValue){
			v = this.maxValue;
		}

		return this.fixPrecision(v);
	},
	
    // private
    fixPrecision : function(value){
        var nan = isNaN(value);
        if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
            return nan ? '' : value;
        }
        return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
    }
});


/***
 * Concrete Strategy: Date
 */
Ext.ux.form.Spinner.DateStrategy = function(config){
	Ext.ux.form.Spinner.DateStrategy.superclass.constructor.call(this, config);
};

Ext.extend(Ext.ux.form.Spinner.DateStrategy, Ext.ux.form.Spinner.Strategy, {
	defaultValue : new Date(),
	format : "Y-m-d",
	incrementValue : 1,
	incrementConstant : Date.DAY,
	alternateIncrementValue : 1,
	alternateIncrementConstant : Date.MONTH,

	spin : function(field, down, alternate){
		Ext.ux.form.Spinner.DateStrategy.superclass.spin.call(this);

		var v = field.getRawValue();
		
		v = Date.parseDate(v, this.format);
		var dir = (down == true) ? -1 : 1 ;
		var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
		var dtconst = (alternate == true) ? this.alternateIncrementConstant : this.incrementConstant;

		if(typeof this.defaultValue == 'string'){
			this.defaultValue = Date.parseDate(this.defaultValue, this.format);
		}

		v = (v) ? v.add(dtconst, dir*incr) : this.defaultValue;

		v = this.fixBoundries(v);
		field.setRawValue(v.format(this.format));
	},
	
	//private
	fixBoundries : function(date){
		var dt = date;
		var min = (typeof this.minValue == 'string') ? Date.parseDate(this.minValue, this.format) : this.minValue ;
		var max = (typeof this.maxValue == 'string') ? Date.parseDate(this.maxValue, this.format) : this.maxValue ;

		if(this.minValue != undefined && dt < min){
			dt = min;
		}
		if(this.maxValue != undefined && dt > max){
			dt = max;
		}

		return dt;
	}

});


/***
 * Concrete Strategy: Time
 */
Ext.ux.form.Spinner.TimeStrategy = function(config){
	Ext.ux.form.Spinner.TimeStrategy.superclass.constructor.call(this, config);
};

Ext.extend(Ext.ux.form.Spinner.TimeStrategy, Ext.ux.form.Spinner.DateStrategy, {
	format : "H:i",
	incrementValue : 1,
	incrementConstant : Date.MINUTE,
	alternateIncrementValue : 1,
	alternateIncrementConstant : Date.HOUR
});
/*
 * Ext JS Library 2.1
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 *
 * http://extjs.com/license
 */

Ext.grid.RowExpander = function(config){
	Ext.apply(this, config);

	this.addEvents({
		beforeexpand : true,
		expand: true,
		beforecollapse: true,
		collapse: true
	});

	Ext.grid.RowExpander.superclass.constructor.call(this);

	if(this.tpl){
		if(typeof this.tpl == 'string'){
			this.tpl = new Ext.Template(this.tpl);
		}
		this.tpl.compile();
	}

	this.state = {};
	this.bodyContent = {};
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
	header: "",
	width: 20,
	sortable: false,
	fixed:true,
	menuDisabled:true,
	dataIndex: '',
	id: 'expander',
	lazyRender : true,
	enableCaching: true,

	getRowClass : function(record, rowIndex, p, ds){
		p.cols = p.cols-1;
		var content = this.bodyContent[record.id];
		if(!content && !this.lazyRender){
			content = this.getBodyContent(record, rowIndex);
		}
		if(content){
			p.body = content;
		}
		return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
	},

	init : function(grid){
		this.grid = grid;

		var view = grid.getView();
		view.getRowClass = this.getRowClass.createDelegate(this);

		view.enableRowBody = true;

		grid.on('render', function(){
			view.mainBody.on('mousedown', this.onMouseDown, this);
		}, this);
	},

	getBodyContent : function(record, index){
		if(!this.enableCaching){
			return this.tpl.apply(record.data);
		}
		var content = this.bodyContent[record.id];
		if(!content){
			content = this.tpl.apply(record.data);
			this.bodyContent[record.id] = content;
		}
		return content;
	},

	onMouseDown : function(e, t){
		if(t.className == 'x-grid3-row-expander'){
			e.stopEvent();
			var row = e.getTarget('.x-grid3-row');
			this.toggleRow(row);
		}
	},

	renderer : function(v, p, record){
		p.cellAttr = 'rowspan="2"';
		return '<div class="x-grid3-row-expander">&#160;</div>';
	},

	beforeExpand : function(record, body, rowIndex){
		if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
			if(this.tpl && this.lazyRender){
				body.innerHTML = this.getBodyContent(record, rowIndex);
			}
			return true;
		}else{
			return false;
		}
	},

	toggleRow : function(row){
		if(typeof row == 'number'){
			row = this.grid.view.getRow(row);
		}
		this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
	},

	expandRow : function(row){
		if(typeof row == 'number'){
			row = this.grid.view.getRow(row);
		}
		var record = this.grid.store.getAt(row.rowIndex);
		var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
		if(this.beforeExpand(record, body, row.rowIndex)){
			this.state[record.id] = true;
			Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
			this.fireEvent('expand', this, record, body, row.rowIndex);
		}
	},

	collapseRow : function(row){
		if(typeof row == 'number'){
			row = this.grid.view.getRow(row);
		}
		var record = this.grid.store.getAt(row.rowIndex);
		var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
		if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){
			this.state[record.id] = false;
			Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
			this.fireEvent('collapse', this, record, body, row.rowIndex);
		}
	}
});
Ext.namespace('Ext.ux.dd');

Ext.ux.dd.GridReorderDropTarget = function(grid, config) {
    this.target = new Ext.dd.DropTarget(grid.getEl(), {
        ddGroup: grid.ddGroup || 'GridDD'
        ,grid: grid
        ,gridDropTarget: this
        ,notifyDrop: function(dd, e, data){
            // determine the row
            var t = Ext.lib.Event.getTarget(e);
            var rindex = this.grid.getView().findRowIndex(t);
            if (rindex === false) return false;
            if (rindex == data.rowIndex) return false;

            // fire the before move/copy event
            if (this.gridDropTarget.fireEvent(this.copy?'beforerowcopy':'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections) === false) return false;

            // update the store
            var ds = this.grid.getStore();
            if (!this.copy) {
                for(i = 0; i < data.selections.length; i++) {
                    ds.remove(ds.getById(data.selections[i].id));
                }
            }
            ds.insert(rindex,data.selections);

            // re-select the row(s)
            var sm = this.grid.getSelectionModel();
            if (sm) sm.selectRecords(data.selections);

            // fire the after move/copy event
            this.gridDropTarget.fireEvent(this.copy?'afterrowcopy':'afterrowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections);

            return true;
        }
        ,notifyOver: function(dd, e, data) {
            var t = Ext.lib.Event.getTarget(e);
            var rindex = this.grid.getView().findRowIndex(t);
            if (rindex == data.rowIndex) rindex = false;

            return (rindex === false)? this.dropNotAllowed : this.dropAllowed;
        }
    });
    if (config) {
        Ext.apply(this.target, config);
        if (config.listeners) Ext.apply(this,{listeners: config.listeners});
    }

    this.addEvents({
        "beforerowmove": true
        ,"afterrowmove": true
        ,"beforerowcopy": true
        ,"afterrowcopy": true
    });

    Ext.ux.dd.GridReorderDropTarget.superclass.constructor.call(this);
};

Ext.extend(Ext.ux.dd.GridReorderDropTarget, Ext.util.Observable, {
    getTarget: function() {
        return this.target;
    }
    ,getGrid: function() {
        return this.target.grid;
    }
    ,getCopy: function() {
        return this.target.copy?true:false;
    }
    ,setCopy: function(b) {
        this.target.copy = b?true:false;
    }
});(function(){var EV=Ext.lib.Event;Ext.ux.ManagedIFrame=function(){var args=Array.prototype.slice.call(arguments,0),el=Ext.get(args[0]),config=args[0];if(el&&el.dom&&el.dom.tagName=="IFRAME"){config=args[1]||{};}else{config=args[0]||args[1]||{};el=config.autoCreate?Ext.get(Ext.DomHelper.append(config.autoCreate.parent||document.body,Ext.apply({tag:"iframe",src:(Ext.isIE&&Ext.isSecure)?Ext.SSL_SECURE_URL:""},config.autoCreate))):null;}if(!el||el.dom.tagName!="IFRAME"){return el;}el.dom.name||(el.dom.name=el.dom.id);el.dom.mifId=el.dom.id;this.addEvents({"focus":true,"blur":true,"unload":true,"domready":true,"documentloaded":true,"exception":true,"message":true});if(config.listeners){this.listeners=config.listeners;Ext.ux.ManagedIFrame.superclass.constructor.call(this);}Ext.apply(el,this);el.addClass("x-managed-iframe");if(config.style){el.applyStyles(config.style);}el._maskEl=el.parent(".x-managed-iframe-mask")||el.parent().addClass("x-managed-iframe-mask");Ext.apply(el,{disableMessaging:config.disableMessaging===true,loadMask:Ext.apply({msg:"Loading..",msgCls:"x-mask-loading",maskEl:el._maskEl,hideOnReady:true,disabled:!config.loadMask},config.loadMask),_eventName:Ext.isIE?"onreadystatechange":"onload",_windowContext:null,eventsFollowFrameLinks:typeof config.eventsFollowFrameLinks=="undefined"?true:config.eventsFollowFrameLinks});el.dom[el._eventName]=el.loadHandler.createDelegate(el);var um=el.updateManager=new Ext.UpdateManager(el,true);um.showLoadIndicator=config.showLoadIndicator||false;if(config.src){el.setSrc(config.src);}else{var content=config.html||config.content||false;if(content){el.update.defer(10,el,[content]);}}return Ext.ux.ManagedIFrame.Manager.register(el);};var MIM=Ext.ux.ManagedIFrame.Manager=function(){var frames={};var readyHandler=function(e,target){try{var id=target?target.mifId:null,frame;if((frame=this.getFrameById(id||target.id))&&frame._frameAction){frame.loadHandler({type:"domready"});}}catch(rhEx){}};var implementation={shimCls:"x-frame-shim",register:function(frame){frame.manager=this;frames[frame.id]=frames[frame.dom.name]={ref:frame,elCache:{}};return frame;},deRegister:function(frame){frame._unHook();delete frames[frame.id];delete frames[frame.dom.name];},hideShims:function(){if(!this.shimApplied){return ;}Ext.select("."+this.shimCls,true).removeClass(this.shimCls+"-on");this.shimApplied=false;},showShims:function(){if(!this.shimApplied){this.shimApplied=true;Ext.select("."+this.shimCls,true).addClass(this.shimCls+"-on");}},getFrameById:function(id){return typeof id=="string"?(frames[id]?frames[id].ref||null:null):null;},getFrameByName:function(name){return this.getFrameById(name);},getFrameHash:function(frame){return frame.id?frames[frame.id]:null;},eventProxy:function(e){if(!e){return ;}e=Ext.EventObject.setEvent(e);var be=e.browserEvent||e;if(e.type=="unload"){this._unHook();}if(!be["eventPhase"]||(be["eventPhase"]==(be["AT_TARGET"]||2))){return this.fireEvent(e.type,e);}},_flyweights:{},destroy:function(){if(this._domreadySignature){Ext.EventManager.un.apply(Ext.EventManager,this._domreadySignature);}},removeNode:Ext.isIE?function(frame,n){frame=MIM.getFrameHash(frame);if(frame&&n&&n.tagName!="BODY"){d=frame.scratchDiv||(frame.scratchDiv=frame.getDocument().createElement("div"));d.appendChild(n);d.innerHTML="";}}:function(frame,n){if(n&&n.parentNode&&n.tagName!="BODY"){n.parentNode.removeChild(n);}}};if(document.addEventListener){Ext.EventManager.on.apply(Ext.EventManager,implementation._domreadySignature=[window,"DOMFrameContentLoaded",readyHandler,implementation]);}Ext.EventManager.on(window,"beforeunload",implementation.destroy,implementation);return implementation;}();MIM.showDragMask=MIM.showShims;MIM.hideDragMask=MIM.hideShims;MIM.El=function(frame,el,forceNew){var frameObj;frame=(frameObj=MIM.getFrameHash(frame))?frameObj.ref:null;if(!frame){return null;}var elCache=frameObj.elCache||(frameObj.elCache={});var dom=frame.getDom(el);if(!dom){return null;}var id=dom.id;if(forceNew!==true&&id&&elCache[id]){return elCache[id];}this.dom=dom;this.id=id||Ext.id(dom);};MIM.El.get=function(frame,el){var ex,elm,id,doc;if(!frame||!el){return null;}var frameObj;frame=(frameObj=MIM.getFrameHash(frame))?frameObj.ref:null;if(!frame){return null;}var elCache=frameObj.elCache||(frameObj.elCache={});if(!(doc=frame.getDocument())){return null;}if(typeof el=="string"){if(!(elm=frame.getDom(el))){return null;}if(ex=elCache[el]){ex.dom=elm;}else{ex=elCache[el]=new MIM.El(frame,elm);}return ex;}else{if(el.tagName){if(!(id=el.id)){id=Ext.id(el);}if(ex=elCache[id]){ex.dom=el;}else{ex=elCache[id]=new MIM.El(frame,el);}return ex;}else{if(el instanceof MIM.El){if(el!=frameObj.docEl){el.dom=frame.getDom(el.id)||el.dom;elCache[el.id]=el;}return el;}else{if(el.isComposite){return el;}else{if(Ext.isArray(el)){return frame.select(el);}else{if(el==doc){if(!frameObj.docEl){var f=function(){};f.prototype=MIM.El.prototype;frameObj.docEl=new f();frameObj.docEl.dom=doc;}return frameObj.docEl;}}}}}}return null;};Ext.apply(MIM.El.prototype,Ext.Element.prototype);Ext.extend(Ext.ux.ManagedIFrame,Ext.util.Observable,{src:null,resetUrl:Ext.isIE&&Ext.isSecure?Ext.SSL_SECURE_URL:"about:blank",setSrc:function(url,discardUrl,callback){var src=url||this.src||this.resetUrl;this._windowContext=null;this._unHook();this._frameAction=this.frameInit=this._domReady=false;if(Ext.isOpera){this.reset();}this._callBack=callback||false;this.showMask();(function(){var s=typeof src=="function"?src()||"":src;try{this._frameAction=true;this.dom.src=s;this.frameInit=true;this.checkDOM();}catch(ex){this.fireEvent("exception",this,ex);}}).defer(100,this);if(discardUrl!==true){this.src=src;}return this;},reset:function(src,callback){this.dom.src=src||this.resetUrl;if(typeof callback=="function"){callback.defer(100);}return this;},scriptRE:/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/gi,update:function(content,loadScripts,callback){loadScripts=loadScripts||this.getUpdateManager().loadScripts||false;content=Ext.DomHelper.markup(content||"");content=loadScripts===true?content:content.replace(this.scriptRE,"");var doc;if(doc=this.getDocument()){this._frameAction=!!content.length;this._windowContext=this.src=null;this._callBack=callback||false;this._unHook();this.showMask();doc.open();doc.write(content);doc.close();this.frameInit=true;if(this._frameAction){this.checkDOM();}else{this.hideMask(true);if(this._callBack){this._callBack();}}}else{this.hideMask(true);if(this._callBack){this._callBack();}}return this;},disableMessaging:true,_XFrameMessaging:function(){var tagStack={"$":[]};var isEmpty=function(v,allowBlank){return v===null||v===undefined||(!allowBlank?v==="":false);};window.sendMessage=function(message,tag,origin){var MIF;if(MIF=arguments.callee.manager){if(message._fromHost){var fn,result;var compTag=message.tag||tag||null;var mstack=!isEmpty(compTag)?tagStack[compTag.toLowerCase()]||[]:tagStack["$"];for(var i=0,l=mstack.length;i<l;i++){if(fn=mstack[i]){result=fn.apply(fn.__scope,arguments)===false?false:result;if(fn.__single){mstack[i]=null;}if(result===false){break;}}}return result;}else{message={type:isEmpty(tag)?"message":"message:"+tag.toLowerCase().replace(/^\s+|\s+$/g,""),data:message,domain:origin||document.domain,uri:document.documentURI,source:window,tag:isEmpty(tag)?null:tag.toLowerCase()};try{return MIF.disableMessaging!==true?MIF.fireEvent.call(MIF,message.type,MIF,message):null;}catch(ex){}return null;}}};window.onhostmessage=function(fn,scope,single,tag){if(typeof fn=="function"){if(!isEmpty(fn.__index)){throw"onhostmessage: duplicate handler definition"+(tag?" for tag:"+tag:"");}var k=isEmpty(tag)?"$":tag.toLowerCase();tagStack[k]||(tagStack[k]=[]);Ext.apply(fn,{__tag:k,__single:single||false,__scope:scope||window,__index:tagStack[k].length});tagStack[k].push(fn);}else{throw"onhostmessage: function required";}};window.unhostmessage=function(fn){if(typeof fn=="function"&&typeof fn.__index!="undefined"){var k=fn.__tag||"$";tagStack[k][fn.__index]=null;}};},get:function(el){return MIM.El.get(this,el);},fly:function(el,named){named=named||"_global";el=this.getDom(el);if(!el){return null;}if(!MIM._flyweights[named]){MIM._flyweights[named]=new Ext.Element.Flyweight();}MIM._flyweights[named].dom=el;return MIM._flyweights[named];},getDom:function(el){var d;if(!el||!(d=this.getDocument())){return null;}return el.dom?el.dom:(typeof el=="string"?d.getElementById(el):el);},select:function(selector,unique){var d;return(d=this.getDocument())?Ext.Element.select(selector,unique,d):null;},query:function(selector){var d;return(d=this.getDocument())?Ext.DomQuery.select(selector,d):null;},getDoc:function(){return this.get(this.getDocument());},removeNode:function(node){MIM.removeNode(this,this.getDom(node));},_unHook:function(){var elcache,h=MIM.getFrameHash(this)||{};if(this._hooked&&h&&(elcache=h.elCache)){for(var id in elcache){var el=elcache[id];delete elcache[id];if(el.removeAllListeners){el.removeAllListeners();}}if(h.docEl){h.docEl.removeAllListeners();h.docEl=null;delete h.docEl;}}this._hooked=this._domReady=this._domFired=false;},_renderHook:function(){this._windowContext=this.CSS=null;this._hooked=false;try{if(this.writeScript('(function(){(window.hostMIF = parent.Ext.get("'+this.dom.id+'"))._windowContext='+(Ext.isIE?"window":"{eval:function(s){return eval(s);}}")+";})();")){this._frameProxy||(this._frameProxy=MIM.eventProxy.createDelegate(this));var w=this.getWindow();EV.doAdd(w,"focus",this._frameProxy);EV.doAdd(w,"blur",this._frameProxy);EV.doAdd(w,"unload",this._frameProxy);if(this.disableMessaging!==true){this.loadFunction({name:"XMessage",fn:this._XFrameMessaging},false,true);var sm;if(sm=w.sendMessage){sm.manager=this;}}this.CSS=new CSSInterface(this.getDocument());}}catch(ex){}return this.domWritable();},sendMessage:function(message,tag,origin){var win;if(this.disableMessaging!==true&&(win=this.getWindow())){tag||(tag=message.tag||"");tag=tag.toLowerCase();message=Ext.applyIf(message.data?message:{data:message},{type:Ext.isEmpty(tag)?"message":"message:"+tag,domain:origin||document.domain,uri:document.documentURI,source:window,tag:tag||null,_fromHost:this});return win.sendMessage?win.sendMessage.call(null,message,tag,origin):null;}return null;},_windowContext:null,getDocument:function(){var win=this.getWindow(),doc=null;try{doc=(Ext.isIE&&win?win.document:null)||this.dom.contentDocument||window.frames[this.id].document||null;}catch(gdEx){return false;}return doc;},getBody:function(){var d;return(d=this.getDocument())?d.body:null;},getDocumentURI:function(){var URI,d;try{URI=this.src&&(d=this.getDocument())?d.location.href:null;}catch(ex){}return URI||this.src;},getWindow:function(){var dom=this.dom,win=null;try{win=dom.contentWindow||window.frames[dom.name]||null;}catch(gwEx){}return win;},print:function(){try{var win=this.getWindow();if(Ext.isIE){win.focus();}win.print();}catch(ex){throw"print exception: "+(ex.description||ex.message||ex);}},destroy:function(){this.removeAllListeners();if(this.dom){this.dom[this._eventName]=null;Ext.ux.ManagedIFrame.Manager.deRegister(this);this._windowContext=null;if(Ext.isIE&&this.dom.src){this.dom.src="javascript:false";}this._maskEl=null;this.remove();}if(this.loadMask){Ext.apply(this.loadMask,{masker:null,maskEl:null});}},domWritable:function(){return !!this._windowContext;},execScript:function(block,useDOM){try{if(this.domWritable()){if(useDOM){this.writeScript(block);}else{return this._windowContext.eval(block);}}else{throw"execScript:non-secure context";}}catch(ex){this.fireEvent("exception",this,ex);return false;}return true;},writeScript:function(block,attributes){attributes=Ext.apply({},attributes||{},{type:"text/javascript",text:block});try{var head,script,doc=this.getDocument();if(doc&&typeof doc.getElementsByTagName!="undefined"){if(!(head=doc.getElementsByTagName("head")[0])){head=doc.createElement("head");doc.getElementsByTagName("html")[0].appendChild(head);}if(head&&(script=doc.createElement("script"))){for(var attrib in attributes){if(attributes.hasOwnProperty(attrib)&&attrib in script){script[attrib]=attributes[attrib];}}return !!head.appendChild(script);}}}catch(ex){this.fireEvent("exception",this,ex);}return false;},loadFunction:function(fn,useDOM,invokeIt){var name=fn.name||fn;var fn=fn.fn||window[fn];this.execScript(name+"="+fn,useDOM);if(invokeIt){this.execScript(name+"()");}},showMask:function(msg,msgCls,forced){var lmask;if((lmask=this.loadMask)&&(!lmask.disabled||forced)){if(lmask._vis){return ;}lmask.masker||(lmask.masker=Ext.get(lmask.maskEl||this.dom.parentNode||this.wrap({tag:"div",style:{position:"relative"}})));lmask._vis=true;lmask.masker.mask.defer(lmask.delay||5,lmask.masker,[msg||lmask.msg,msgCls||lmask.msgCls]);}},hideMask:function(forced){var tlm;if((tlm=this.loadMask)&&!tlm.disabled&&tlm.masker){if(!forced&&(tlm.hideOnReady!==true&&this._domReady)){return ;}tlm._vis=false;tlm.masker.unmask.defer(tlm.delay||5,tlm.masker);}},loadHandler:function(e,target){if(!this.frameInit||(!this._frameAction&&!this.eventsFollowFrameLinks)){return ;}target||(target={});var rstatus=(e&&typeof e.type!=="undefined"?e.type:this.dom.readyState);switch(rstatus){case"loading":case"interactive":break;case"domready":if(this._domReady){return ;}this._domReady=true;if(this._hooked=this._renderHook()){this._domFired=true;this.fireEvent("domready",this);}case"domfail":this._domReady=true;this.hideMask();break;case"load":case"complete":if(!this._domReady){this.loadHandler({type:"domready",id:this.id});}this.hideMask(true);if(this._frameAction||this.eventsFollowFrameLinks){this.fireEvent.defer(50,this,["documentloaded",this]);}this._frameAction=this._frameInit=false;if(this.eventsFollowFrameLinks){this._domFired=this._domReady=false;}if(this._callBack){this._callBack(this);}break;default:}this.frameState=rstatus;},checkDOM:function(win){if(Ext.isOpera||Ext.isGecko||!this._frameAction){return ;}var n=0,win=win||this.getWindow(),manager=this,domReady=false,max=300;var poll=function(){try{var doc=manager.getDocument(),body=null;if(doc===false){throw"Document Access Denied";}if(!manager._domReady){domReady=!!(doc&&doc.getElementsByTagName);domReady=domReady&&(body=doc.getElementsByTagName("body")[0])&&!!body.innerHTML.length;}}catch(ex){n=max;}if(!manager._frameAction||manager._domReady){return ;}if((++n<max)&&!domReady){setTimeout(arguments.callee,10);return ;}manager.loadHandler({type:domReady?"domready":"domfail"});};setTimeout(poll,40);}});var styleCamelRe=/(-[a-z])/gi;var styleCamelFn=function(m,a){return a.charAt(1).toUpperCase();};var CSSInterface=function(hostDocument){var doc;if(hostDocument){doc=hostDocument;return{rules:null,createStyleSheet:function(cssText,id){var ss;if(!doc){return ;}var head=doc.getElementsByTagName("head")[0];var rules=doc.createElement("style");rules.setAttribute("type","text/css");if(id){rules.setAttribute("id",id);}if(Ext.isIE){head.appendChild(rules);ss=rules.styleSheet;ss.cssText=cssText;}else{try{rules.appendChild(doc.createTextNode(cssText));}catch(e){rules.cssText=cssText;}head.appendChild(rules);ss=rules.styleSheet?rules.styleSheet:(rules.sheet||doc.styleSheets[doc.styleSheets.length-1]);}this.cacheStyleSheet(ss);return ss;},removeStyleSheet:function(id){if(!doc){return ;}var existing=doc.getElementById(id);if(existing){existing.parentNode.removeChild(existing);}},swapStyleSheet:function(id,url){this.removeStyleSheet(id);if(!doc){return ;}var ss=doc.createElement("link");ss.setAttribute("rel","stylesheet");ss.setAttribute("type","text/css");ss.setAttribute("id",id);ss.setAttribute("href",url);doc.getElementsByTagName("head")[0].appendChild(ss);},refreshCache:function(){return this.getRules(true);},cacheStyleSheet:function(ss){if(this.rules){this.rules={};}try{var ssRules=ss.cssRules||ss.rules;for(var j=ssRules.length-1;j>=0;--j){this.rules[ssRules[j].selectorText]=ssRules[j];}}catch(e){}},getRules:function(refreshCache){if(this.rules==null||refreshCache){this.rules={};if(doc){var ds=doc.styleSheets;for(var i=0,len=ds.length;i<len;i++){try{this.cacheStyleSheet(ds[i]);}catch(e){}}}}return this.rules;},getRule:function(selector,refreshCache){var rs=this.getRules(refreshCache);if(!Ext.isArray(selector)){return rs[selector];}for(var i=0;i<selector.length;i++){if(rs[selector[i]]){return rs[selector[i]];}}return null;},updateRule:function(selector,property,value){if(!Ext.isArray(selector)){var rule=this.getRule(selector);if(rule){rule.style[property.replace(styleCamelRe,styleCamelFn)]=value;return true;}}else{for(var i=0;i<selector.length;i++){if(this.updateRule(selector[i],property,value)){return true;}}}return false;}};}};Ext.ux.ManagedIframePanel=Ext.extend(Ext.Panel,{defaultSrc:null,bodyStyle:{height:"100%",width:"100%",position:"relative"},frameStyle:{overflow:"auto"},frameConfig:null,hideMode:!Ext.isIE?"nosize":"display",shimCls:Ext.ux.ManagedIFrame.Manager.shimCls,shimUrl:null,loadMask:false,stateful:false,animCollapse:Ext.isIE&&Ext.enableFx,autoScroll:false,closable:true,ctype:"Ext.ux.ManagedIframePanel",showLoadIndicator:false,unsupportedText:"Inline frames are NOT enabled/supported by your browser.",initComponent:function(){this.bodyCfg||(this.bodyCfg={cls:"x-managed-iframe-mask",children:[Ext.apply({tag:"iframe",frameborder:0,cls:"x-managed-iframe",style:this.frameStyle||null,html:this.unsupportedText||null},this.frameConfig?this.frameConfig.autoCreate||{}:false,Ext.isIE&&Ext.isSecure?{src:Ext.SSL_SECURE_URL}:false),{tag:"img",src:this.shimUrl||Ext.BLANK_IMAGE_URL,cls:this.shimCls,galleryimg:"no"}]});this.autoScroll=false;this.items=null;if(this.stateful!==false){this.stateEvents||(this.stateEvents=["documentloaded"]);}Ext.ux.ManagedIframePanel.superclass.initComponent.call(this);this.monitorResize||(this.monitorResize=this.fitToParent);this.addEvents({documentloaded:true,domready:true,message:true,exception:true});this.addListener=this.on;},doLayout:function(){if(this.fitToParent&&!this.ownerCt){var pos=this.getPosition(),size=(Ext.get(this.fitToParent)||this.getEl().parent()).getViewSize();this.setSize(size.width-pos[0],size.height-pos[1]);}Ext.ux.ManagedIframePanel.superclass.doLayout.apply(this,arguments);},beforeDestroy:function(){if(this.rendered){if(this.tools){for(var k in this.tools){Ext.destroy(this.tools[k]);}}if(this.header&&this.headerAsText){var s;if(s=this.header.child("span")){s.remove();}this.header.update("");}Ext.each(["iframe","shim","header","topToolbar","bottomToolbar","footer","loadMask","body","bwrap"],function(elName){if(this[elName]){if(typeof this[elName].destroy=="function"){this[elName].destroy();}else{Ext.destroy(this[elName]);}this[elName]=null;delete this[elName];}},this);}Ext.ux.ManagedIframePanel.superclass.beforeDestroy.call(this);},onDestroy:function(){Ext.Panel.superclass.onDestroy.call(this);},onRender:function(ct,position){Ext.ux.ManagedIframePanel.superclass.onRender.call(this,ct,position);if(this.iframe=this.body.child("iframe.x-managed-iframe")){this.iframe.ownerCt=this;var El=Ext.Element;var mode=El[this.hideMode.toUpperCase()]||"x-hide-nosize";Ext.each([this[this.collapseEl],this.floating?null:this.getActionEl(),this.iframe],function(el){if(el){el.setVisibilityMode(mode);}},this);if(this.loadMask){this.loadMask=Ext.apply({disabled:false,maskEl:this.body,hideOnReady:true},this.loadMask);}if(this.iframe=new Ext.ux.ManagedIFrame(this.iframe,{loadMask:this.loadMask,showLoadIndicator:this.showLoadIndicator,disableMessaging:this.disableMessaging,style:this.frameStyle})){this.loadMask=this.iframe.loadMask;this.relayEvents(this.iframe,["blur","focus","unload","documentloaded","domready","exception","message"].concat(this._msgTagHandlers||[]));delete this._msgTagHandlers;}this.getUpdater().showLoadIndicator=this.showLoadIndicator||false;var ownerCt=this.ownerCt;while(ownerCt){ownerCt.on("afterlayout",function(container,layout){var MIM=Ext.ux.ManagedIFrame.Manager,st=false;Ext.each(["north","south","east","west"],function(region){var reg;if((reg=layout[region])&&reg.splitEl){st=true;if(!reg.split._splitTrapped){reg.split.on("beforeresize",MIM.showShims,MIM);reg.split._splitTrapped=true;}}},this);if(st&&!this._splitTrapped){this.on("resize",MIM.hideShims,MIM);this._splitTrapped=true;}},this,{single:true});ownerCt=ownerCt.ownerCt;}}this.shim=Ext.get(this.body.child("."+this.shimCls));},toggleShim:function(){if(this.shim&&this.shimCls){this.shim.toggleClass(this.shimCls+"-on");}},afterRender:function(container){var html=this.html;delete this.html;Ext.ux.ManagedIframePanel.superclass.afterRender.call(this);if(this.iframe){if(this.defaultSrc){this.setSrc();}else{if(html){this.iframe.update(typeof html=="object"?Ext.DomHelper.markup(html):html);}}}},sendMessage:function(){if(this.iframe){this.iframe.sendMessage.apply(this.iframe,arguments);}},on:function(name){var tagRE=/^message\:/i,n=null;if(typeof name=="object"){for(var na in name){if(!this.filterOptRe.test(na)&&tagRE.test(na)){n||(n=[]);n.push(na.toLowerCase());}}}else{if(tagRE.test(name)){n=[name.toLowerCase()];}}if(this.getFrame()&&n){this.relayEvents(this.iframe,n);}else{this._msgTagHandlers||(this._msgTagHandlers=[]);if(n){this._msgTagHandlers=this._msgTagHandlers.concat(n);}}Ext.ux.ManagedIframePanel.superclass.on.apply(this,arguments);},setSrc:function(url,discardUrl,callback){url=url||this.defaultSrc||false;if(!url){return this;}if(url.url){callback=url.callback||false;discardUrl=url.discardUrl||false;url=url.url||false;}var src=url||(Ext.isIE&&Ext.isSecure?Ext.SSL_SECURE_URL:"");if(this.rendered&&this.iframe){this.iframe.setSrc(src,discardUrl,callback);}return this;},getState:function(){var URI=this.iframe?this.iframe.getDocumentURI()||null:null;return Ext.apply(Ext.ux.ManagedIframePanel.superclass.getState.call(this)||{},URI?{defaultSrc:typeof URI=="function"?URI():URI}:null);},getUpdater:function(){return this.rendered?(this.iframe||this.body).getUpdater():null;},getFrame:function(){return this.rendered?this.iframe:null;},getFrameWindow:function(){return this.rendered&&this.iframe?this.iframe.getWindow():null;},getFrameDocument:function(){return this.rendered&&this.iframe?this.iframe.getDocument():null;},getFrameDoc:function(){return this.rendered&&this.iframe?this.iframe.getDoc():null;},getFrameBody:function(){return this.rendered&&this.iframe?this.iframe.getBody():null;},load:function(loadCfg){var um;if(um=this.getUpdater()){if(loadCfg&&loadCfg.renderer){um.setRenderer(loadCfg.renderer);delete loadCfg.renderer;}um.update.apply(um,arguments);}return this;},doAutoLoad:function(){this.load(typeof this.autoLoad=="object"?this.autoLoad:{url:this.autoLoad});}});Ext.reg("iframepanel",Ext.ux.ManagedIframePanel);Ext.ux.ManagedIframePortlet=Ext.extend(Ext.ux.ManagedIframePanel,{anchor:"100%",frame:true,collapseEl:"bwrap",collapsible:true,draggable:true,cls:"x-portlet"});Ext.reg("iframeportlet",Ext.ux.ManagedIframePortlet);Ext.apply(Ext.Element.prototype,{setVisible:function(visible,animate){if(!animate||!Ext.lib.Anim){if(this.visibilityMode==Ext.Element.DISPLAY){this.setDisplayed(visible);}else{if(this.visibilityMode==Ext.Element.VISIBILITY){this.fixDisplay();this.dom.style.visibility=visible?"visible":"hidden";}else{this[visible?"removeClass":"addClass"](String(this.visibilityMode));}}}else{var dom=this.dom;var visMode=this.visibilityMode;if(visible){this.setOpacity(0.01);this.setVisible(true);}this.anim({opacity:{to:(visible?1:0)}},this.preanim(arguments,1),null,0.35,"easeIn",function(){if(!visible){if(visMode==Ext.Element.DISPLAY){dom.style.display="none";}else{if(visMode==Ext.Element.VISIBILITY){dom.style.visibility="hidden";}else{Ext.get(dom).addClass(String(visMode));}}Ext.get(dom).setOpacity(1);}});}return this;},isVisible:function(deep){var vis=!(this.getStyle("visibility")=="hidden"||this.getStyle("display")=="none"||this.hasClass(this.visibilityMode));if(deep!==true||!vis){return vis;}var p=this.dom.parentNode;while(p&&p.tagName.toLowerCase()!="body"){if(!Ext.fly(p,"_isVisible").isVisible()){return false;}p=p.parentNode;}return true;}});Ext.onReady(function(){var CSS=Ext.util.CSS,rules=[];CSS.getRule(".x-managed-iframe")||(rules.push(".x-managed-iframe {height:100%;width:100%;overflow:auto;}"));CSS.getRule(".x-managed-iframe-mask")||(rules.push(".x-managed-iframe-mask{width:100%;height:100%;position:relative;}"));if(!CSS.getRule(".x-frame-shim")){rules.push(".x-frame-shim {z-index:8500;position:absolute;top:0px;left:0px;background:transparent!important;overflow:hidden;display:none;}");rules.push(".x-frame-shim-on{width:100%;height:100%;display:block;zoom:1;}");rules.push(".ext-ie6 .x-frame-shim{margin-left:5px;margin-top:3px;}");}CSS.getRule(".x-hide-nosize")||(rules.push(".x-hide-nosize,.x-hide-nosize *{height:0px!important;width:0px!important;border:none;}"));if(!!rules.length){CSS.createStyleSheet(rules.join(" "));}});})();/**
 * Ext.ux.form.FckEditor, Extjs wrapper for FckEditor
 *
 * @author    Srdjan Kostadinovic (srdjan.kostadinovic@troxo.com)
 * @copyright (c) 2008, by Troxo
 * @date      28. May 2008
 *
 */

Ext.namespace('Ext.ux.form');

//Ext.ux.form.FckEditor_loading = false;
Ext.ux.form.FckEditor = function(config) {
	if ('undefined' != typeof(config.fckContainer)) {
		this.fckContainer = config.fckContainer;
	}
	if ('undefined' != typeof(config.fckHeightAnchor)) {
		this.fckHeightAnchor = config.fckHeightAnchor;
	}
	if ('undefined' != typeof(config.fckWaitText)) {
		this.fckWaitText = config.fckWaitText;
	}
	if ('undefined' != typeof(config.fckWaitTitle)) {
		this.fckWaitTitle = config.fckWaitTitle;
	}
	if ('undefined' != typeof(config.fckConfig)) {
		this.fckConfig = config.fckConfig;
	}
	if('undefined' != typeof(config.fckEditorPath)) {
		this.fckEditorPath = config.fckEditorPath;
	}

	this.fckInstanceName = config.fckInstanceName;
	this.fckWidth = config.fckWidth;
	this.fckHeight = config.fckHeight;
	this.fckToolbarSet = config.fckToolbarSet;
	this.fckValue = config.fckValue;
	if ('undefined' == typeof(config.listeners)) {
		config.listeners = new Object();
	}

	this.fckGetHeight = function() {
		var heightToReturn;
		if (!Ext.getCmp(this.fckContainer) || !Ext.getCmp(this.fckContainer).rendered) {
			heightToReturn = this.fckHeight;
		} else {
			heightToReturn = Ext.getCmp(this.fckContainer).getInnerHeight() + this.fckHeightAnchor;
		}

		// Fck size should not be less than 150px - it is not editable then
		heightToReturn = heightToReturn ? heightToReturn : 0;
		heightToReturn = Math.max(parseInt(heightToReturn), 150);

		return heightToReturn;
	};

	this.fckGetWidth = function() {
		var maxContainerWidth = 99999999;
		if (this.ownerCt) {
			maxContainerWidth = this.ownerCt.getInnerWidth();
		}
		return Math.min(this.fckWidth, maxContainerWidth);
	};

	config.height = this.fckGetHeight();

	config.listeners.render = function(component) {
		if ('undefined' == typeof(this.fckInstanceName)) {
			this.fckInstanceName = component.id;
		}
		if ('undefined' == typeof(this.fckHeight)) {
			this.fckHeight = component.fckGetHeight();
		}
		if ('undefined' == typeof(this.fckValue)) {
			this.fckWidth = component.width;
		}
		var oFCKeditor = new FCKeditor(this.fckInstanceName);
		oFCKeditor.BasePath = this.fckEditorPath;
		oFCKeditor.Height = this.fckHeight;
		oFCKeditor.ToolbarSet = this.fckToolbarSet;
		if ('undefined' != this.fckConfig) {
			for(configAttribute in this.fckConfig) {
				oFCKeditor.Config[configAttribute] = this.fckConfig[configAttribute];
			}
		}
		oFCKeditor.ReplaceTextarea();
		Ext.MessageBox.wait(this.fckWaitText, this.fckWaitTitle);
	};

	config.preventScrollbars = true;
	Ext.ux.form.FckEditor.superclass.constructor.call(this, config);
};

Ext.extend(Ext.ux.form.FckEditor, Ext.form.TextArea, {
	oFckEditor: new Object(),
	fckIsLoaded: false,
	fckContainer: 'Layout.regionCenter',
	fckSizeInitialized: false,
	fckHeightAnchor: -250,
	fckHeight: 770,
	fckWidth: 770,
	fckEditorPath: '/js/fckeditor-2.6.3/',
	fckToolbarSet: 'Default',
	fckWaitText: 'Please wait ... ',
	fckWaitTitle: 'Loading editor',

	/**
	 * Ensuring image ratio preservation in IE
	 * @author Espen Roberts (espena@nsn.no)
	 */
	fckAttachImageResizeHandlers: function(editorInstance) {
		var eEditorArea = editorInstance.EditorWindow;
		for (var i = 0; i < eEditorArea.document.images.length; i++) {
			var currentImage = eEditorArea.document.images[i];

			if (('FCK__Flash' == currentImage.className) || ('FCK__Anchor' == currentImage.className) || (0 <= currentImage.className.search(/webshopCampaign/)) || (0 <= currentImage.className.search(/imageGallery/))) {
				continue;
			} // Skip FCKeditors native images for representing elements

			// Added to overcome 'porting' problems..
			if (currentImage.width && ('' == currentImage.style.width)) {
				currentImage.style.width  = currentImage.width;
				currentImage.style.height = currentImage.height;
			}

			// Attach event handler for onresize
			currentImage.onresize = function() {
				var image = this;
				// Copy to get original sizes
				var origimage = new Image;
				origimage.src = image.src;

				// Set default/minimum width
				if ((10 > image.style.width.replace('px','') ) && ('' != image.style.width)) {
					image.style.width = '10px';
				}

				var newheight = Math.round(image.style.width.replace('px','') * origimage.height / origimage.width) ;
				if ((newheight > 0) && ((newheight != image.style.height) && (newheight != image.height))) {
					image.height = newheight;
					image.style.height = newheight + 'px';
					image.width = image.style.width.replace('px','');
				}

				return true;
			};
		}
		return true;
	},
	// Manual trigger of saving hidden field with content
	updateTextArea: function(forceUpdate) {
		try {
			forceUpdate = ('undefined' == typeof(forceUpdate)) ? false : forceUpdate;
			if (forceUpdate || (this.oFckEditor && this.oFckEditor.IsDirty())) {
				this.setValue(this.oFckEditor.GetData());
				this.setRawValue(this.oFckEditor.GetData());
			}
		} catch (e) {

		}
	},
	refreshEditorSize: function() {
		try {
			if (this.oFckEditor && this.oFckEditor.EditorWindow && this.oFckEditor.EditorWindow.parent && this.oFckEditor.EditorWindow.parent.frameElement && this.oFckEditor.EditorWindow.parent.frameElement.height) {
				try {var newHeight = this.fckGetHeight();} catch (e) {}
				try {this.oFckEditor.EditorWindow.parent.frameElement.height = newHeight;} catch (e) {}
				try {this.setHeight(newHeight);} catch (e) {}

				try {var newWidth = this.fckGetWidth();} catch (e) {}
				try {this.oFckEditor.EditorWindow.parent.frameElement.width = newWidth;} catch (e) {}
				try {this.setWidth(newWidth);} catch (e) {}

				if (this.oFckEditor.EditorWindow.parent.frameElement.style) {
					try {this.oFckEditor.EditorWindow.parent.frameElement.style.height = newHeight + 'px';} catch (e) {}
					try {this.oFckEditor.EditorWindow.parent.frameElement.style.width = newWidth + 'px';} catch (e) {}
				}

				this.fixIE8Height();
			}
		} catch (e) {}
	},
	//START - workaround for IE bug (editor not rendering when its container is hidden)
	//(http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide/Troubleshooting#HiddenDiv)
	fixIEDisplay: function() {
		try {
			if (this.oFckEditor) {
				if (this.oFckEditor.ToolbarSet && this.oFckEditor.ToolbarSet.IsEnabled && this.fckToolbarSet) {
					this.oFckEditor.ToolbarSet.Load(this.fckToolbarSet);
				}

				this.oFckEditor.SwitchEditMode();
				this.oFckEditor.SwitchEditMode();
			}
		} catch (e) {

		}
	},
	fixIE8Height: function() {
		if (Ext.isIE8 && this.oFckEditor.EditingArea.IFrame.style) { // Fix for IE8
			try {
				this.oFckEditor.EditingArea.IFrame.style.height = (this.fckGetHeight() - 60) + 'px';
			} catch (e) {}
		}
	}
});
Ext.reg('fckeditor', Ext.ux.form.FckEditor);

var FCKeditor_OnComplete = function(editorInstance) {
	var attachedField = Ext.getCmp(editorInstance.Name);

	editorInstance.EditorWindow.parent.FCKEmbedAndObjectProcessor.AddCustomHandler( function( el, fakeImg ){
		var nodeName = new String();
		nodeName = el.nodeName;
		nodeName = nodeName.toLowerCase()
		if (!((('object' == nodeName) || ('embed' == nodeName)) && ((el.type == 'application/x-shockwave-flash') || /\.swf($|#|\?)/i.test(el.src)))) {
			return;
		}
		fakeImg.className = 'FCK__Flash' ;
		fakeImg.setAttribute( '_fckflash', 'true', 0 );
	});

	attachedField.fckIsLoaded = true;
	attachedField.oFckEditor = editorInstance;

	//delayed execution queue speeds up performance since keyup is most freequent action
	var delayedExecutionQueue = new Ext.util.DelayedTask();

	var resetImageGallerySize = function() {
		if (editorInstance.EditorDocument && editorInstance.EditorDocument.body) {
			var selectedElement = editorInstance.Selection.GetSelectedElement();
			var eEditorArea = editorInstance.EditorWindow;
			if (('undefined' == typeof(editorInstance.EditorDocument)) || (null == editorInstance.EditorDocument)) {
				return true;
			}
			var pageSize = Ext.fly(editorInstance.EditorDocument.body).getSize(true);
			pageSize.width -= 20; // - 20 is for IE scrollbars

			for (var i = 0; i < eEditorArea.document.images.length; i++) {
				var currentImage = eEditorArea.document.images[i];

				// Dont allow resizing for image gallery and campaign placeholder image
				if ((0 <= currentImage.className.search(/webshopCampaign/)) || (0 <= currentImage.className.search(/imageGallery/))) {
					Ext.fly(currentImage).setSize(pageSize.width, parseInt(pageSize.width * 0.75));
				}
			}

			if (selectedElement) {
				editorInstance.Selection.SelectNode(selectedElement);
			}
		}
		return true;
	};

	Ext.fly(editorInstance.EditorDocument.body).on('load', resetImageGallerySize);
	Ext.fly(editorInstance.EditorDocument.body).on('mouseup', resetImageGallerySize);


	//this fixes scope problem cause updateTextArea() uses this inside function body
	var updateTextArea = function() {
		delayedExecutionQueue.delay(500, attachedField.updateTextArea, attachedField);
	};

	var setListenersToIframe = function() {
		attachedField.fixIE8Height();
		if (editorInstance.EditingArea.Document && editorInstance.EditingArea.Document.documentElement) {
			Ext.EventManager.addListener(editorInstance.EditingArea.Document.documentElement, 'keyup', updateTextArea);
			Ext.EventManager.addListener(editorInstance.EditingArea.Document.documentElement, 'click', updateTextArea);
			Ext.EventManager.addListener(editorInstance.EditingArea.Document.documentElement, 'focus', updateTextArea);
			Ext.EventManager.addListener(editorInstance.EditingArea.Document.documentElement, 'mouseup', resetImageGallerySize);

			//custom width fix - custom set width is lost every time edit mode is switched (iframe is recreated)
			if (('undefined' != typeof(attachedField.fckEditingAreaWidth)) && editorInstance.EditorWindow) {
				editorInstance.EditorWindow._FCKEditingArea.Document.body.style.width =  String(attachedField.fckEditingAreaWidth) + 'px';
			}
		}

		resetImageGallerySize();
	};

	var setListenersToTargetElement = function() {
		if (editorInstance.EditingArea.TargetElement) {
			Ext.EventManager.addListener(editorInstance.EditingArea.TargetElement, 'keyup', updateTextArea);
			Ext.EventManager.addListener(editorInstance.EditingArea.TargetElement, 'click', updateTextArea);
			Ext.EventManager.addListener(editorInstance.EditingArea.TargetElement, 'focus', updateTextArea);
		}
	};

	if (editorInstance.Events) {
		editorInstance.Events.AttachEvent('OnSelectionChange', attachedField.fckAttachImageResizeHandlers);
		//must be done every time cause iframe gets reloaded
		editorInstance.Events.AttachEvent('OnAfterSetHTML', setListenersToIframe);

		editorInstance.Events.AttachEvent('OnFocus', resetImageGallerySize);
		editorInstance.Events.AttachEvent('OnPaste', resetImageGallerySize);
	}

	if (editorInstance.EditingArea) {
		setListenersToTargetElement();
		setListenersToIframe();
	}

	attachedField.ownerCt.on('resize', attachedField.refreshEditorSize, attachedField);
	attachedField.ownerCt.on('move', attachedField.refreshEditorSize, attachedField);

	var waitForCSS = function(editorInstance, attachedField, currentCSSLength, cssIndex, tries) {
		if ('undefined' == typeof(currentCSSLength)) {
			var currentCSSLength = -1;
		}
		if ('undefined' == typeof(tries)) {
			var tries = 0;
		}

		if (window.FCKeditorAPI.Instances[editorInstance.Name].EditorDocument.styleSheets[cssIndex].cssRules){ // Standards Compliant
			var thecss = window.FCKeditorAPI.Instances[editorInstance.Name].EditorDocument.styleSheets[cssIndex].cssRules;
		} else {
			var thecss = window.FCKeditorAPI.Instances[editorInstance.Name].EditorDocument.styleSheets[cssIndex].rules;  // IE
		}

		tries++;

		/**
		 * wait for CSS to load to a maximum of 10 secconds
		 */
		if (0 == thecss.length) {
			if (((thecss.length != currentCSSLength) || (0 == thecss.length)) && (10 >= tries)) {
				currentCSSLength = thecss.length;
				setTimeout(function() {
					waitForCSS(editorInstance, attachedField, currentCSSLength, cssIndex, tries);
				},1000);
				return true;
			} else {
				initFCKSize(editorInstance, attachedField, thecss);
				return true;
			}
		} else {
			initFCKSize(editorInstance, attachedField, thecss);
			return true;
		}
	}

	var initFCKSize = function(editorInstance, attachedField, thecss){
		for (var j = 0; j < thecss.length; j++) {
			if ((thecss[j].selectorText == '#' + attachedField.fckWidthCSSSelector) && thecss[j].style.width ) {
				var width = thecss[j].style.width;
				var editingAreaWidth = width.replace('px','');
				var iframeWidth = parseInt(editingAreaWidth) + 31;
				if ('' != iframeWidth) {
					if (editorInstance.EditorWindow && editorInstance.EditorWindow.parent && editorInstance.EditorWindow.parent.frameElement && editorInstance.EditorWindow.parent.frameElement.width) {
						editorInstance.EditorWindow.parent.frameElement.width = iframeWidth;
						if ('undefined' != typeof(editorInstance.EditorWindow.parent.frameElement.style)) {
							if ('undefined' != typeof(editorInstance.EditorWindow.parent.frameElement.style.width)) {
								editorInstance.EditorWindow.parent.frameElement.style.width = String(iframeWidth) + 'px';

								//this information must be stored because it's lost once edit mode is switched (iframe gets re-created)
								editorInstance.EditorWindow._FCKEditingArea.Document.body.style.width =  String(editingAreaWidth) + 'px';
							}
						}
						attachedField.fckWidth = iframeWidth;
						attachedField.fckEditingAreaWidth = editingAreaWidth;

						attachedField.setWidth(iframeWidth);
						attachedField.container.dom.style.width = iframeWidth + 'px';
						attachedField.fckSizeInitialized = true;
					}
				}
			}
		}

		resetImageGallerySize();
	}

	attachedField.fixIE8Height();

	// Adjust fck width according to body ID css definition
	if (!attachedField.fckSizeInitialized && editorInstance.Config.EditorAreaCSS && attachedField.fckWidthCSSSelector) {
		try {
			var thecss;
			var docStylesheets = editorInstance.EditorDocument.styleSheets;
			for (var i = 0; i < docStylesheets.length; i++) {
				if (docStylesheets[i].href && docStylesheets[i].href.match(editorInstance.Config.EditorAreaCSS)) {
					waitForCSS(editorInstance, attachedField, 0, i, 0);
				}
			}
		} catch (e) {}
	}

	Ext.MessageBox.hide();
	return true;
};/*
http://www.extjs.com/forum/showthread.php?t=54602
*/

Ext.ux.LinkButton = Ext.extend(Ext.Button, {
    template: new Ext.Template(
        '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap">' +
        '	<tbody>' +
        '		<tr>',
        '			<td class="x-btn-left"><i></i></td>' +
        '			<td class="x-btn-center"><a style="text-decoration: none;" class="x-btn-text" href="{1}" target="{2}">{0}</a></td>' +
        '			<td class="x-btn-right"><i></i></td>',
        '		</tr>' +
        '	</tbody>' +
        '</table>'
    ),
    onRender: function(ct, position){
        var btn, targs = [this.text || ' ', this.href, this.target || "_self"];
        if (position) {
            btn = this.template.insertBefore(position, targs, true);
        } else {
            btn = this.template.append(ct, targs, true);
        }
        var btnEl = btn.child("a:first");
        btnEl.on('focus', this.onFocus, this);
        btnEl.on('blur', this.onBlur, this);

        this.initButtonEl(btn, btnEl);
        btn.un(this.clickEvent, this.onClick, this);
        Ext.ButtonToggleMgr.register(this);
    }
}); /**
 * @class Ext.ux.form.DateTime
 * @extends Ext.form.Field
 *
 * DateTime field, combination of DateField and TimeField
 *
 * @author      Ing. Jozef Sak�lo�
 * @copyright (c) 2008, Ing. Jozef Sak�lo�
 * @version   2.0
 * @revision  $Id: Ext.ux.form.DateTime.js 603 2009-03-04 22:27:06Z jozo $
 *
 * @license Ext.ux.form.DateTime is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
 * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
 *
 * @forum      22661
 *
 * @donate
 * <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
 * <input type="hidden" name="cmd" value="_s-xclick">
 * <input type="hidden" name="hosted_button_id" value="3430419">
 * <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" 
 * border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
 * <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
 * </form>
 */

Ext.ns('Ext.ux.form');

/**
 * Creates new DateTime
 * @constructor
 * @param {Object} config A config object
 */
Ext.ux.form.DateTime = Ext.extend(Ext.form.Field, {
    /**
     * @cfg {String/Object} defaultAutoCreate DomHelper element spec
     * Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
     */
     defaultAutoCreate:{tag:'input', type:'hidden'}
    /**
     * @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
     */
    ,timeWidth:100
    /**
     * @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
     */
    ,dtSeparator:' '
    /**
     * @cfg {String} hiddenFormat Format of datetime used to store value in hidden field
     * and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql format)
     */
    ,hiddenFormat:'Y-m-d H:i:s'
    /**
     * @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
     */
    ,otherToNow:true
    /**
     * @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty value.
     * If it is true then setValue() sets value of field to current date and time (defaults to false)
     */
    /**
     * @cfg {String} timePosition Where the time field should be rendered. 'right' is suitable for forms
     * and 'below' is suitable if the field is used as the grid editor (defaults to 'right')
     */
    ,timePosition:'right' // valid values:'below', 'right'
    /**
     * @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
     */
    ,dateFormat:'m/d/y'
    /**
     * @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
     */
    ,timeFormat:'g:i A'
    /**
     * @cfg {Object} dateConfig Config for DateField constructor.
     */
    /**
     * @cfg {Object} timeConfig Config for TimeField constructor.
     */

    // {{{
    /**
     * @private
     * creates DateField and TimeField and installs the necessary event handlers
     */
    ,initComponent:function() {
        // call parent initComponent
        Ext.ux.form.DateTime.superclass.initComponent.call(this);

        // create DateField
        var dateConfig = Ext.apply({}, {
             id:this.id + '-date'
            ,format:this.dateFormat || Ext.form.DateField.prototype.format
            ,width:this.timeWidth
            ,selectOnFocus:this.selectOnFocus
            ,listeners:{
                  blur:{scope:this, fn:this.onBlur}
                 ,focus:{scope:this, fn:this.onFocus}
            }
        }, this.dateConfig);
        this.df = new Ext.form.DateField(dateConfig);
        this.df.ownerCt = this;
        delete(this.dateFormat);

        // create TimeField
        var timeConfig = Ext.apply({}, {
             id:this.id + '-time'
            ,format:this.timeFormat || Ext.form.TimeField.prototype.format
            ,width:this.timeWidth
            ,selectOnFocus:this.selectOnFocus
            ,listeners:{
                  blur:{scope:this, fn:this.onBlur}
                 ,focus:{scope:this, fn:this.onFocus}
            }
        }, this.timeConfig);
        this.tf = new Ext.form.TimeField(timeConfig);
        this.tf.ownerCt = this;
        delete(this.timeFormat);

        // relay events
        this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
        this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);

    } // eo function initComponent
    // }}}
    // {{{
    /**
     * @private
     * Renders underlying DateField and TimeField and provides a workaround for side error icon bug
     */
    ,onRender:function(ct, position) {
        // don't run more than once
        if(this.isRendered) {
            return;
        }

        // render underlying hidden field
        Ext.ux.form.DateTime.superclass.onRender.call(this, ct, position);

        // render DateField and TimeField
        // create bounding table
        var t;
        if('below' === this.timePosition || 'bellow' === this.timePosition) {
            t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
                 {tag:'tr',children:[{tag:'td', style:'padding-bottom:1px', cls:'ux-datetime-date'}]}
                ,{tag:'tr',children:[{tag:'td', cls:'ux-datetime-time'}]}
            ]}, true);
        }
        else {
            t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
                {tag:'tr',children:[
                    {tag:'td',style:'padding-right:4px', cls:'ux-datetime-date'},{tag:'td', cls:'ux-datetime-time'}
                ]}
            ]}, true);
        }

        this.tableEl = t;
        this.wrap = t.wrap({cls:'x-form-field-wrap'});
//        this.wrap = t.wrap();
        this.wrap.on("mousedown", this.onMouseDown, this, {delay:10});

        // render DateField & TimeField
        this.df.render(t.child('td.ux-datetime-date'));
        this.tf.render(t.child('td.ux-datetime-time'));

        // workaround for IE trigger misalignment bug
        if(Ext.isIE && Ext.isStrict) {
//            t.select('input').applyStyles({top:0}); // http://extjs.com/forum/showthread.php?p=341651
        }

        this.on('specialkey', this.onSpecialKey, this);
        this.df.el.swallowEvent(['keydown', 'keypress']);
        this.tf.el.swallowEvent(['keydown', 'keypress']);

        // create icon for side invalid errorIcon
        if('side' === this.msgTarget) {
            var elp = this.el.findParent('.x-form-element', 10, true);
            this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});

            this.df.errorIcon = this.errorIcon;
            this.tf.errorIcon = this.errorIcon;
        }

        // setup name for submit
        this.el.dom.name = this.hiddenName || this.name || this.id;

        // prevent helper fields from being submitted
        this.df.el.dom.removeAttribute("name");
        this.tf.el.dom.removeAttribute("name");

        // we're rendered flag
        this.isRendered = true;

        // update hidden field
        this.updateHidden();

    } // eo function onRender
    // }}}
    // {{{
    /**
     * @private
     */
    ,adjustSize:Ext.BoxComponent.prototype.adjustSize
    // }}}
    // {{{
    /**
     * @private
     */
    ,alignErrorIcon:function() {
        this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
    }
    // }}}
    // {{{
    /**
     * @private initializes internal dateValue
     */
    ,initDateValue:function() {
        this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
    }
    // }}}
    // {{{
    /**
     * Calls clearInvalid on the DateField and TimeField
     */
    ,clearInvalid:function(){
        this.df.clearInvalid();
        this.tf.clearInvalid();
    } // eo function clearInvalid
    // }}}
    // {{{
    /**
     * Calls markInvalid on both DateField and TimeField
     * @param {String} msg Invalid message to display
     */
    ,markInvalid:function(msg){
        this.df.markInvalid(msg);
        this.tf.markInvalid(msg);
    } // eo function markInvalid
    // }}}
    // {{{
    /**
     * @private
     * called from Component::destroy. 
     * Destroys all elements and removes all listeners we've created.
     */
    ,beforeDestroy:function() {
        if(this.isRendered) {
//            this.removeAllListeners();
            this.wrap.removeAllListeners();
            this.wrap.remove();
            this.tableEl.remove();
            this.df.destroy();
            this.tf.destroy();
        }
    } // eo function beforeDestroy
    // }}}
    // {{{
    /**
     * Disable this component.
     * @return {Ext.Component} this
     */
    ,disable:function() {
        if(this.isRendered) {
            this.df.disabled = this.disabled;
            this.df.onDisable();
            this.tf.onDisable();
        }
        this.disabled = true;
        this.df.disabled = true;
        this.tf.disabled = true;
        this.fireEvent("disable", this);
        return this;
    } // eo function disable
    // }}}
    // {{{
    /**
     * Enable this component.
     * @return {Ext.Component} this
     */
    ,enable:function() {
        if(this.rendered){
            this.df.onEnable();
            this.tf.onEnable();
        }
        this.disabled = false;
        this.df.disabled = false;
        this.tf.disabled = false;
        this.fireEvent("enable", this);
        return this;
    } // eo function enable
    // }}}
    // {{{
    /**
     * @private Focus date filed
     */
    ,focus:function() {
        this.df.focus();
    } // eo function focus
    // }}}
    // {{{
    /**
     * @private
     */
    ,getPositionEl:function() {
        return this.wrap;
    }
    // }}}
    // {{{
    /**
     * @private
     */
    ,getResizeEl:function() {
        return this.wrap;
    }
    // }}}
    // {{{
    /**
     * @return {Date/String} Returns value of this field
     */
    ,getValue:function() {
        // create new instance of date
        return this.dateValue ? new Date(this.dateValue) : '';
    } // eo function getValue
    // }}}
    // {{{
    /**
     * @return {Boolean} true = valid, false = invalid
     * @private Calls isValid methods of underlying DateField and TimeField and returns the result
     */
    ,isValid:function() {
        return this.df.isValid() && this.tf.isValid();
    } // eo function isValid
    // }}}
    // {{{
    /**
     * Returns true if this component is visible
     * @return {boolean} 
     */
    ,isVisible : function(){
        return this.df.rendered && this.df.getActionEl().isVisible();
    } // eo function isVisible
    // }}}
    // {{{
    /** 
     * @private Handles blur event
     */
    ,onBlur:function(f) {
        // called by both DateField and TimeField blur events

        // revert focus to previous field if clicked in between
        if(this.wrapClick) {
            f.focus();
            this.wrapClick = false;
        }

        // update underlying value
        if(f === this.df) {
            this.updateDate();
        }
        else {
            this.updateTime();
        }
        this.updateHidden();

        // fire events later
        (function() {
            if(!this.df.hasFocus && !this.tf.hasFocus) {
                var v = this.getValue();
                if(String(v) !== String(this.startValue)) {
                    this.fireEvent("change", this, v, this.startValue);
                }
                this.hasFocus = false;
                this.fireEvent('blur', this);
            }
        }).defer(100, this);

    } // eo function onBlur
    // }}}
    // {{{
    /**
     * @private Handles focus event
     */
    ,onFocus:function() {
        if(!this.hasFocus){
            this.hasFocus = true;
            this.startValue = this.getValue();
            this.fireEvent("focus", this);
        }
    }
    // }}}
    // {{{
    /**
     * @private Just to prevent blur event when clicked in the middle of fields
     */
    ,onMouseDown:function(e) {
        if(!this.disabled) {
            this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
        }
    }
    // }}}
    // {{{
    /**
     * @private
     * Handles Tab and Shift-Tab events
     */
    ,onSpecialKey:function(t, e) {
        var key = e.getKey();
        if(key === e.TAB) {
            if(t === this.df && !e.shiftKey) {
                e.stopEvent();
                this.tf.focus();
            }
            if(t === this.tf && e.shiftKey) {
                e.stopEvent();
                this.df.focus();
            }
        }
        // otherwise it misbehaves in editor grid
        if(key === e.ENTER) {
            this.updateValue();
        }

    } // eo function onSpecialKey
    // }}}
    // {{{
    /**
     * @private Sets the value of DateField
     */
    ,setDate:function(date) {
        this.df.setValue(date);
    } // eo function setDate
    // }}}
    // {{{
    /** 
     * @private Sets the value of TimeField
     */
    ,setTime:function(date) {
        this.tf.setValue(date);
    } // eo function setTime
    // }}}
    // {{{
    /**
     * @private
     * Sets correct sizes of underlying DateField and TimeField
     * With workarounds for IE bugs
     */
    ,setSize:function(w, h) {
        if(!w) {
            return;
        }
        if('below' === this.timePosition) {
            this.df.setSize(w, h);
            this.tf.setSize(w, h);
            if(Ext.isIE) {
                this.df.el.up('td').setWidth(w);
                this.tf.el.up('td').setWidth(w);
            }
        }
        else {
            this.df.setSize(w - this.timeWidth - 4, h);
            this.tf.setSize(this.timeWidth, h);

            if(Ext.isIE) {
                this.df.el.up('td').setWidth(w - this.timeWidth - 4);
                this.tf.el.up('td').setWidth(this.timeWidth);
            }
        }
    } // eo function setSize
    // }}}
    // {{{
    /**
     * @param {Mixed} val Value to set
     * Sets the value of this field
     */
    ,setValue:function(val) {
        if(!val && true === this.emptyToNow) {
            this.setValue(new Date());
            return;
        }
        else if(!val) {
            this.setDate('');
            this.setTime('');
            this.updateValue();
            return;
        }
        if ('number' === typeof val) {
          val = new Date(val);
        }
        else if('string' === typeof val && this.hiddenFormat) {
            val = Date.parseDate(val, this.hiddenFormat)
        }
        val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
        var da, time;
        if(val instanceof Date) {
            this.setDate(val);
            this.setTime(val);
            this.dateValue = new Date(val);
        }
        else {
            da = val.split(this.dtSeparator);
            this.setDate(da[0]);
            if(da[1]) {
                if(da[2]) {
                    // add am/pm part back to time
                    da[1] += da[2];
                }
                this.setTime(da[1]);
            }
        }
        this.updateValue();
    } // eo function setValue
    // }}}
    // {{{
    /**
     * Hide or show this component by boolean
     * @return {Ext.Component} this
     */
    ,setVisible: function(visible){
        if(visible) {
            this.df.show();
            this.tf.show();
        }else{
            this.df.hide();
            this.tf.hide();
        }
        return this;
    } // eo function setVisible
    // }}}
    //{{{
    ,show:function() {
        return this.setVisible(true);
    } // eo function show
    //}}}
    //{{{
    ,hide:function() {
        return this.setVisible(false);
    } // eo function hide
    //}}}
    // {{{
    /**
     * @private Updates the date part
     */
    ,updateDate:function() {

        var d = this.df.getValue();
        if(d) {
            if(!(this.dateValue instanceof Date)) {
                this.initDateValue();
                if(!this.tf.getValue()) {
                    this.setTime(this.dateValue);
                }
            }
            this.dateValue.setMonth(0); // because of leap years
            this.dateValue.setFullYear(d.getFullYear());
            this.dateValue.setMonth(d.getMonth(), d.getDate());
//            this.dateValue.setDate(d.getDate());
        }
        else {
            this.dateValue = '';
            this.setTime('');
        }
    } // eo function updateDate
    // }}}
    // {{{
    /**
     * @private
     * Updates the time part
     */
    ,updateTime:function() {
        var t = this.tf.getValue();
        if(t && !(t instanceof Date)) {
            t = Date.parseDate(t, this.tf.format);
        }
        if(t && !this.df.getValue()) {
            this.initDateValue();
            this.setDate(this.dateValue);
        }
        if(this.dateValue instanceof Date) {
            if(t) {
                this.dateValue.setHours(t.getHours());
                this.dateValue.setMinutes(t.getMinutes());
                this.dateValue.setSeconds(t.getSeconds());
            }
            else {
                this.dateValue.setHours(0);
                this.dateValue.setMinutes(0);
                this.dateValue.setSeconds(0);
            }
        }
    } // eo function updateTime
    // }}}
    // {{{
    /**
     * @private Updates the underlying hidden field value
     */
    ,updateHidden:function() {
        if(this.isRendered) {
            var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
            this.el.dom.value = value;
        }
    }
    // }}}
    // {{{
    /**
     * @private Updates all of Date, Time and Hidden
     */
    ,updateValue:function() {

        this.updateDate();
        this.updateTime();
        this.updateHidden();

        return;
    } // eo function updateValue
    // }}}
    // {{{
    /**
     * @return {Boolean} true = valid, false = invalid
     * calls validate methods of DateField and TimeField
     */
    ,validate:function() {
        return this.df.validate() && this.tf.validate();
    } // eo function validate
    // }}}
    // {{{
    /**
     * Returns renderer suitable to render this field
     * @param {Object} Column model config
     */
    ,renderer: function(field) {
        var format = field.editor.dateFormat || Ext.ux.form.DateTime.prototype.dateFormat;
        format += ' ' + (field.editor.timeFormat || Ext.ux.form.DateTime.prototype.timeFormat);
        var renderer = function(val) {
            var retval = Ext.util.Format.date(val, format);
            return retval;
        };
        return renderer;
    } // eo function renderer
    // }}}

}); // eo extend

// register xtype
Ext.reg('xdatetime', Ext.ux.form.DateTime);/*
 * Source: http://extjs.com/forum/showthread.php?t=21331
 * Extension description : Grid Summary Plugin With Fixed Summary Row
 * Created by  : mystix - Ext Support Team (http://extjs.com/forum/member.php?u=1459)
 */

Ext.ns('Ext.ux.grid');

Ext.ux.grid.GridSummary = function(config) {
	Ext.apply(this, config);
};

Ext.extend(Ext.ux.grid.GridSummary, Ext.util.Observable, {
	init : function(grid) {
		this.grid = grid;
		this.cm = grid.getColumnModel();
		this.view = grid.getView();

		var v = this.view;

		// override GridView's onLayout() method
		v.onLayout = this.onLayout;

		v.afterMethod('render', this.refreshSummary, this);
		v.afterMethod('refresh', this.refreshSummary, this);
		v.afterMethod('syncScroll', this.syncSummaryScroll, this);
		v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
		v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
		v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);

		// update summary row on store's add/remove/clear/update events
		grid.store.on({
			add: this.refreshSummary,
			remove: this.refreshSummary,
			clear: this.refreshSummary,
			update: this.refreshSummary,
			scope: this
		});

		if (!this.rowTpl) {
			this.rowTpl = new Ext.Template(
				'<div class="x-grid3-summary-row x-grid3-gridsummary-row-offset">',
					'<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
						'<tbody><tr>{cells}</tr></tbody>',
					'</table>',
				'</div>'
			);
			this.rowTpl.disableFormats = true;
		}
		this.rowTpl.compile();

		if (!this.cellTpl) {
			this.cellTpl = new Ext.Template(
				'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
					'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
				"</td>"
			);
			this.cellTpl.disableFormats = true;
		}
		this.cellTpl.compile();
	},

	calculate : function(rs, cm) {
		var data = {}, cfg = cm.config;
		for (var i = 0, len = cfg.length; i < len; i++) { // loop through all columns in ColumnModel
			var cf = cfg[i], // get column's configuration
				cname = cf.dataIndex; // get column dataIndex

			// initialise grid summary row data for
			// the current column being worked on
			data[cname] = 0;

			if (cf.summaryType) {
				for (var j = 0, jlen = rs.length; j < jlen; j++) {
					var r = rs[j]; // get a single Record
					data[cname] = Ext.ux.grid.GridSummary.Calculations[cf.summaryType](r.get(cname), r, cname, data, j);
				}
			}
		}

		return data;
	},

	onLayout : function(vw, vh) {
		if (Ext.type(vh) != 'number') { // handles grid's height:'auto' config
			return;
		}
		// note: this method is scoped to the GridView
		if (!this.grid.getGridEl().hasClass('x-grid-hide-gridsummary')) {
			// readjust gridview's height only if grid summary row is visible
			this.scroller.setHeight(vh - this.summary.getHeight());
		}
	},

	syncSummaryScroll : function() {
		var mb = this.view.scroller.dom;

		this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft;
		this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
	},

	doWidth : function(col, w, tw) {
		var s = this.view.summary.dom;

		s.firstChild.style.width = tw;
		s.firstChild.rows[0].childNodes[col].style.width = w;
	},

	doAllWidths : function(ws, tw) {
		var s = this.view.summary.dom, wlen = ws.length;

		s.firstChild.style.width = tw;

		var cells = s.firstChild.rows[0].childNodes;

		for (var j = 0; j < wlen; j++) {
			cells[j].style.width = ws[j];
		}
	},

	doHidden : function(col, hidden, tw) {
		var s = this.view.summary.dom,
			display = hidden ? 'none' : '';

		s.firstChild.style.width = tw;
		s.firstChild.rows[0].childNodes[col].style.display = display;
	},

	renderSummary : function(o, cs, cm) {
		cs = cs || this.view.getColumnData();
		var cfg = cm.config,
			buf = [],
			last = cs.length - 1;

		for (var i = 0, len = cs.length; i < len; i++) {
			var c = cs[i], cf = cfg[i], p = {};

			p.id = c.id;
			p.style = c.style;
			p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');

			if (cf.summaryType || cf.summaryRenderer) {
				p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
			} else {
				p.value = '';
			}
			if (p.value == undefined || p.value === "") p.value = "&#160;";
			buf[buf.length] = this.cellTpl.apply(p);
		}

		return this.rowTpl.apply({
			tstyle: 'width:' + this.view.getTotalWidth() + ';',
			cells: buf.join('')
		});
	},

	refreshSummary : function() {
		var g = this.grid, ds = g.store,
			cs = this.view.getColumnData(),
			cm = this.cm,
			rs = ds.getRange(),
			data = this.calculate(rs, cm),
			buf = this.renderSummary({data: data}, cs, cm);

		if (!this.view.summaryWrap) {
			this.view.summaryWrap = Ext.DomHelper.insertAfter(this.view.scroller, {
				tag: 'div',
				cls: 'x-grid3-gridsummary-row-inner'
			}, true);
		}
		this.view.summary = this.view.summaryWrap.update(buf).first();
	},

	toggleSummary : function(visible) { // true to display summary row
		var el = this.grid.getGridEl();

		if (el) {
			if (visible === undefined) {
				visible = el.hasClass('x-grid-hide-gridsummary');
			}
			el[visible ? 'removeClass' : 'addClass']('x-grid-hide-gridsummary');

			this.view.layout(); // readjust gridview height
		}
	},

	getSummaryNode : function() {
		return this.view.summary
	}
});
Ext.reg('gridsummary', Ext.ux.grid.GridSummary);

/*
 * all Calculation methods are called on each Record in the Store
 * with the following 5 parameters:
 *
 * v - cell value
 * record - reference to the current Record
 * colName - column name (i.e. the ColumnModel's dataIndex)
 * data - the cumulative data for the current column + summaryType up to the current Record
 * rowIdx - current row index
 */
Ext.ux.grid.GridSummary.Calculations = {
	sum : function(v, record, colName, data, rowIdx) {
		return data[colName] + Ext.num(v, 0);
	},

	count : function(v, record, colName, data, rowIdx) {
		return rowIdx + 1;
	},

	max : function(v, record, colName, data, rowIdx) {
		return Math.max(Ext.num(v, 0), data[colName]);
	},

	min : function(v, record, colName, data, rowIdx) {
		return Math.min(Ext.num(v, 0), data[colName]);
	},

	average : function(v, record, colName, data, rowIdx) {
		var t = data[colName] + Ext.num(v, 0), count = record.store.getCount();
		return rowIdx == count - 1 ? (t / count) : t;
	}
}

Ext.ux.GridPrinter = {
  /**
   * Prints the passed grid. Reflects on the grid's column model to build a table, and fills it using the store
   * @param {Ext.grid.GridPanel} grid The grid to print
   */
  print: function(grid) {
	var gridBody = Ext.fly(grid.getGridEl().dom.cloneNode(true));
	gridBody.child('.x-grid3').setStyle('height', 'auto');
	gridBody.child('.x-grid3-scroller').setStyle('height', 'auto');
	
	
	var html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' +
	  '<html>' +
		'<head>' +
		  '<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />' +
		  '<link href="' + Ext.ux.GridPrinter.stylesheetPath + '" rel="stylesheet" type="text/css" media="screen,print" />' +
		  '<link href="/css/cp_ext.css" rel="stylesheet" type="text/css" media="screen,print" />' +
		  '<title>' + grid.title + '</title>' +
		'</head>' +
		'<body>' + gridBody.dom.innerHTML + '</body>' +
	  '</html>';

	//open up a new printing window, write to it, print it and close
	var win = window.open('about:blank', 'printgrid');

	win.document.write(html);
	win.document.close();

//	win.print();
//	win.close();
  },

  /**
   * @property stylesheetPath
   * @type String
   * The path at which the print stylesheet can be found (defaults to '/stylesheets/print.css')
   */
  stylesheetPath: '/js/ext-ensions/gridprinter/css/Ext.ux.GridPrinter.css'
};// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.form.LovCombo, List of Values Combo
 *
 * @author    Ing. Jozef Sakáloš
 * @copyright (c) 2008, by Ing. Jozef Sakáloš
 * @date      16. April 2008
 * @version   $Id: Ext.ux.form.LovCombo.js 248 2008-05-09 15:54:40Z jozo $
 *
 * @license Ext.ux.form.LovCombo.js is licensed under the terms of the Open Source
 * LGPL 3.0 license. Commercial use is permitted to the extent that the 
 * code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */
 
/*global Ext */

// add RegExp.escape if it has not been already added
if('function' !== typeof RegExp.escape) {
	RegExp.escape = function(s) {
		if('string' !== typeof s) {
			return s;
		}
		// Note: if pasting from forum, precede ]/\ with backslash manually
		return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
	}; // eo function escape
}

// create namespace
Ext.ns('Ext.ux.form');
 
/**
 *
 * @class Ext.ux.form.LovCombo
 * @extends Ext.form.ComboBox
 */
Ext.ux.form.LovCombo = Ext.extend(Ext.form.ComboBox, {

	// {{{
    // configuration options
	/**
	 * @cfg {String} checkField name of field used to store checked state.
	 * Change it only if it collides with your normal field
	 */
	 checkField:'checked'

	/**
	 * @cfg {String} separator separator to use between values and texts
	 */
    ,separator:','
	 // }}}
    // {{{
    ,initComponent:function() {
        
		// template with checkbox
		if(!this.tpl) {
			this.tpl = 
				 '<tpl for=".">'
				+'<div class="x-combo-list-item">'
				+'<img src="' + Ext.BLANK_IMAGE_URL + '" '
				+'class="ux-lovcombo-icon ux-lovcombo-icon-'
				+'{[values.' + this.checkField + '?"checked":"unchecked"' + ']}">'
				+'<div class="ux-lovcombo-item-text">{[Ext.util.Format.htmlEncode(values.' + (this.displayField || 'text') + ')]}</div>'
				+'</div>'
				+'</tpl>'
			;
		}
 
        // call parent
        Ext.ux.form.LovCombo.superclass.initComponent.apply(this, arguments);

		// install internal event handlers
		this.on({
			 scope:this
			,beforequery:this.onBeforeQuery
			,blur:this.onRealBlur
		});

		// remove selection from input field
		this.onLoad = this.onLoad.createSequence(function() {
			if(this.el) {
				var v = this.el.dom.value;
				this.el.dom.value = '';
				this.el.dom.value = v;
			}
		});
 
    } // e/o function initComponent
    // }}}
	// {{{
	/**
	 * Disables default tab key bahavior
	 * @private
	 */
	,initEvents:function() {
		Ext.ux.form.LovCombo.superclass.initEvents.apply(this, arguments);

		// disable default tab handling - does no good
		this.keyNav.tab = false;
//		this.keyNav.del = Ext.emptyFn;
//		this.keyMap = new Ext.KeyMap(this.el, {
//			 key:Ext.EventObject.BACKSPACE
//			,fn:function(key, e) {
//				e.stopEvent();
//				return false;
//			}
//		});

	} // eo function initEvents
	// }}}
	// {{{
	/**
	 * clears value
	 */
	,clearValue:function() {
		this.value = '';
		this.setRawValue(this.value);
		this.store.clearFilter();
		this.store.each(function(r) {
			r.set(this.checkField, false);
		}, this);
		if(this.hiddenField) {
			this.hiddenField.value = '';
		}
		this.applyEmptyText();
	} // eo function clearValue
	// }}}
	// {{{
	/**
	 * @return {String} separator (plus space) separated list of selected displayFields
	 * @private
	 */
	,getCheckedDisplay:function() {
		var re = new RegExp(this.separator, "g");
		return this.getCheckedValue(this.displayField).replace(re, this.separator + ' ');
	} // eo function getCheckedDisplay
	// }}}
	// {{{
	/**
	 * @return {String} separator separated list of selected valueFields
	 * @private
	 */
	,getCheckedValue:function(field) {
		field = field || this.valueField;
		var c = [];

		// store may be filtered so get all records
		var snapshot = this.store.snapshot || this.store.data;

		snapshot.each(function(r) {
			if(r.get(this.checkField)) {
				c.push(r.get(field));
			}
		}, this);

		return c.join(this.separator);
	} // eo function getCheckedValue
	// }}}
	// {{{
	/**
	 * beforequery event handler - handles multiple selections
	 * @param {Object} qe query event
	 * @private
	 */
	,onBeforeQuery:function(qe) {
		qe.query = qe.query.replace(new RegExp(this.getCheckedDisplay() + '[ ' + this.separator + ']*'), '');
	} // eo function onBeforeQuery
	// }}}
	// {{{
	/**
	 * blur event handler - runs only when real blur event is fired
	 */
	,onRealBlur:function() {
		this.list.hide();
		var v = this.getRawValue();
		var va = [];
		this.store.clearFilter();
		this.store.each(function(r) {
			var re = new RegExp(RegExp.escape(r.get(this.displayField)));
			if(v.match(re)) {
				va.push(r.get(this.valueField));
			}
		}, this);
		this.setValue(va.join(this.separator));
	} // eo function onRealBlur
	// }}}
	// {{{
	/**
	 * Combo's onSelect override
	 * @private
	 * @param {Ext.data.Record} record record that has been selected in the list
	 * @param {Number} index index of selected (clicked) record
	 */
	,onSelect:function(record, index) {
        if(this.fireEvent('beforeselect', this, record, index) !== false){

			// toggle checked field
			record.set(this.checkField, !record.get(this.checkField));

			// display full list
			this.doQuery(this.allQuery);

			// set (update) value and fire event
			this.setValue(this.getCheckedValue());
            this.fireEvent('select', this, record, index);
        }
	} // eo function onSelect
	// }}}
	// {{{
	/**
	 * Sets the value of the LovCombo
	 * @param {Mixed} v value
	 */
	,setValue:function(v) {
		if(v) {
			v = '' + v;
			if(this.valueField) {
				this.store.clearFilter();
				this.store.each(function(r) {
					var checked = !(!v.match(
						 '(^|' + this.separator + ')' + RegExp.escape(r.get(this.valueField))
						+'(' + this.separator + '|$)'))
					;

					r.set(this.checkField, checked);
				}, this);
				this.value = this.getCheckedValue();
				this.setRawValue(this.getCheckedDisplay());
				if(this.hiddenField) {
					this.hiddenField.value = this.value;
				}
			}
			else {
				this.value = v;
				this.setRawValue(v);
				if(this.hiddenField) {
					this.hiddenField.value = v;
				}
			}
			if(this.el) {
				this.el.removeClass(this.emptyClass);
			}
		}
		else {
			this.clearValue();
		}
	} // eo function setValue
	// }}}
	// {{{
	,selectAll:function() {
        this.store.each(function(record){
            // toggle checked field
            record.set(this.checkField, true);
        }, this);

        //display full list
        this.doQuery(this.allQuery);
        this.setValue(this.getCheckedValue());
    } // eo full selectAll
	// }}}
	// {{{
    ,deselectAll:function() {
		this.clearValue();
    } // eo full deselectAll 
	// }}}

}); // eo extend
 
// register xtype
Ext.reg('lovcombo', Ext.ux.form.LovCombo); 
 
// eof
/*
 * Ext JS Library 2.1
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 *
 * http://extjs.com/license
 */


Ext.DataView.LabelEditor = function(cfg, field) {
	Ext.DataView.LabelEditor.superclass.constructor.call(this,
		field || new Ext.form.TextField({
			allowBlank: false,
			growMin:90,
			growMax:240,
			grow:true,
			selectOnFocus:true
		}), cfg
	);
}

Ext.extend(Ext.DataView.LabelEditor, Ext.Editor, {
	alignment: "tl-tl",
	hideEl : false,
	cls: "x-small-editor",
	shim: false,
	completeOnEnter: true,
	cancelOnEsc: true,
	labelSelector: 'span.x-editable',

	init: function(view) {
		this.view = view;
		view.on('render', this.initEditor, this);
		this.on('complete', this.onSave, this);
	},

	initEditor: function() {
		this.view.getEl().on('mousedown', this.onMouseDown, this, {delegate: this.labelSelector});
	},

	onMouseDown: function(e, target) {
		if (!e.ctrlKey && !e.shiftKey) {
			var item = this.view.findItemFromChild(target);
			e.stopEvent();
			var record = this.view.store.getAt(this.view.indexOf(item));
			this.startEdit(target, record.data[this.dataIndex]);
			this.activeRecord = record;
		} else {
			e.preventDefault();
		}
	},

	onSave: function(ed, value){
		this.activeRecord.set(this.dataIndex, value);
	}
});

// TODO: Better solution can be taken from the one from grid: http://www.extjs.com/forum/showthread.php?t=17826
Ext.DataView.DragSelector = function(cfg) {
	cfg = cfg || {};
	var view, regions, proxy, tracker, selectAction, dragAction;
	var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
	var dragSafe = cfg.dragSafe === true;

	this.init = function(dataView) {
		view = dataView;
		view.on('render', onRender);
	};

	function fillRegions() {
//		var selectedItems = {};
//		if (rs) {
//			for (var i = 0; i < rs.length; i++) {
//				selectedItems[rs[i].id] = rs[i].selected;
//			}
//		}

		rs = [];
		view.all.each(function(el) {
			var margins = el.getMargins();
			var itemRegion = el.getRegion();
			itemRegion.adjust(margins.top, margins.left, -margins.bottom, -margins.right);
//			itemRegion.id = el.dom.id;
//			itemRegion.selected = ('undefined' == typeof(selectedItems[el.dom.id])) ? false : selectedItems[el.dom.id];
			rs[rs.length] = itemRegion;
		});
		bodyRegion = view.container.getRegion();
	}

	// Check if empty space is the target (when clicked between items, on view or on container)
	function isEmptySpace(target) {
		return !target || (target.parentNode.id == view.el.id) || (target.id == view.el.id) || (target.id == view.container.id);
	}
	
	function cancelClick(target) {
		if (!Ext.EventObject.ctrlKey && !Ext.EventObject.shiftKey && isEmptySpace(target)) {
			view.clearSelections(); // Deselect if target is empty space and no addon to selection
		}
		return false;
	}

	function onBeforeStart(e) {
		// Allow multiple drag selecting when empty space is target for drag start
		return isEmptySpace(e.target);
	}

	function setDragRegion() {
		var startXY = tracker.startXY;
		var xy = tracker.getXY();

		var x = Math.min(startXY[0], xy[0]);
		var y = Math.min(startXY[1], xy[1]);
		var w = Math.abs(startXY[0] - xy[0]);
		var h = Math.abs(startXY[1] - xy[1]);

		dragRegion.left = x;
		dragRegion.top = y;
		dragRegion.right = x + w;
		dragRegion.bottom = y + h;
	}

	function onStart(startXY) {
		if (!dragAction.scroll) { // Triggers deselection with event arguments from mousedown
			cancelClick();
		}

		fillRegions();
		setDragRegion();

		view.fireEvent('selectionstart', view);
		selectAction = true;
//		var selectedIndex = false;
		for (var i = 0, len = rs.length; (i < len) && selectAction; i++) {
			selectAction = !dragRegion.intersect(rs[i]);
//			selectedIndex = i;
		}
		if (selectAction && !dragAction.scroll) {
			if (!proxy) {
				proxy = view.container.createChild({cls: 'x-view-selector'});
			} else {
				if (!view.container.contains(proxy.id)) {
					view.container.appendChild(proxy); // Handles reloading of store so proxy isn't lost
				}
				proxy.setDisplayed('block');
			}
		} else {
//			if ((false !== selectedIndex) && !rs[selectedIndex].selected) {
//				if (!Ext.EventObject.ctrlKey && !Ext.EventObject.shiftKey) {
//					view.clearSelections();
//				}
//				rs[selectedIndex].selected = true;
//				view.select(selectedIndex, true);
//			}
		}
	}

	function onDrag(e) {
		if (selectAction && !dragAction.scroll) {
			setDragRegion();
			dragRegion.constrainTo(bodyRegion);
			proxy.setRegion(dragRegion);

			for (var i = 0, len = rs.length; i < len; i++) {
				var sel = dragRegion.intersect(rs[i]);
				if (sel && !rs[i].selected) {
					rs[i].selected = true;
					view.select(i, true);
				} else if (!sel && rs[i].selected) {
					rs[i].selected = false;
					view.deselect(i);
				}
			}
		}
	}

	function onEnd(e) {
		if (selectAction && proxy) {
			proxy.setDisplayed(false);
		} else if (!dragAction.scroll) { // Triggers deselection if no select action was executed
			cancelClick(e.target);
		}
		selectAction = false;
		view.fireEvent('selectionend', view);
	}

	function onRender() {
		tracker = new Ext.dd.DragTracker({
			onBeforeStart: onBeforeStart,
			onStart: onStart,
			onDrag: onDrag,
			onEnd: onEnd
		});
		tracker.initEl(view.container);
		view.getEl().addClass('singleclick-dataview-view'); // Arrow cursor over DataView in IE

		view.on('containerclick', function(view, e) {
			// Needee override for IE where DataView's container click has clearSelection
			// Execute it only if target is in between items
			return e.target != view.el.dom;
		});
            
		view.container.on('mousedown', function(e, target) {
			dragAction = { // Needed to set in onStart() whether selection should be cleared
				scroll: false
			};
		});

		view.container.on('scroll', function() {
			if (dragAction) {
				dragAction.scroll = true; // Activate scrolling state
			}
		});
	}
};Ext.namespace('Ext.ux.form');

/**
 * @class Ext.ux.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
 */
Ext.ux.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(){
		Ext.ux.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 || null;
		this.originalScope = this.scope || window;
		this.handler = null;
		this.scope = null;
	},
	
	/**
	 * @see Ext.Button.onRender
	 */
	onRender: function(ct, position){
		Ext.ux.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'
			}
		});
		
		// CUSTOM TWEAK: IE bug with fix for click doesent work alaways, more on http://extjs.com/forum/showthread.php?t=29032&page=3 post #24
		this.clipEl.applyStyles({
			'background-color': 'green'
		});
		this.floatEl.applyStyles({
			'background-color': 'red'
		});
		
		if (!this.debug) {
			this.clipEl.setOpacity(0.0);
		}
		/*if (this.debug) {
			this.clipEl.applyStyles({
				'background-color': 'green'
			});
			this.floatEl.applyStyles({
				'background-color': 'red'
			});
		} else {
			this.clipEl.setOpacity(0.0);
		}*/
		
		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();
			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(){
	
		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),
			// 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,
			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;
			}
		}
	},
	
	/**
	 * 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);
	},
	
	/**
	 * 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(){
		Ext.ux.form.BrowseButton.superclass.disable.call(this);
		this.inputFileEl.dom.disabled = true;
	},
	
	/**
	 * @see Ext.Button.enable
	 */
	enable: function(){
		Ext.ux.form.BrowseButton.superclass.enable.call(this);
		this.inputFileEl.dom.disabled = false;
	}
});

Ext.reg('browsebutton', Ext.ux.form.BrowseButton);
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.FileUploader
 *
 * @author  Ing. Jozef Sakáloš
 * @version $Id: Ext.ux.FileUploader.js 83 2008-03-21 12:54:35Z jozo $
 * @date    15. March 2008
 *
 * @license Ext.ux.FileUploader is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext */

/**
 * @class Ext.ux.FileUploader
 * @extends Ext.util.Observable
 * @constructor
 */
Ext.ux.FileUploader = function(config) {
	Ext.apply(this, config);

	// call parent
	Ext.ux.FileUploader.superclass.constructor.apply(this, arguments);

	// add events
	// {{{
	this.addEvents(
		/**
		 * @event beforeallstart
		 * Fires before an upload (of all files) is started. Return false to cancel the event.
		 * @param {Ext.ux.FileUploader} this
		 */
		 'beforeallstart'
		/**
		 * @event allfinished
		 * Fires after upload (of all files) is finished
		 * @param {Ext.ux.FileUploader} this
		 */
		,'allfinished'
		/**
		 * @event beforefilestart
		 * Fires before the file upload is started. Return false to cancel the event.
		 * Fires only when singleUpload = false
		 * @param {Ext.ux.FileUploader} this
		 * @param {Ext.data.Record} record upload of which is being started
		 */
		,'beforefilestart'
		/**
		 * @event filefinished
		 * Fires when file finished uploading.
		 * Fires only when singleUpload = false
		 * @param {Ext.ux.FileUploader} this
		 * @param {Ext.data.Record} record upload of which has finished
		 */
		,'filefinished'
		/**
		 * @event progress
		 * Fires when progress has been updated
		 * @param {Ext.ux.FileUploader} this
		 * @param {Object} data Progress data object
		 * @param {Ext.data.Record} record Only if singleUpload = false
		 */
		,'progress'
	);
	// }}}

}; // eo constructor

Ext.extend(Ext.ux.FileUploader, Ext.util.Observable, {
	
	// configuration options
	// {{{
	/**
	 * @cfg {Object} baseParams baseParams are sent to server in each request.
	 */
	 baseParams:{cmd:'upload',dir:'.'}

	/**
	 * @cfg {Boolean} concurrent true to start all requests upon upload start, false to start
	 * the next request only if previous one has been completed (or failed). Applicable only if
	 * singleUpload = false
	 */
	,concurrent:true

	/**
	 * @cfg {Boolean} enableProgress true to enable querying server for progress information
	 */
	,enableProgress:true

	/**
	 * @cfg {String} jsonErrorText Text to use for json error
	 */
	,jsonErrorText:'Cannot decode JSON object'

	/**
	 * @cfg {Number} Maximum client file size in bytes
	 */
	,maxFileSize:524288

	/**
	 * @cfg {String} progressIdName Name to give hidden field for upload progress identificator
	 */
	,progressIdName:'UPLOAD_IDENTIFIER'

	/**
	 * @cfg {Number} progressInterval How often (in ms) is progress requested from server
	 */
	,progressInterval:2000

	/**
	 * @cfg {String} progressUrl URL to request upload progress from
	 */
	,progressUrl:'progress.php'

	/**
	 * @cfg {Object} progressMap Mapping of received progress fields to store progress fields
	 */
	,progressMap:{
		 bytes_total:'bytesTotal'
		,bytes_uploaded:'bytesUploaded'
		,est_sec:'estSec'
		,files_uploaded:'filesUploaded'
		,speed_average:'speedAverage'
		,speed_last:'speedLast'
		,time_last:'timeLast'
		,time_start:'timeStart'
	}
	/**
	 * @cfg {Boolean} singleUpload true to upload files in one form, false to upload one by one
	 */
	,singleUpload:false
	
	/**
	 * @cfg {Ext.data.Store} store Mandatory. Store that holds files to upload
	 */

	/**
	 * @cfg {String} unknownErrorText Text to use for unknow error
	 */
	,unknownErrorText:'Unknown error'

	/**
	 * @cfg {String} url Mandatory. URL to upload to
	 */

	// }}}

	// private
	// {{{
	/**
	 * uploads in progress count
	 * @private
	 */
	,upCount:0
	// }}}

	// methods
	// {{{
	/**
	 * creates form to use for upload.
	 * @private
	 * @return {Ext.Element} form
	 */
	,createForm:function(record) {
		var progressId = parseInt(Math.random() * 1e10, 10);
		var form = Ext.getBody().createChild({
			 tag:'form'
			,action:this.url
			,method:'post'
			,cls:'x-hidden'
			,id:Ext.id()
			,cn:[{
				 tag:'input'
				,type:'hidden'
				,name:'APC_UPLOAD_PROGRESS'
				,value:progressId
			},{
				 tag:'input'
				,type:'hidden'
				,name:this.progressIdName
				,value:progressId
			},{
				 tag:'input'
				,type:'hidden'
				,name:'MAX_FILE_SIZE'
				,value:this.maxFileSize
			}]
		});
		if(record) {
			record.set('form', form);
			record.set('progressId', progressId);
		}
		else {
			this.progressId = progressId;
		}
		return form;

	} // eo function createForm
	// }}}
	// {{{
	,deleteForm:function(form, record) {
		form.remove();
		if(record) {
			record.set('form', null);
		}
	} // eo function deleteForm
	// }}}
	// {{{
	/**
	 * Fires event(s) on upload finish/error
	 * @private
	 */
	,fireFinishEvents:function(options) {
		if(true !== this.eventsSuspended && !this.singleUpload) {
			this.fireEvent('filefinished', this, options && options.record);
		}
		if(true !== this.eventsSuspended && 0 === this.upCount) {
			this.stopProgress();
			this.fireEvent('allfinished', this);
		}
	} // eo function fireFinishEvents
	// }}}
	// {{{
	/**
	 * Geg the iframe identified by record
	 * @private
	 * @param {Ext.data.Record} record
	 * @return {Ext.Element} iframe or null if not found
	 */
	,getIframe:function(record) {
		var iframe = null;
		var form = record.get('form');
		if(form && form.dom && form.dom.target) {
			iframe = Ext.get(form.dom.target);
		}
		return iframe;
	} // eo function getIframe
	// }}}
	// {{{
	/**
	 * returns options for Ajax upload request
	 * @private
	 * @param {Ext.data.Record} record
	 * @param {Object} params params to add
	 */
	,getOptions:function(record, params) {
		var o = {
			 url:this.url
			,method:'post'
			,isUpload:true
			,scope:this
			,callback:this.uploadCallback
			,record:record
			,params:this.getParams(record, params)
		};
		return o;
	} // eo function getOptions
	// }}}
	// {{{
	/**
	 * get params to use for request
	 * @private
	 * @return {Object} params
	 */
	,getParams:function(record, params) {
		var p = {path:this.path};
		Ext.apply(p, this.baseParams || {}, params || {});
		return p;
	}
	// }}}
	// {{{
	/**
	 * processes success response
	 * @private
	 * @param {Object} options options the request was called with
	 * @param {Object} response request response object
	 * @param {Object} o decoded response.responseText
	 */
	,processSuccess:function(options, response, o) {
		var record = false;

		// all files uploadded ok
		if(this.singleUpload) {
			this.store.each(function(r) {
				r.set('state', 'done');
				r.set('error', '');
				r.commit();
			});
		}
		else {
			record = options.record;
			record.set('state', 'done');
			record.set('error', '');
			record.commit();
		}

		this.deleteForm(options.form, record);

	} // eo processSuccess
	// }}}
	// {{{
	/**
	 * processes failure response
	 * @private
	 * @param {Object} options options the request was called with
	 * @param {Object} response request response object
	 * @param {String/Object} error Error text or JSON decoded object. Optional.
	 */
	,processFailure:function(options, response, error) {
		var record = options.record;
		var records;

		// singleUpload - all files uploaded in one form
		if(this.singleUpload) {
			// some files may have been successful
			//Goran note: Line below is wrong, I replaced it with new code. For more info see: http://extjs.com/forum/showthread.php?t=29090&page=43 post #423
//			records = this.store.queryBy(function(r){return 'done' !== r.get('state');});
			records = this.store.queryBy(function(r){
                var state = r.get('state');
                return 'done' !== state && 'uploading' !== state;
            });
			records.each(function(record) {
				var e = error.errors ? error.errors[record.id] : this.unknownErrorText;
				if(e) {
					record.set('state', 'failed');
					record.set('error', e);
					Ext.getBody().appendChild(record.get('input'));
				}
				else {
					record.set('state', 'done');
					record.set('error', '');
				}
				record.commit();
			}, this);

			this.deleteForm(options.form);
		}
		// multipleUpload - each file uploaded in it's own form
		else {
			if(error && 'object' === Ext.type(error)) {
				record.set('error', error.errors && error.errors[record.id] ? error.errors[record.id] : this.unknownErrorText);
			}
			else if(error) {
				record.set('error', error);
			}
			else if(response && response.responseText) {
				record.set('error', response.responseText);
			}
			else {
				record.set('error', this.unknownErrorText);
			}
			record.set('state', 'failed');
			record.commit();
		}
	} // eof processFailure
	// }}}
	// {{{
	/**
	 * Delayed task callback
	 */
	,requestProgress:function() {
		var records, p;
		var o = {
			 url:this.progressUrl
			,method:'post'
			,params:{}
			,scope:this
			,callback:function(options, success, response) {
				var o;
				if(true !== success) {
					return;
				}
				try {
					o = Ext.decode(response.responseText);
				}
				catch(e) {
					return;
				}
				if('object' !== Ext.type(o) || true !== o.success) {
					return;
				}

				if(this.singleUpload) {
					this.progress = {};
					for(p in o) {
						if(this.progressMap[p]) {
							this.progress[this.progressMap[p]] = parseInt(o[p], 10);
						}
					}
					if(true !== this.eventsSuspended) {
						this.fireEvent('progress', this, this.progress);
					}

				}
				else {
					for(p in o) {
						if(this.progressMap[p] && options.record) {
							options.record.set(this.progressMap[p], parseInt(o[p], 10));
						}
					}
					if(options.record) {
						options.record.commit();
						if(true !== this.eventsSuspended) {
							this.fireEvent('progress', this, options.record.data, options.record);
						}
					}
				}
				this.progressTask.delay(this.progressInterval);
			}
		};
		if(this.singleUpload) {
			o.params[this.progressIdName] = this.progressId;
			o.params.APC_UPLOAD_PROGRESS = this.progressId;
			Ext.Ajax.request(o);
		}
		else {
			records = this.store.query('state', 'uploading');
			records.each(function(r) {
				o.params[this.progressIdName] = r.get('progressId');
				o.params.APC_UPLOAD_PROGRESS = o.params[this.progressIdName];
				o.record = r;
				(function() {
					Ext.Ajax.request(o);
				}).defer(250);
			}, this);
		}
	} // eo function requestProgress
	// }}}
	// {{{
	/**
	 * path setter
	 * @private
	 */
	,setPath:function(path) {
		this.path = path;
	} // eo setPath
	// }}}
	// {{{
	/**
	 * url setter
	 * @private
	 */
	,setUrl:function(url) {
		this.url = url;
	} // eo setUrl
	// }}}
	// {{{
	/**
	 * Starts progress fetching from server
	 * @private
	 */
	,startProgress:function() {
		if(!this.progressTask) {
			this.progressTask = new Ext.util.DelayedTask(this.requestProgress, this);
		}
		this.progressTask.delay.defer(this.progressInterval / 2, this.progressTask, [this.progressInterval]);
	} // eo function startProgress
	// }}}
	// {{{
	/**
	 * Stops progress fetching from server
	 * @private
	 */
	,stopProgress:function() {
		if(this.progressTask) {
			this.progressTask.cancel();
		}
	} // eo function stopProgress
	// }}}
	// {{{
	/**
	 * Stops all currently running uploads
	 */
	,stopAll:function() {
		var records = this.store.query('state', 'uploading');
		records.each(this.stopUpload, this);
	} // eo function stopAll
	// }}}
	// {{{
	/**
	 * Stops currently running upload
	 * @param {Ext.data.Record} record Optional, if not set singleUpload = true is assumed
	 * and the global stop is initiated
	 */
	,stopUpload:function(record) {
		// single abord
		var iframe = false;
		if(record) {
			iframe = this.getIframe(record);
			this.stopIframe(iframe);
			this.upCount--;
			this.upCount = 0 > this.upCount ? 0 : this.upCount;
			record.set('state', 'stopped');
			this.fireFinishEvents({record:record});
		}
		// all abort
		else if(this.form) {
			iframe = Ext.fly(this.form.dom.target);
			this.stopIframe(iframe);
			this.upCount = 0;
			this.fireFinishEvents();
		}

	} // eo function abortUpload
	// }}}
	// {{{
	/**
	 * Stops uploading in hidden iframe
	 * @private
	 * @param {Ext.Element} iframe
	 */
	,stopIframe:function(iframe) {
		if(iframe) {
			try {
				iframe.dom.contentWindow.stop();
				iframe.remove.defer(250, iframe);
			}
			catch(e){}
		}
	} // eo function stopIframe
	// }}}
	// {{{
	/**
	 * Main public interface function. Preforms the upload
	 */
	,upload:function() {
		
		var records = this.store.queryBy(function(r){return 'done' !== r.get('state');});
		if(!records.getCount()) {
			return;
		}

		// fire beforeallstart event
		if(true !== this.eventsSuspended && false === this.fireEvent('beforeallstart', this)) {
			return;
		}
		if(this.singleUpload) {
			this.uploadSingle();
		}
		else {
			records.each(this.uploadFile, this);
		}
		
		if(true === this.enableProgress) {
			this.startProgress();
		}

	} // eo function upload
	// }}}
	// {{{
	/**
	 * called for both success and failure. Does nearly nothing
	 * @private
	 * but dispatches processing to processSuccess and processFailure functions
	 */
	,uploadCallback:function(options, success, response) {

		var o;
		this.upCount--;
		this.form = false;

		// process ajax success
		if(true === success) {
			try {
				o = Ext.decode(response.responseText);
			}
			catch(e) {
				this.processFailure(options, response, this.jsonErrorText);
				this.fireFinishEvents(options);
				return;
			}
			// process command success
			if(true === o.success) {
				this.processSuccess(options, response, o);
			}
			// process command failure
			else {
				this.processFailure(options, response, o);
			}
		}
		// process ajax failure
		else {
			this.processFailure(options, response);
		}

		this.fireFinishEvents(options);

	} // eo function uploadCallback
	// }}}
	// {{{
	/**
	 * Uploads one file
	 * @param {Ext.data.Record} record
	 * @param {Object} params Optional. Additional params to use in request.
	 */
	,uploadFile:function(record, params) {
		// fire beforestart event
		if(true !== this.eventsSuspended && false === this.fireEvent('beforefilestart', this, record)) {
			return;
		}

		// create form for upload
		var form = this.createForm(record);

		// append input to the form
		var inp = record.get('input');
		inp.set({name:inp.id});
		form.appendChild(inp);

		// get params for request
		var o = this.getOptions(record, params);
		o.form = form;

		// set state 
		record.set('state', 'uploading');
		record.set('pctComplete', 0);

		// increment active uploads count
		this.upCount++;

		// request upload
		Ext.Ajax.request(o);

		// todo:delete after devel
		this.getIframe.defer(100, this, [record]);

	} // eo function uploadFile
	// }}}
	// {{{
	/**
	 * Uploads all files in single request
	 */
	,uploadSingle:function() {

		// get records to upload
		var records = this.store.queryBy(function(r){return 'done' !== r.get('state');});
		if(!records.getCount()) {
			return;
		}

		// create form and append inputs to it
		var form = this.createForm();
		records.each(function(record) {
			var inp = record.get('input');
			inp.set({name:inp.id});
			form.appendChild(inp);
			record.set('state', 'uploading');
		}, this);

		// create options for request
		var o = this.getOptions();
		o.form = form;

		// save form for stop
		this.form = form;

		// increment active uploads counter
		this.upCount++;

		// request upload
		Ext.Ajax.request(o);
	
	} // eo function uploadSingle
	// }}}

}); // eo extend

// register xtype
Ext.reg('fileuploader', Ext.ux.FileUploader);

 // eof
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.form.UploadPanel
 *
 * @author  Ing. Jozef Sakáloš
 * @version $Id: Ext.ux.UploadPanel.js 94 2008-03-24 01:04:27Z jozo $
 * @date    13. March 2008
 *
 * @license Ext.ux.form.UploadPanel is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext */

/**
 * @class Ext.ux.UploadPanel
 * @extends Ext.Panel
 */
Ext.ux.UploadPanel = Ext.extend(Ext.Panel, {

	// configuration options overridable from outside
	// {{{
	/**
	 * @cfg {String} addIconCls icon class for add (file browse) button
	 */
	 addIconCls:'icon-plus'

	/**
	 * @cfg {String} addText Text on Add button
	 */
	,addText:'Add'

	/**
	 * @cfg {Object} baseParams This object is not used directly by FileTreePanel but it is
	 * propagated to lower level objects instead. Included here for convenience.
	 */

	/**
	 * @cfg {String} bodyStyle style to use for panel body
	 */
	,bodyStyle:'padding:2px'

	/**
	 * @cfg {String} buttonsAt Where buttons are placed. Valid values are tbar, bbar, body (defaults to 'tbar')
	 */
	,buttonsAt:'tbar'

	/**
	 * @cfg {String} clickRemoveText
	 */
	,clickRemoveText:'Click to remove'

	/**
	 * @cfg {String} clickStopText
	 */
	,clickStopText:'Click to stop'

	/**
	 * @cfg {String} emptyText empty text for dataview
	 */
	,emptyText:'No files'

	/**
	 * @cfg {Boolean} enableProgress true to enable querying server for progress information
	 * Passed to underlying uploader. Included here for convenience.
	 */
	,enableProgress:true
	
	// CUSTOM TWEAK: ProgressUrl wasnt passed originaly, so default was used, and there was no way of changing it
	/**
	 * @cfg {String} progressUrl url for querying server for progress information
	 * Passed to underlying uploader. Included here for convenience.
	 */
	,progressUrl:'progress.php'

	/**
	 * @cfg {String} errorText
	 */
	,errorText:'Error'

	/**
	 * @cfg {String} fileCls class prefix to use for file type classes
	 */
	,fileCls:'file'

	/**
	 * @cfg {String} fileQueuedText File upload status text
	 */
	,fileQueuedText:'File <b>{0}</b> is queued for upload'

	/**
	 * @cfg {String} fileDoneText File upload status text
	 */
	,fileDoneText:'File <b>{0}</b> has been successfully uploaded'

	/**
	 * @cfg {String} fileFailedText File upload status text
	 */
	,fileFailedText:'File <b>{0}</b> failed to upload'

	/**
	 * @cfg {String} fileStoppedText File upload status text
	 */
	,fileStoppedText:'File <b>{0}</b> stopped by user'

	/**
	 * @cfg {String} fileUploadingText File upload status text
	 */
	,fileUploadingText:'Uploading file <b>{0}</b>'

	/**
	 * @cfg {Number} maxFileSize Maximum upload file size in bytes
	 * This config property is propagated down to uploader for convenience
	 */
	,maxFileSize:524288

	/**
	 * @cfg {Number} Maximum file name length for short file names
	 */
	,maxLength:18

	/**
	 * @cfg {String} removeAllIconCls iconClass to use for Remove All button (defaults to 'icon-cross'
	 */
	,removeAllIconCls:'icon-cross'

	/**
	 * @cfg {String} removeAllText text to use for Remove All button tooltip
	 */
	,removeAllText:'Remove All'

	/**
	 * @cfg {String} removeIconCls icon class to use for remove file icon
	 */
	,removeIconCls:'icon-minus'

	/**
	 * @cfg {String} removeText Remove text
	 */
	,removeText:'Remove'

	/**
	 * @cfg {String} selectedClass class for selected item of DataView
	 */
	,selectedClass:'ux-up-item-selected'

	/**
	 * @cfg {Boolean} singleUpload true to upload files in one form, false to upload one by one
	 * This config property is propagated down to uploader for convenience
	 */
	,singleUpload:false

	/**
	 * @cfg {String} stopAllText
	 */
	,stopAllText:'Stop All'

	/**
	 * @cfg {String} stopIconCls icon class to use for stop
	 */
	,stopIconCls:'icon-stop'

	/**
	 * @cfg {String/Ext.XTemplate} tpl Template for DataView.
	 */

	/**
	 * @cfg {String} uploadText Upload text
	 */
	,uploadText:'Upload'

	/**
	 * @cfg {String} uploadText Upload text
	 */
	,uploadInText:'Upload in: '

	
	/**
	 * @cfg {String} uploadIconCls icon class to use for upload button
	 */
	,uploadIconCls:'icon-upload'

	/**
	 * @cfg {String} workingIconCls iconClass to use for busy indicator
	 */
	,workingIconCls:'icon-working'

	// }}}

	// overrides
	// {{{
	,initComponent:function() {

		// {{{
		// create buttons
		// add (file browse button) configuration
		var addCfg = {
			 xtype:'browsebutton'
			,text:this.addText + '...'
			,iconCls:this.addIconCls
			,scope:this
			,handler:this.onAddFile
		};

		// upload button configuration
		var upCfg = {
			 xtype:'button'
			,iconCls:this.uploadIconCls
			,text:this.uploadText
			,scope:this
			,handler:this.onUpload
			,disabled:true
		};

		// remove all button configuration
		var removeAllCfg = {
			 xtype:'button'
			,iconCls:this.removeAllIconCls
			,tooltip:this.removeAllText
			,scope:this
			,handler:this.onRemoveAllClick
			,disabled:true
		};

		// todo: either to cancel buttons in body or implement it
		if('body' !== this.buttonsAt) {
			this[this.buttonsAt] = [addCfg, upCfg, '->', removeAllCfg];
		}
		// }}}
		// {{{
		// create store
		// fields for record
		var fields = [
			 {name:'id', type:'text', system:true}
			,{name:'shortName', type:'text', system:true}
			,{name:'fileName', type:'text', system:true}
			,{name:'filePath', type:'text', system:true}
			,{name:'fileCls', type:'text', system:true}
			,{name:'input', system:true}
			,{name:'form', system:true}
			,{name:'state', type:'text', system:true}
			,{name:'error', type:'text', system:true}
			,{name:'progressId', type:'int', system:true}
			,{name:'bytesTotal', type:'int', system:true}
			,{name:'bytesUploaded', type:'int', system:true}
			,{name:'estSec', type:'int', system:true}
			,{name:'filesUploaded', type:'int', system:true}
			,{name:'speedAverage', type:'int', system:true}
			,{name:'speedLast', type:'int', system:true}
			,{name:'timeLast', type:'int', system:true}
			,{name:'timeStart', type:'int', system:true}
			,{name:'pctComplete', type:'int', system:true}
		];

		// add custom fields if passed
		if(Ext.isArray(this.customFields)) {
			fields.push(this.customFields);
		}

		// create store
		this.store = new Ext.data.SimpleStore({
			 id:0
			,fields:fields
			,data:[]
		});
		// }}}
		// {{{
		// create view
		Ext.apply(this, {
			items:[{
				 xtype:'dataview'
				,itemSelector:'div.ux-up-item'
				,store:this.store
				,selectedClass:this.selectedClass
				,singleSelect:true
				,emptyText:this.emptyText
				,tpl: this.tpl || this.showFoders ? new Ext.XTemplate(
					  '<tpl for=".">'
					+ '<div class="ux-up-item" style="height:34px">'
//					+ '<div class="ux-up-indicator">&#160;</div>'
					+ '<div class="ux-up-icon-file {fileCls}">&#160;</div>'
					+ '<div class="ux-up-text x-unselectable" qtip="{fileName}">{shortName}</div>'
					+ '<div id="remove-{[values.input.id]}" class="ux-up-icon-state ux-up-icon-{state}"'
					+ 'qtip="{[this.scope.getQtip(values)]}">&#160;</div>'
					// CUSTOM TWEAK: NOTE (Goran): added this line, because I couldnt get qtips from prev row working
					+ '<br/><div class="ux-up-text x-unselectable" qtip="{folderPath}"'
					+ 'style="width:100%;background-color:#f0f0f0;background-image:url(/js/ext-ensions/filetree/img/white_bg.png);background-repeat:no-repeat">'
					+ this.uploadInText + '{shortFolderPath}</div><br/>'
					+ '</div>'
					+ '</tpl>'
					, {scope:this}) : new Ext.XTemplate(
					  '<tpl for=".">'
					+ '<div class="ux-up-item">'
//					+ '<div class="ux-up-indicator">&#160;</div>'
					+ '<div class="ux-up-icon-file {fileCls}">&#160;</div>'
					+ '<div class="ux-up-text x-unselectable" qtip="{fileName}">{shortName}</div>'
					+ '<div id="remove-{[values.input.id]}" class="ux-up-icon-state ux-up-icon-{state}"'
					+ 'qtip="{[this.scope.getQtip(values)]}">&#160;</div>'
					+ '</div>'
					+ '</tpl>'
					, {scope:this}
				)
				,listeners:{click:{scope:this, fn:this.onViewClick}}

			}]
		});
		// }}}

		// call parent
		Ext.ux.UploadPanel.superclass.initComponent.apply(this, arguments);

		// save useful references
		this.view = this.items.itemAt(0);

		// {{{
		// add events
		this.addEvents(
			/**
			 * Fires before the file is added to store. Return false to cancel the add
			 * @event beforefileadd
			 * @param {Ext.ux.UploadPanel} this
			 * @param {Ext.Element} input (type=file) being added
			 */
			'beforefileadd'
			/**
			 * Fires after the file is added to the store
			 * @event fileadd
			 * @param {Ext.ux.UploadPanel} this
			 * @param {Ext.data.Store} store
			 * @param {Ext.data.Record} Record (containing the input) that has been added to the store
			 */
			,'fileadd'
			/**
			 * Fires before the file is removed from the store. Return false to cancel the remove
			 * @event beforefileremove
			 * @param {Ext.ux.UploadPanel} this
			 * @param {Ext.data.Store} store
			 * @param {Ext.data.Record} Record (containing the input) that is being removed from the store
			 */
			,'beforefileremove'
			/**
			 * Fires after the record (file) has been removed from the store
			 * @event fileremove
			 * @param {Ext.ux.UploadPanel} this
			 * @param {Ext.data.Store} store
			 */
			,'fileremove'
			/**
			 * Fires before all files are removed from the store (queue). Return false to cancel the clear.
			 * Events for individual files being removed are suspended while clearing the queue.
			 * @event beforequeueclear
			 * @param {Ext.ux.UploadPanel} this
			 * @param {Ext.data.Store} store
			 */
			,'beforequeueclear'
			/**
			 * Fires after the store (queue) has been cleared
			 * Events for individual files being removed are suspended while clearing the queue.
			 * @event queueclear
			 * @param {Ext.ux.UploadPanel} this
			 * @param {Ext.data.Store} store
			 */
			,'queueclear'
			/**
			 * Fires after the upload button is clicked but before any upload is started
			 * Return false to cancel the event
			 * @param {Ext.ux.UploadPanel} this
			 */
			,'beforeupload'
		);
		// }}}
		// {{{
		// relay view events
		this.relayEvents(this.view, [
			 'beforeclick'
			,'beforeselect'
			,'click'
			,'containerclick'
			,'contextmenu'
			,'dblclick'
			,'selectionchange'
		]);
		// }}}

		// create uploader
		var config = {
			 store:this.store
			,singleUpload:this.singleUpload
			,maxFileSize:this.maxFileSize
			,enableProgress:this.enableProgress
			// CUSTOM TWEAK: ProgressUrl wasnt passed originaly, so default was used, and there was no way of changing it
			,progressUrl:this.progressUrl
			,url:this.url
			,path:this.path
		};
		if(this.baseParams) {
			config.baseParams = this.baseParams;
		}
		this.uploader = new Ext.ux.FileUploader(config);

		// relay uploader events
		this.relayEvents(this.uploader, [
			 'beforeallstart'
			,'allfinished'
			,'progress'
		]);

		// install event handlers
		this.on({
			 beforeallstart:{scope:this, fn:function() {
			 	this.uploading = true;
				this.updateButtons();
			}}
			,allfinished:{scope:this, fn:function() {
				this.uploading = false;
				this.updateButtons();
			}}
			,progress:{fn:this.onProgress.createDelegate(this)}
		});
	} // eo function initComponent
	// }}}
	// {{{
	/**
	 * onRender override, saves references to buttons
	 * @private
	 */
	,onRender:function() {
		// call parent
		Ext.ux.UploadPanel.superclass.onRender.apply(this, arguments);

		// save useful references
		var tb = 'tbar' === this.buttonsAt ? this.getTopToolbar() : this.getBottomToolbar();
		this.addBtn = Ext.getCmp(tb.items.first().id);
		this.uploadBtn = Ext.getCmp(tb.items.itemAt(1).id);
		this.removeAllBtn = Ext.getCmp(tb.items.last().id);
	} // eo function onRender
	// }}}

	// added methods
	// {{{
	/**
	 * called by XTemplate to get qtip depending on state
	 * @private
	 * @param {Object} values XTemplate values
	 */
	,getQtip:function(values) {
		var qtip = '';
		switch(values.state) {
			case 'queued':
				qtip = String.format(this.fileQueuedText, values.fileName);
				qtip += '<br>' + this.clickRemoveText;
			break;

			case 'uploading':
				qtip = String.format(this.fileUploadingText, values.fileName);
				qtip += '<br>' + values.pctComplete + '% done';
				qtip += '<br>' + this.clickStopText;
			break;

			case 'done':
				qtip = String.format(this.fileDoneText, values.fileName);
				qtip += '<br>' + this.clickRemoveText;
			break;

			case 'failed':
				qtip = String.format(this.fileFailedText, values.fileName);
				qtip += '<br>' + this.errorText + ':' + values.error;
				qtip += '<br>' + this.clickRemoveText;
			break;

			case 'stopped':
				qtip = String.format(this.fileStoppedText, values.fileName);
				qtip += '<br>' + this.clickRemoveText;
			break;
		}
		return qtip;
	} // eo function getQtip
	// }}}
	// {{{
	/**
	 * get file name
	 * @private
	 * @param {Ext.Element} inp Input element containing the full file path
	 * @return {String}
	 */
	,getFileName:function(inp) {
		return inp.getValue().split(/[\/\\]/).pop();
	} // eo function getFileName
	// }}}
	// {{{
	/**
	 * get file path (excluding the file name)
	 * @private
	 * @param {Ext.Element} inp Input element containing the full file path
	 * @return {String}
	 */
	,getFilePath:function(inp) {
		return inp.getValue().replace(/[^\/\\]+$/,'');
	} // eo function getFilePath
	// }}}
	// {{{
	/**
	 * returns file class based on name extension
	 * @private
	 * @param {String} name File name to get class of
	 * @return {String} class to use for file type icon
	 */
	,getFileCls: function(name) {
		var atmp = name.split('.');
		if(1 === atmp.length) {
			return this.fileCls;
		}
		else {
			return this.fileCls + '-' + atmp.pop().toLowerCase();
		}
	}
	// }}}
	// {{{
	/**
	 * called when file is added - adds file to store
	 * @private
	 * @param {Ext.ux.BrowseButton}
	 */
	,onAddFile:function(bb) {
		if(true !== this.eventsSuspended && false === this.fireEvent('beforefileadd', this, bb.getInputFile())) {
			return;
		}
		var inp = bb.detachInputFile();
		inp.addClass('x-hidden');
		var fileName = this.getFileName(inp);

		// create new record and add it to store
		var rec = new this.store.recordType({
			 input:inp
			,fileName:fileName
			,filePath:this.getFilePath(inp)
			,shortName: Ext.util.Format.ellipsis(fileName, this.maxLength)
			,fileCls:this.getFileCls(fileName)
			,state:'queued'
		}, inp.id);
		rec.commit();
		this.store.add(rec);

		this.syncShadow();

		this.uploadBtn.enable();
		this.removeAllBtn.enable();

		if(true !== this.eventsSuspended) {
			this.fireEvent('fileadd', this, this.store, rec);
		}

	} // eo onAddFile
	// }}}
	// {{{
	/**
	 * destroys child components
	 * @private
	 */
	,onDestroy:function() {

		// destroy uploader
		if(this.uploader) {
			this.uploader.stopAll();
			this.uploader.purgeListeners();
			this.uploader = null;
		}

		// destroy view
		if(this.view) {
			this.view.purgeListeners();
			this.view.destroy();
			this.view = null;
		}

		// destroy store
		if(this.store) {
			this.store.purgeListeners();
			this.store.destroy();
			this.store = null;
		}

	} // eo function onDestroy
	// }}}
	// {{{
	/**
	 * progress event handler
	 * @private
	 * @param {Ext.ux.FileUploader} uploader
	 * @param {Object} data progress data
	 * @param {Ext.data.Record} record
	 */
	,onProgress:function(uploader, data, record) {
		var bytesTotal, bytesUploaded, pctComplete, state, idx, item, width, pgWidth;
		if(record) {
			state = record.get('state');
			bytesTotal = record.get('bytesTotal') || 1;
			bytesUploaded = record.get('bytesUploaded') || 0;
			if('uploading' === state) {
				pctComplete = Math.round(1000 * bytesUploaded/bytesTotal) / 10;
			}
			else if('done' === 'state') {
				pctComplete = 100;
			}
			else {
				pctComplete = 0;
			}
			record.set('pctComplete', pctComplete);

			idx = this.store.indexOf(record);
			item = Ext.get(this.view.getNode(idx));
			if(item) {
				width = item.getWidth();
				item.applyStyles({'background-position':width * pctComplete / 100 + 'px'});
			}
		}
	} // eo function onProgress
	// }}}
	// {{{
	/**
	 * called when file remove icon is clicked - performs the remove
	 * @private
	 * @param {Ext.data.Record}
	 */
	,onRemoveFile:function(record) {
		if(true !== this.eventsSuspended && false === this.fireEvent('beforefileremove', this, this.store, record)) {
			return;
		}

		// remove DOM elements
		var inp = record.get('input');
		// CUSTOM TWEAK: NOTE (Goran): commented lines regarding wrap because of IE problem, see
		//http://extjs.com/forum/showthread.php?p=149078#post149078 for details
		//var wrap = inp.up('em');
		inp.remove();
		/*if(wrap) {
			wrap.remove();
		}*/

		// remove record from store
		this.store.remove(record);

		var count = this.store.getCount();
		this.uploadBtn.setDisabled(!count);
		this.removeAllBtn.setDisabled(!count);

		if(true !== this.eventsSuspended) {
			this.fireEvent('fileremove', this, this.store);
			this.syncShadow();
		}
	} // eo function onRemoveFile
	// }}}
	// {{{
	/**
	 * Remove All/Stop All button click handler
	 * @private
	 */
	,onRemoveAllClick:function(btn) {
		if(true === this.uploading) {
			this.stopAll();
		}
		else {
			this.removeAll();
		}
	} // eo function onRemoveAllClick

	,stopAll:function() {
		this.uploader.stopAll();
	} // eo function stopAll
	// }}}
	// {{{
	/**
	 * DataView click handler
	 * @private
	 */
	,onViewClick:function(view, index, node, e) {
		var t = e.getTarget('div:any(.ux-up-icon-queued|.ux-up-icon-failed|.ux-up-icon-done|.ux-up-icon-stopped)');
		if(t) {
			this.onRemoveFile(this.store.getAt(index));
		}
		t = e.getTarget('div.ux-up-icon-uploading');
		if(t) {
			this.uploader.stopUpload(this.store.getAt(index));
		}
	} // eo function onViewClick
	// }}}
	// {{{
	/**
	 * tells uploader to upload
	 * @private
	 */
	,onUpload:function() {
		if(true !== this.eventsSuspended && false === this.fireEvent('beforeupload', this)) {
			return false;
		}
		this.uploader.upload();
	} // eo function onUpload
	// }}}
	// {{{
	/**
	 * url setter
	 */
	,setUrl:function(url) {
		this.url = url;
		this.uploader.setUrl(url);
	} // eo function setUrl
	// }}}
	// {{{
	/**
	 * path setter
	 */
	,setPath:function(path) {
		this.uploader.setPath(path);
	} // eo function setPath
	// }}}
	// {{{
	/**
	 * Updates buttons states depending on uploading state
	 * @private
	 */
	,updateButtons:function() {
		if(true === this.uploading) {
			this.addBtn.disable();
			this.uploadBtn.disable();
			this.removeAllBtn.setIconClass(this.stopIconCls);
			this.removeAllBtn.getEl().child(this.removeAllBtn.buttonSelector).dom[this.removeAllBtn.tooltipType] = this.stopAllText;
		}
		else {
			this.addBtn.enable();
			this.uploadBtn.enable();
			this.removeAllBtn.setIconClass(this.removeAllIconCls);
			this.removeAllBtn.getEl().child(this.removeAllBtn.buttonSelector).dom[this.removeAllBtn.tooltipType] = this.removeAllText;
		}
	} // eo function updateButtons
	// }}}
	// {{{
	/**
	 * Removes all files from store and destroys file inputs
	 */
	,removeAll:function() {
		var suspendState = this.eventsSuspended;
		if(false !== this.eventsSuspended && false === this.fireEvent('beforequeueclear', this, this.store)) {
			return false;
		}
		this.suspendEvents();

		this.store.each(this.onRemoveFile, this);

		this.eventsSuspended = suspendState;
		if(true !== this.eventsSuspended) {
			this.fireEvent('queueclear', this, this.store);
		}
		this.syncShadow();
	} // eo function removeAll
	// }}}
	// {{{
	/**
	 * synchronize context menu shadow if we're in contextmenu
	 * @private
	 */
	,syncShadow:function() {
		if(this.contextmenu && this.contextmenu.shadow) {
			this.contextmenu.getEl().shadow.show(this.contextmenu.getEl());
		}
	} // eo function syncShadow
	// }}}

}); // eo extend

// register xtype
Ext.reg('uploadpanel', Ext.ux.UploadPanel);

// eof
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.FileTreeMenu
 *
 * @author  Ing. Jozef Sakáloš
 * @version $Id: Ext.ux.FileTreeMenu.js 112 2008-03-28 21:11:17Z jozo $
 * @date    13. March 2008
 *
 * @license Ext.ux.FileField is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext */

/**
 * @class Ext.ux.FileTreeMenu
 * @extends Ext.menu.Menu
 * @constructor
 * Creates new FileTreeMenu object
 * @param {Object} config A configuration object
 */
Ext.ux.FileTreeMenu = function(config) {
	config = config || {};

	var uploadPanelConfig = {
		 contextmenu:this
		,buttonsAt:config.buttonsAt || 'tbar'
		,singleUpload:config.singleUpload || false
		,maxFileSize:config.maxFileSize
		,enableProgress:config.enableProgress
		// CUSTOM TWEAK: ProgressUrl wasnt passed originaly, so default was used, and there was no way of changing it
		,progressUrl:config.progressUrl
	};
	
	// CUSTOM TWEAK: Translation text wasnt passed originaly
	var translationParams = {
		confirmText: config.confirmText
		,deleteText: config.deleteText
		,errorText: config.errorText
		,existsText: config.existsText
		,fileText: config.fileText
		,loadingText: config.loadingText
		,newdirText: config.newdirText
		,overwriteText: config.overwriteText
		,reallyWantText: config.reallyWantText
		,collapseText: config.collapseText
		,expandText: config.expandText
		,renameText: config.renameText
		,uploadFileText: config.addText
		,addText: config.addText
		,clickRemoveText: config.clickRemoveText
		,clickStopText: config.clickStopText
		,errorText: config.errorText
		,fileQueuedText: config.fileQueuedText
		,fileDoneText: config.fileDoneText
		,fileFailedText: config.fileFailedText
		,fileStoppedText: config.fileStoppedText
		,fileUploadingText: config.fileUploadingText
		,removeAllText: config.removeAllText
		,removeText: config.removeText
		,stopAllText: config.stopAllText
		,uploadText: config.uploadText
	};
	Ext.apply(uploadPanelConfig, translationParams);
	Ext.apply(this, translationParams);
	
	if(config.baseParams) {
		config.baseParams.cmd = config.baseParams.cmd || 'upload';
		config.baseParams.dir = config.baseParams.dir || '.';
		uploadPanelConfig.baseParams = config.baseParams;
	}

	// {{{
	Ext.apply(config, {
		items:[{
			 text:'&#160'
			,cls:'ux-ftm-nodename'
			,disabledClass:''
			,disabled:true
			,cmd:'nodename'
		},{
			 text:this.openText// + ' (Enter)'// CUSTOM TWEAK: NOTE (Goran): This was originaly not commeted, but we dont need this. Same remark is for all other item shortcuts
			,iconCls:this.openIconCls
			,cmd:'open'
			,menu:{
				items:[{
					 text:this.openSelfText
					,iconCls:this.openSelfIconCls
					,cmd:'open-self'
				},{
					 text:this.openPopupText
					,iconCls:this.openPopupIconCls
					,cmd:'open-popup'
				},{
					 text:this.openBlankText
					,iconCls:this.openBlankIconCls
					,cmd:'open-blank'
				},{
					 text:this.openDwnldText
					,iconCls:this.openDwnldIconCls
					,cmd:'open-dwnld'
				}]
			}
		}
		,new Ext.menu.Separator({cmd:'sep-open'})
		,{
			 text:this.reloadText// + ' (Ctrl+E)'
			,iconCls:this.reloadIconCls
			,cmd:'reload'
		},{
			 text:this.expandText// + ' (Ctrl+&nbsp;&rarr;)'
			,iconCls:this.expandIconCls
			,cmd:'expand'
		},{
			 text:this.collapseText// + ' (Ctrl+&nbsp;&larr;)'
			,iconCls:this.collapseIconCls
			,cmd:'collapse'
		}
		,new Ext.menu.Separator({cmd:'sep-collapse'})
		,{
			 text:this.renameText// + ' (F2)'
			,iconCls:this.renameIconCls
			,cmd:'rename'
		},{
			 text:this.deleteText// + ' (' + this.deleteKeyName + ')'
			,iconCls:this.deleteIconCls
			,cmd:'delete'
		},{
			 text:this.newdirText// + '... (Ctrl+N)'
			,iconCls:this.newdirIconCls
			,cmd:'newdir'
		}
		,new Ext.menu.Separator({cmd:'sep-upload'})
		,{
			 text:this.uploadFileText// + ' (Ctrl+U)'
			,iconCls:this.uploadIconCls
			,hideOnClick:false
			,cmd:'upload'
		}
			,new Ext.menu.Adapter(new Ext.ux.UploadPanel(uploadPanelConfig), {
				 hideOnClick:false
				,cmd:'upload-panel'
			})
		]
	}); // eo apply
// }}}

	// call parent
	Ext.ux.FileTreeMenu.superclass.constructor.call(this, config);

	// relay event from submenu
	this.relayEvents(this.getItemByCmd('open').menu, ['click', 'itemclick']);

}; // eo constructor

Ext.extend(Ext.ux.FileTreeMenu, Ext.menu.Menu, {
	// configuration options overridable from outside
	/**
	 * @cfg {String} collapseIconCls icon class for collapse all item
	 */
	 collapseIconCls:'icon-collapse-all'

	 /**
	  * @cfg {String} collapseText text for collapse all item
	  */
	,collapseText: 'Collapse all'

	/**
	 * @cfg {String} deleteIconCls icon class for delete item
	 */
	,deleteIconCls:'icon-cross'

	/**
	 * @cfg {String} deleteKeyName text for delete item shortcut
	 */
	,deleteKeyName:'Delete Key'

	/**
	 * @cfg {String} deleteText text for delete item
	 */
	,deleteText:'Delete'

	/**
	 * @cfg {String} expandIconCls icon class for expand all item
	 */
	,expandIconCls:'icon-expand-all'

	/**
	 * @cfg {String} expandText text for expand all item
	 */
	,expandText: 'Expand all'

	/**
	 * @cfg {String} newdirIconCls icon class for new directory item
	 */
	,newdirIconCls:'icon-folder-add'

	/**
	 * @cfg {String} newdirText text for new directory item
	 */
	,newdirText:'New folder'

	/**
	 * @cfg {String} openBlankIconCls icon class for open in new window item
	 */
	,openBlankIconCls:'icon-open-blank'

	/**
	 * @cfg {String} openBlankText text for open in new window item
	 */
	,openBlankText:'Open in new window'

	/**
	 * @cfg {String} openDwnldIconCls icon class for download item
	 */
	,openDwnldIconCls:'icon-open-download'

	/**
	 * @cfg {String} openDwnldText text for download item
	 */
	,openDwnldText:'Download'

	/**
	 * @cfg {String} openIconCls icon class for open submenu
	 */
	,openIconCls:'icon-open'

	/**
	 * @cfg {String} openPopupIconCls icon class for open in popup item
	 */
	,openPopupIconCls:'icon-open-popup'

	/**
	 * @cfg {String} text for open in poput item
	 */
	,openPopupText:'Open in popup'

	/**
	 * @cfg {String} openSelfIconCls icon class for open in this window item
	 */
	,openSelfIconCls:'icon-open-self'

	/**
	 * @cfg {String} openSelfText text for open in this window item
	 */
	,openSelfText:'Open in this window'

	/**
	 * @cfg {String} openText text for open submenu
	 */
	,openText:'Open'

	/**
	 * @cfg {String} reloadIconCls icon class for reload item
	 */
	,reloadIconCls:'icon-refresh'

	/**
	 * @cfg {String} reloadText text for reload item
	 */
	,reloadText:'R<span style="text-decoration:underline">e</span>load'

	/**
	 * @cfg {String} icon class for rename item
	 */
	,renameIconCls:'icon-pencil'

	/**
	 * @cfg {String} renameText text for rename item
	 */
	,renameText: 'Rename'

	/**
	 * @cfg {String} uploadFileText text for upload file item
	 */
	,uploadFileText:'<span style="text-decoration:underline">U</span>pload file'

	/**
	 * @cfg {String} uploadIconCls icon class for upload file item
	 */
	,uploadIconCls:'icon-upload'

	/**
	 * @cfg {String} uploadText text for word 'Upload'
	 */
	,uploadText:'Upload'

	/**
	 * @cfg {Number} width Width of the menu.
	 * Cannot be empty as we have upload panel inside.
	 */
	,width:190

	// {{{
	/**
	 * Returns menu item identified by cmd. Unique cmd is used to identify menu items.
	 * I cannot use ids as they are applied to underlying DOM elements that would prevent
	 * to have more than one menu on the page.
	 * @param {String} cmd
	 * Valid cmds are:
	 *		- nodename
	 *		- open
	 *		- open-self
	 *		- open-popup
	 *		- open-blank
	 *		- open-dwnld
	 *		- sep-open (for separator after open submenu)
	 *		- reload
	 *		- expand
	 *		- collapse
	 *		- sep-collapse (for separator after collapse item)
	 *		- rename
	 *		- delete
	 *		- newdir
	 *		- sep-upload (for separator before upload panel)
	 *		- upload (for upload file item that does nothing)
	 *		- upload-panel (for upload panel)
	 * @return {Ext.menu.Item} menu item
	 */
	,getItemByCmd:function(cmd) {
		var open;
		var item = this.items.find(function(i) {
			return cmd === i.cmd;
		});
		if(!item) {
			open = this.items.find(function(i) {
				return 'open' === i.cmd;
			});
			if(!open) {
				return null;
			}
			item = open.menu.items.find(function(i) {
				return cmd === i.cmd;
			});
		}
		return item;
	} // eo function getItemByCmd
	// }}}
	// {{{
	/**
	 * Sets/Unsets item identified by cmd to disabled/enabled state
	 * @param {String} cmd Item indentifier, see getItemByCmd for explanation
	 * @param {Boolean} disabled true to disable the item
	 */
	,setItemDisabled:function(cmd, disabled) {
		var item = this.getItemByCmd(cmd);
		if(item) {
			item.setDisabled(disabled);
		}
	} // eo function setItemDisabled
	// }}}
	// {{{
	/**
	 * destroys uploadPanel if we have one
	 * @private
	 */
	,beforeDestroy:function() {
		var uploadPanel = this.getItemByCmd('upload-panel');
		if(uploadPanel && uploadPanel.component) {
			uploadPanel.component.purgeListeners();
			uploadPanel.component.destroy();
			uploadPanel.component = null;
		}
	} // eo function beforeDestroy
	// }}}

}); // eo extend

// register xtype
Ext.reg('filetreemenu', Ext.ux.FileTreeMenu);

// eof
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.FileTreePanel
 *
 * @author  Ing. Jozef Sakáloš
 * @version $Id: Ext.ux.FileTreePanel.js 112 2008-03-28 21:11:17Z jozo $
 * @date    13. March 2008
 *
 * @license Ext.ux.FileTreePanel is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext, window, document, setTimeout */

/**
 * @class Ext.ux.FileTreePanel
 * @extends Ext.tree.TreePanel
 */

Ext.ux.FileTreePanel = Ext.extend(Ext.tree.TreePanel, {
	// config variables overridable from outside
	// {{{
	/**
	 * @cfg {Object} baseParams This object is not used directly by FileTreePanel but it is
	 * propagated to lower level objects instead. Included here for convenience.
	 */

	/**
	 * @cfg {String} confirmText Text to display as title of confirmation message box
	 */
	 confirmText:'Confirm'

	/**
	 * @cfg {Boolean} containerScroll true to register
	 * this container with ScrollManager (defaults to true)
	 */
	,containerScroll:true

	/**
	 * @cfg {String} deleteText Delete text (for message box title or other displayed texts)
	 */
	,deleteText:'Delete'

	/**
	 * @cfg {String} deleteUrl URL to use when deleting; this.url is used if not set (defaults to undefined)
	 */

	/**
	 * @cfg {String} downloadUrl URL to use when downloading; this.url is used if not set (defaults to undefined)
	 */

	/**
	 * @cfg {Boolean} enableDD true to enable drag & drop of files and folders (defaults to true)
	 */
	,enableDD:true

	/**
	 * @cfg {Boolean) enableDelete true to enable to delete files and directories.
	 * If false context menu item is not shown (defaults to true)
	 */
	,enableDelete:true

	/**
	 * @cfg {Boolean) enableNewDir true to enable to create new directory.
	 * If false context menu item is not shown (defaults to true)
	 */
	,enableNewDir:true

	/**
	 * @cfg {Boolean) enableOpen true to enable open submenu
	 * If false context menu item is not shown (defaults to true)
	 */
	,enableOpen:true

	// CUSTOM TWEAK: NOTE (Goran): I added this config param originaly this was alaways shown
	/**
	 * @cfg {Boolean) enableReload true to enable reload submenu
	 * If false context menu item is not shown (defaults to true)
	 */
	,enableReload:true

	/**
	 * @cfg {Boolean} enableProgress true to enable querying server for progress information
	 * Passed to underlying uploader. Included here for convenience.
	 */
	,enableProgress:true
	
	// CUSTOM TWEAK: ProgressUrl wasnt passed originaly, so default was used, and there was no way of changing it
	/**
	 * @cfg {String} progressUrl url for querying server for progress information
	 * Passed to underlying uploader. Included here for convenience.
	 */
	,progressUrl:'progress.php'

	/**
	 * @cfg {Boolean) enableRename true to enable to rename files and directories.
	 * If false context menu item is not shown (defaults to true)
	 */
	,enableRename:true

	/**
	 * @cfg {Boolean} enableSort true to enable sorting of tree. See also folderSort (defaults to true)
	 */
	,enableSort:true

	/**
	 * @cfg {Boolean) enableUpload true to enable to upload files.
	 * If false context menu item is not shown (defaults to true)
	 */
	,enableUpload:true

	/**
	 * @cfg {String} errorText Text to display for an error
	 */
	,errorText:'Error'

	/**
	 * @cfg {String} existsText Text to display in message box if file exists
	 */
	,existsText:'File <b>{0}</b> already exists'

	/**
	 * @cfg {Boolean} true to expand root node on FileTreePanel render (defaults to true)
	 */
	,expandOnRender:true

	/**
	 * @cfg {String} fileCls class prefix to add to nodes. "-extension" is appended to
	 * this prefix to form filetype class, for example: file-odt, file-pdf. These classes
	 * are used to display correct filetype icons in the tree. css file and icons must
	 * exist of course.
	 */
	,fileCls:'file'

	/**
	 * @cfg {String} fileText
	 */
	,fileText:'File'

	/**
	 * @cfg {Boolean} focusPopup true to focus new browser popup window for 'popup' openMode
	 * (defaults to true)
	 */
	,focusPopup:true

	/**
	 * @cfg {Boolean} folderSort true to place directories at the top of the tree (defaults to true)
	 */
	,folderSort:true

	/**
	 * @cfg {String} hrefPrefix Text to prepend before file href for file open command.
	 * (defaults to '')
	 */
	,hrefPrefix:''

	/**
	 * @cfg {String} hrefSuffix Text to append to file href for file open command.
	 * (defaults to '')
	 */
	,hrefSuffix:''

	/**
	 * @cfg {String} layout Layout to use for this panel (defaults to 'fit')
	 */
	,layout:'fit'

	/**
	 * @cfg {String} loadingText Text to use for load mask msg
	 */
	,loadingText:'Loading'

	/**
	 * @cfg {Boolean} loadMask True to mask tree panel while loading
	 */
	,loadMask:false

	/**
	 * @cfg {Number} maxFileSize Maximum upload file size in bytes
	 * This config property is propagated down to uploader for convenience
	 */
	,maxFileSize:524288

	/**
	 * @cfg {Number} maxMsgLen Maximum message length for message box (defaults to 2000).
	 * If message is longer Ext.util.Format.ellipsis is used to truncate it and append ...
	 */
	,maxMsgLen:2000

	/**
	 * @cfg {String} method Method to use when posting to server. Other valid value is 'get'
	 * (defaults to 'post')
	 */
	,method:'post'

	/**
	 * @cfg {String} newdirText Default name for new directories (defaults to 'New Folder')
	 */
	,newdirText:'New Folder'

	/**
	 * @cfg {String} newdirUrl URL to use when creating new directory;
	 * this.url is used if not set (defaults to undefined)
	 */

	/**
	 * @cfg {String} openMode Default file open mode. This mode is used when user dblclicks
	 * a file. Other valid values are '_self', '_blank' and 'download' (defaults to 'popup')
	 */
	,openMode:'popup'

	/**
	 * @cfg {String} overwriteText Text to use in overwrite confirmation message box
	 */
	,overwriteText:'Do you want to overwrite it?'

	/**
	 * @cfg {String} popupFeatures Features for new browser window opened by popup open mode
	 */
	,popupFeatures:'width=800,height=600,dependent=1,scrollbars=1,resizable=1,toolbar=1'

	/**
	 * @cfg {Boolean} readOnly true to disable write operations. treeEditor and context menu
	 * are not created if true (defaults to false)
	 */
	,readOnly:false

	/**
	 * @cfg {String} reallyWantText Text to display for that question
	 */
	,reallyWantText:'Do you really want to'

	/**
	 * @cfg {String} renameUrl URL to use when renaming; this.url is used if not set (defaults to undefined)
	 */

	/**
	 * @cfg {String} rootPath Relative path pointing to the directory that is root of this tree (defaults to 'root')
	 */
	,rootPath:'root'

	/**
	 * @cfg {String} rootText Text to display for root node (defaults to 'Tree Root')
	 */
	,rootText:'Tree Root'

	/**
	 * @cfg {Boolean} selectOnEdit true to select the edited text on edit start (defaults to true)
	 */
	,selectOnEdit:true

	/**
	 * @cfg {Boolean} singleUpload true to upload files in one form, false to upload one by one
	 * This config property is propagated down to uploader for convenience
	 */
	,singleUpload:false

	/**
	 * @cfg {Boolean} topMenu true to create top toolbar with menu in addition to contextmenu
	 */
	,topMenu:false

	/**
	 * @cfg {String} url URL to use when communicating with server
	 */
	,url:'filetree.php'

	// CUSTOM TWEAK: NOTE (Goran): I added this config param tu be used when creating new node in newDir function

	/**
	 * @cfg {Object} newNodeConfig Config object to use when adding new folder. Default walue is original one
	 */
	,newDirConfig:{text:'New Folder', iconCls:'folder'}
	// }}}

	// overrides
	// {{{
	/**
	 * called by Ext when instantiating
	 * @private
	 * @param {Object} config Configuration object
	 */
	,initComponent:function() {

		// {{{
		Ext.apply(this, {

			// create root node
			 root:new Ext.tree.AsyncTreeNode({
				 text:this.rootText
				,path:this.rootPath
				,allowDrag:false
			})

			// create treeEditor
			,treeEditor:!this.readOnly ? new Ext.tree.TreeEditor(this, {
				 allowBlank:false
				,cancelOnEsc:true
				,completeOnEnter:true
				,ignoreNoChange:true
				,selectOnFocus:this.selectOnEdit
			}) : undefined

			// drop config
			,dropConfig:this.dropConfig ? this.dropConfig : {
				 ddGroup:this.ddGroup || 'TreeDD'
				,appendOnly:this.enableSort
				,expandDelay:3600000 // do not expand on drag over node
			}

			// create treeSorter
			,treeSorter:this.enableSort ? new Ext.tree.TreeSorter(this, {folderSort:this.folderSort}) : undefined

			// {{{
			,keys:[{
				// Enter = open
				 key:Ext.EventObject.ENTER, scope:this
				,fn:function(key, e) {
					var sm = this.getSelectionModel();
					var node = sm.getSelectedNode();
					if(node && 0 !== node.getDepth() && node.isLeaf()) {
						this.openNode(node);
					}
			}},{
				// F2 = edit
				 key:113, scope:this
				,fn:function(key, e) {
					var sm = this.getSelectionModel();
					var node = sm.getSelectedNode();
					if(node && 0 !== node.getDepth() && this.enableRename && this.readOnly !== true) {
						this.treeEditor.triggerEdit(node);
					}
			}},{
				// Delete Key = Delete
				 key:46, stopEvent:true, scope:this
				,fn:function(key, e) {
					var sm = this.getSelectionModel();
					var node = sm.getSelectedNode();
					if(node && 0 !== node.getDepth() && this.enableDelete && this.readOnly !== true) {
						this.deleteNode(node);
					}
			}},{
				// Ctrl + E = reload
				 key:69, ctrl:true, stopEvent:true, scope:this
				,fn:function(key, e) {
					var sm = this.getSelectionModel();
					var node = sm.getSelectedNode();
					if(node) {
						node = node.isLeaf() ? node.parentNode : node;
						sm.select(node);
						node.reload();
					}
			}},{
				// Ctrl + -> = expand deep
				 key:39, ctrl:true, stopEvent:true, scope:this
				,fn:function(key, e) {
					var sm = this.getSelectionModel();
					var node = sm.getSelectedNode();
					if(node && !node.isLeaf()) {
						sm.select(node);
						node.expand.defer(1, node, [true]);
					}
				}},{
				// Ctrl + <- = collapse deep
				 key:37, ctrl:true, scope:this, stopEvent:true
				,fn:function(key, e) {
					var sm = this.getSelectionModel();
					var node = sm.getSelectedNode();
					if(node && !node.isLeaf()) {
						sm.select(node);
						node.collapse.defer(1, node, [true]);
					}
				}},{
				// Ctrl + N = New Directory
				 key:78, ctrl:true, scope:this, stopEvent:true
				,fn:function(key, e) {
					var sm, node;
					sm = this.getSelectionModel();
					node = sm.getSelectedNode();
					if(node && this.enableNewDir && this.readOnly !== true) {
						node = node.isLeaf() ? node.parentNode : node;
						this.createNewDir(node);
					}
			}}]
			// }}}

		}); // eo apply
		// }}}
		// {{{
		// create loader
		if(!this.loader) {
			this.loader = new Ext.tree.TreeLoader({
				 url:this.url
				,baseParams:{cmd:'get'}
				,listeners:{
					beforeload:{scope:this, fn:function(loader, node) {
						loader.baseParams.path = this.getPath(node);
					}}
				}
			});
		}
		// }}}
		// {{{
		// install top menu if configured
		if(true === this.topMenu) {
			this.tbar = [{
				 text:this.fileText
				,disabled:true
				,scope:this
				,menu:this.getContextMenu()
			}];
		}
		// }}}

		// call parent
		Ext.ux.FileTreePanel.superclass.initComponent.apply(this, arguments);

		// {{{
		// install treeEditor event handlers
		if(this.treeEditor) {
			// do not enter edit mode on selected node click
			this.treeEditor.beforeNodeClick = function(node,e){return true;};

			// treeEditor event handlers
			this.treeEditor.on({
				 complete:{scope:this, fn:this.onEditComplete}
				,beforecomplete:{scope:this, fn:this.onBeforeEditComplete}
			});
		}
		// }}}
		// {{{
		// install event handlers
		this.on({
			 contextmenu:{scope:this, fn:this.onContextMenu, stopEvent:true}
			,dblclick:{scope:this, fn:this.onDblClick}
			,beforenodedrop:{scope:this, fn:this.onBeforeNodeDrop}
			,nodedrop:{scope:this, fn:this.onNodeDrop}
			,nodedragover:{scope:this, fn:this.onNodeDragOver}
		});

		// }}}
		// {{{
		// add events
		this.addEvents(
			/**
			 * @event beforeopen
			 * Fires before file open. Return false to cancel the event
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {String} fileName name of the file being opened
			 * @param {String} url url of the file being opened
			 * @param {String} mode open mode
			 */
			 'beforeopen'
			/**
			 * @event open
			 * Fires after file open has been initiated
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {String} fileName name of the file being opened
			 * @param {String} url url of the file being opened
			 * @param {String} mode open mode
			 */
			,'open'
			/**
			 * @event beforerename
			 * Fires after the user completes file name editing
			 * but before the file is renamed. Return false to cancel the event
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} node being renamed
			 * @param {String} newPath including file name
			 * @param {String} oldPath including file name
			 */
			,'beforerename'
			/**
			 * @event rename
			 * Fires after the file has been successfully renamed
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} node that has been renamed
			 * @param {String} newPath including file name
			 * @param {String} oldPath including file name
			 */
			,'rename'
			/**
			 * @event renamefailure
			 * Fires after a failure when renaming file
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} node rename of which failed
			 * @param {String} newPath including file name
			 * @param {String} oldPath including file name
			 */
			,'renamefailure'
			/**
			 * @event beforedelete
			 * Fires before a file or directory is deleted. Return false to cancel the event.
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} node being deleted
			 */
			,'beforedelete'
			/**
			 * @event delete
			 * Fires after a file or directory has been deleted
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {String} path including file name that has been deleted
			 */
			,'delete'
			/**
			 * @event deletefailure
			 * Fires if node delete failed
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} node delete of which failed
			 */
			,'deletefailure'
			/**
			 * @event beforenewdir
			 * Fires before new directory is created. Return false to cancel the event
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} node under which the new directory is being created
			 */
			,'beforenewdir'
			/**
			 * @event newdir
			 * Fires after the new directory has been successfully created
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {Ext.tree.AsyncTreeNode} new node/directory that has been created
			 */
			,'newdir'
			/**
			 * @event newdirfailure
			 * Fires if creation of new directory failed
			 * @param {Ext.ux.FileTreePanel} this
			 * @param {String} path creation of which failed
			 */
			,'newdirfailure'
		); // eo addEvents
		// }}}

	} // eo function initComponent
	// }}}
	// {{{
	/**
	 * onRender override - just expands root node if configured
	 * @private
	 */
	,onRender:function() {
		// call parent
		Ext.ux.FileTreePanel.superclass.onRender.apply(this, arguments);

		if(true === this.topMenu) {
			this.topMenu = Ext.getCmp(this.getTopToolbar().items.itemAt(0).id);
			this.getSelectionModel().on({
				 scope:this
				,selectionchange:function(sm, node) {
					var disable = node ? false : true;
					disable = disable || this.readOnly;
					this.topMenu.setDisabled(disable);
				}
			});
			Ext.apply(this.topMenu, {
				 showMenu:function() {
					this.showContextMenu(false);
				}.createDelegate(this)
//				,menu:this.getContextMenu()
			});
		}

		// expand root node if so configured
		if(this.expandOnRender) {
			this.root.expand();
		}

		// prevent default browser context menu to appear
		this.el.on({
			contextmenu:{fn:function(){return false;},stopEvent:true}
		});

		// setup loading mask if configured
		if(true === this.loadMask) {
			this.loader.on({
				 scope:this.el
				,beforeload:this.el.mask.createDelegate(this.el, [this.loadingText + '...'])
				,load:this.el.unmask
				,loadexception:this.el.unmask
			});
		}

	} // eo function onRender
	// }}}

	// new methods
	// {{{
	/**
	 * runs after an Ajax requested command has completed/failed
	 * @private
	 * @param {Object} options Options used for the request
	 * @param {Boolean} success true if ajax call was successful (cmd may have failed)
	 * @param {Object} response ajax call response object
	 */
	,cmdCallback:function(options, success, response) {
		var i, o, node;
		var showMsg = true;

		// process Ajax success
		if(true === success) {

			// try to decode JSON response
			try {
				o = Ext.decode(response.responseText);
			}
			catch(ex) {
				this.showError(response.responseText);
			}

			// process command success
			if(true === o.success) {
				switch(options.params.cmd) {
					case 'delete':
						if(true !== this.eventsSuspended) {
							this.fireEvent('delete', this, this.getPath(options.node));
						}
						options.node.parentNode.removeChild(options.node);
					break;

					case 'newdir':
						if(true !== this.eventsSuspended) {
							// CUSTOM TWEAK: NOTE (Goran): Need last param o to set folderId from returned data, it is not set in original file
							this.fireEvent('newdir', this, options.node, o);
						}
					break;

					case 'rename':
						this.updateCls(options.node, options.params.oldname);
						if(true !== this.eventsSuspended) {
							this.fireEvent('rename', this, options.node, options.params.newname, options.params.oldname);
						}
					break;
				}
			} // eo process command success
			// process command failure
			else {
				switch(options.params.cmd) {

					case 'rename':
						// handle drag & drop rename error
						if(options.oldParent) {
							options.oldParent.appendChild(options.node);
						}
						// handle simple rename error
						else {
							options.node.setText(options.oldName);
						}
						// signal failure to onNodeDrop
						if(options.e) {
							options.e.failure = true;
						}
						if(true !== this.eventsSuspended) {
							this.fireEvent('renamefailure', this, options.node, options.params.newname, options.params.oldname);
						}
					break;

					case 'newdir':
						if(false !== this.eventsSuspended) {
							this.fireEvent('newdirfailure', this, options.params.dir);
						}
						options.node.parentNode.removeChild(options.node);
					break;

					case 'delete':
						if(true !== this.eventsSuspended) {
							this.fireEvent('deletefailure', this, options.node);
						}
						options.node.parentNode.reload.defer(1, options.node.parentNode);
					break;

					default:
						this.root.reload();
					break;
				}

				// show default message box with server error
				this.showError(o.error || response.responseText);
			} // eo process command failure
		} // eo process Ajax success

		// process Ajax failure
		else {
			this.showError(response.responseText);
		}
	} // eo function cmdCallback
	// }}}
	// {{{
	/**
	 * displays overwrite confirm msg box and runs passed callback if response is yes
	 * @private
	 * @param {String} filename File to overwrite
	 * @param {Function} callback Function to call on yes response
	 * @param {Object} scope Scope for callback (defaults to this)
	 */
	,confirmOverwrite:function(filename, callback, scope) {
		Ext.Msg.show({
			 title:this.confirmText
			,msg:String.format(this.existsText, filename) + '. ' + this.overwriteText
			,icon:Ext.Msg.QUESTION
			,buttons:Ext.Msg.YESNO
			,fn:callback.createDelegate(scope || this)
		});
	}
	// }}}
	// {{{
	/**
	 * creates new directory (node)
	 * @private
	 * @param {Ext.tree.AsyncTreeNode} node
	 */
	,createNewDir:function(node) {

		// fire beforenewdir event
		if(true !== this.eventsSuspended && false === this.fireEvent('beforenewdir', this, node)) {
			return;
		}

		var treeEditor = this.treeEditor;
		var newNode;

		// get node to append the new directory to
		var appendNode = node.isLeaf() ? node.parentNode : node;

		// create new folder after the appendNode is expanded
		appendNode.expand(false, false, function(n) {
			// create new node
			//newNode = n.appendChild(new Ext.tree.AsyncTreeNode({text:this.newdirText, iconCls:'folder'}));
			// CUSTOM TWEAK: NOTE (Goran):: This line is changed, previous line is original
			// This temp object is needed because Ext share config object betwean new nodes, and new
			// node get all changes from old one, this caused bug that new node couldnt be accessed
			var newConfigs = {};
			newConfigs = Ext. apply(newConfigs, this.newDirConfig);
			newNode = n.appendChild(new Ext.tree.AsyncTreeNode(newConfigs));

			// setup one-shot event handler for editing completed
			treeEditor.on({
				complete:{
					 scope:this
					,single:true
					,fn:this.onNewDir
				}}
			);

			// creating new directory flag
			treeEditor.creatingNewDir = true;

			// start editing after short delay
			(function(){treeEditor.triggerEdit(newNode);}.defer(10));
		// expand callback needs to run in this context
		}.createDelegate(this));

	} // eo function creatingNewDir
	// }}}
	// {{{
	/**
	 * deletes the passed node
	 * @private
	 * @param {Ext.tree.AsyncTreeNode} node
	 */
	,deleteNode:function(node) {
		// fire beforedelete event
		if(true !== this.eventsSuspended && false === this.fireEvent('beforedelete', this, node)) {
			return;
		}

		Ext.Msg.show({
			 title:this.deleteText
			,msg:this.reallyWantText + ' ' + this.deleteText.toLowerCase()  + ' "' + node.text + '"?'
			,icon:Ext.Msg.WARNING
			,buttons:Ext.Msg.YESNO
			,scope:this
			,fn:function(response) {
				// do nothing if answer is not yes
				if('yes' !== response) {
					this.getEl().dom.focus();
					return;
				}
				// setup request options
				var options = {
					 url:this.deleteUrl || this.url
					,method:this.method
					,scope:this
					,callback:this.cmdCallback
					,node:node
					,params:{
						 cmd:'delete'
						,file:this.getPath(node)
					}
				};
				Ext.Ajax.request(options);
			}
		});
	} // eo function deleteNode
	// }}}
	// {{{
	/**
	 * requests file download from server
	 * @private
	 * @param {String} path Full path including file name but relative to server root path
	 */
	,downloadFile:function(path) {

		// create hidden target iframe
		var id = Ext.id();
		var frame = document.createElement('iframe');
		frame.id = id;
		frame.name = id;
		frame.className = 'x-hidden';
		if(Ext.isIE) {
			frame.src = Ext.SSL_SECURE_URL;
		}

		document.body.appendChild(frame);

		if(Ext.isIE) {
			document.frames[id].name = id;
		}

		var form = Ext.DomHelper.append(document.body, {
			 tag:'form'
			,method:'post'
			,action:this.downloadUrl || this.url
			,target:id
		});

		document.body.appendChild(form);

		var hidden;

		// append cmd to form
		hidden = document.createElement('input');
		hidden.type = 'hidden';
		hidden.name = 'cmd';
		hidden.value = 'download';
		form.appendChild(hidden);

		// append path to form
		hidden = document.createElement('input');
		hidden.type = 'hidden';
		hidden.name = 'path';
		hidden.value = path;
		form.appendChild(hidden);

		var callback = function() {
			Ext.EventManager.removeListener(frame, 'load', callback, this);
			setTimeout(function() {document.body.removeChild(form);}, 100);
			setTimeout(function() {document.body.removeChild(frame);}, 110);
		};

		Ext.EventManager.on(frame, 'load', callback, this);

		form.submit();
	}
	// }}}
	// {{{
	/**
	 * returns (and lazy create) the context menu
	 * @private
	 */
	,getContextMenu:function() {
		// lazy create context menu
		if(!this.contextmenu) {
			var config = {
				 singleUpload:this.singleUpload
				,maxFileSize:this.maxFileSize
				,enableProgress:this.enableProgress
				// CUSTOM TWEAK: ProgressUrl wasnt passed originaly, so default was used, and there was no way of changing it
				,progressUrl:this.progressUrl
				// CUSTOM TWEAK: Translation text wasnt passed originaly
				,confirmText: this.confirmText
				,deleteText: this.deleteText
				,errorText: this.errorText
				,existsText: this.existsText
				,fileText: this.fileText
				,loadingText: this.loadingText
				,newdirText: this.newdirText
				,overwriteText: this.overwriteText
				,reallyWantText: this.reallyWantText
				,collapseText: this.collapseText
				,expandText: this.expandText
				,renameText: this.renameText
				,uploadFileText: this.addText
				,addText: this.addText
				,clickRemoveText: this.clickRemoveText
				,clickStopText: this.clickStopText
				,errorText: this.errorText
				,fileQueuedText: this.fileQueuedText
				,fileDoneText: this.fileDoneText
				,fileFailedText: this.fileFailedText
				,fileStoppedText: this.fileStoppedText
				,fileUploadingText: this.fileUploadingText
				,removeAllText: this.removeAllText
				,removeText: this.removeText
				,stopAllText: this.stopAllText
				,uploadText: this.uploadText
			};
			if(this.baseParams) {
				config.baseParams = this.baseParams;
			}
			this.contextmenu = new Ext.ux.FileTreeMenu(config);
			this.contextmenu.on({click:{scope:this, fn:this.onContextClick}});

			this.uploadPanel = this.contextmenu.getItemByCmd('upload-panel').component;
			this.uploadPanel.on({
				 beforeupload:{scope:this, fn:this.onBeforeUpload}
				,allfinished:{scope:this, fn:this.onAllFinished}
			});
			this.uploadPanel.setUrl(this.uploadUrl || this.url);
		}
		return this.contextmenu;
	} // eo function getContextMenu
	// }}}
	// {{{
	/**
	 * returns file class based on name extension
	 * @private
	 * @param {String} name File name to get class of
	 */
	,getFileCls:function(name) {
		var atmp = name.split('.');
		if(1 === atmp.length) {
			return this.fileCls;
		}
		else {
			return this.fileCls + '-' + atmp.pop().toLowerCase();
		}
	}
	// }}}
	// {{{
	/**
	 * returns path of node (file/directory)
	 * @private
	 */
	,getPath:function(node) {
		var path, p, a;

		// get path for non-root node
		if(node !== this.root) {
			p = node.parentNode;
			a = [node.text];
			while(p && p !== this.root) {
				a.unshift(p.text);
				p = p.parentNode;
			}
			a.unshift(this.root.attributes.path || '');
			path = a.join(this.pathSeparator);
		}

		// path for root node is it's path attribute
		else {
			path = node.attributes.path || '';
		}

		// a little bit of security: strip leading / or .
		// full path security checking has to be implemented on server
		path = path.replace(/^[\/\.]*/, '');
		return path;
	} // eo function getPath
	// }}}
	// {{{
	/**
	 * returns true if node has child with the specified name (text)
	 * @private
	 * @param {Ext.data.Node} node
	 * @param {String} childName
	 */
	,hasChild:function(node, childName) {
		return (node.isLeaf() ? node.parentNode : node).findChild('text', childName) !== null;
	}
	// }}}
	// {{{
	/**
	 * Hides context menu
	 * @return {Ext.ux.FileTreeMenu} this
	 */
	,hideContextMenu:function() {
		if(this.contextmenu && this.contextmenu.isVisible()) {
			this.contextmenu.hide();
		}
		return this;
	} // eo function hideContextMenu
	// }}}
	// {{{
	/**
	 * called before editing is completed - allows edit cancellation
	 * @private
	 * @param {TreeEditor} editor
	 * @param {String} newName
	 * @param {String} oldName
	 */
	,onBeforeEditComplete:function(editor, newName, oldName) {
		if(editor.cancellingEdit) {
			editor.cancellingEdit = false;
			return;
		}
		var oldPath = this.getPath(editor.editNode);
		// CUSTOM TWEAK: NOTE (Nenad): Error in regex: backslash in square brackets changed to slash
//		var newPath = oldPath.replace(/\/[^\\]+$/, '/' + newName);
		var newPath = oldPath.replace(/\/[^\/]+$/, '/' + newName);

		if(false === this.fireEvent('beforerename', this, editor.editNode, newPath, oldPath)) {
			editor.cancellingEdit = true;
			editor.cancelEdit();
			return false;
		}
	}
	// }}}
	// {{{
	/**
	 * runs before node is dropped
	 * @private
	 * @param {Object} e dropEvent object
	 */
	,onBeforeNodeDrop:function(e) {

		// source node, node being dragged
		var s = e.dropNode;

		// destination node (dropping on this node)
		var d = e.target.leaf ? e.target.parentNode : e.target;

		// node has been dropped within the same parent
		if(s.parentNode === d) {
			return false;
		}

		// check if same name exists in the destination
		// this works only if destination node is loaded
		if(this.hasChild(d, s.text) && undefined === e.confirmed) {
			this.confirmOverwrite(s.text, function(response) {
				e.confirmed = 'yes' === response;
				this.onBeforeNodeDrop(e);
			});
			return false;
		}
		if(false === e.confirmed) {
			return false;
		}

		e.confirmed = undefined;
		e.oldParent = s.parentNode;

		var oldName = this.getPath(s);
		var newName = this.getPath(d) + '/' + s.text;

		// fire beforerename event
		if(true !== this.eventsSuspended && false === this.fireEvent('beforerename', this, s, newName, oldName)) {
			return false;
		}

		var options = {
			 url:this.renameUrl || this.url
			,method:this.method
			,scope:this
			,callback:this.cmdCallback
			,node:s
			,oldParent:s.parentNode
			,e:e
			,params:{
				 cmd:'rename'
				,oldname:oldName
				,newname:newName
			}
		};
		Ext.Ajax.request(options);
		return true;
	}
	// }}}
	// {{{
	/**
	 * sets uploadPanel's destination path
	 * @private
	 */
	,onBeforeUpload:function(uploadPanel) {

		var menu = this.getContextMenu();
		var path = this.getPath(menu.node);
		if(menu.node.isLeaf()) {
			path = path.replace(/\/[^\/]+$/, '', path);
		}
		uploadPanel.setPath(path);

	} // eo function onBeforeUpload
	// }}}
	// {{{
	/**
	 * reloads tree node on upload finish
	 * @private
	 */
	,onAllFinished:function(uploader) {
		var menu = this.getContextMenu();
		(menu.node.isLeaf() ? menu.node.parentNode : menu.node).reload();
	} // eo function onAllFinished
	// }}}
	// {{{
	/**
	 * @private
	 * context menu click handler
	 * @param {Ext.menu.Menu} context menu
	 * @param {Ext.menu.Item} item clicked
	 * @param {Ext.EventObject} raw event
	 */
	,onContextClick:function(menu, item, e) {
		if(item.disabled) {
			return;
		}
		var node = menu.node;
		if(!node) {
			node = menu.parentMenu.node;
		}
		switch(item.cmd) {
			case 'reload':
				node.reload();
			break;

			case 'expand':
				node.expand(true);
			break;

			case 'collapse':
				node.collapse(true);
			break;

			case 'open':
				this.openNode(node);
			break;

			case 'open-self':
				this.openNode(node, '_self');
			break;

			case 'open-popup':
				this.openNode(node, 'popup');
			break;

			case 'open-blank':
				this.openNode(node, '_blank');
			break;

			case 'open-dwnld':
				this.openNode(node, 'download');
			break;

			case 'rename':
				this.treeEditor.triggerEdit(node);
			break;

			case 'delete':
				this.deleteNode(node);
			break;

			case 'newdir':
				this.createNewDir(node);
			break;

			default:
			break;
		}
	} // eo function onContextClick
	// }}}
	// {{{
	/**
	 * contextmenu event handler
	 * @private
	 */
	,onContextMenu:function(node, e) {
		if(this.readOnly) {
			return false;
		}
		this.showContextMenu(node);

		return false;
	} // eo function onContextMenu
	// }}}
	// {{{
	/**
	 * dblclick handlers
	 * @private
	 */
	,onDblClick:function(node, e) {
		this.openNode(node);
	} // eo function onDblClick
	// }}}
	// {{{
	/**
	 * Destroys the FileTreePanel and sub-components
	 * @private
	 */
	,onDestroy:function() {

		// destroy contextmenu
		if(this.contextmenu) {
			this.contextmenu.purgeListeners();
			this.contextmenu.destroy();
			this.contextmenu = null;
		}

		// destroy treeEditor
		if(this.treeEditor) {
			this.treeEditor.purgeListeners();
			this.treeEditor.destroy();
			this.treeEditor = null;
		}

		// remover reference to treeSorter
		if(this.treeSorter) {
			this.treeSorter = null;
		}

		// call parent
		Ext.ux.FileTreePanel.superclass.onDestroy.call(this);

	} // eo function onDestroy
	// }}}
	// {{{
	/**
	 * runs when editing of a node (rename) is completed
	 * @private
	 * @param {Ext.Editor} editor
	 * @param {String} newName
	 * @param {String} oldName
	 */
	,onEditComplete:function(editor, newName, oldName) {

		var node = editor.editNode;

		if(newName === oldName || editor.creatingNewDir) {
			editor.creatingNewDir = false;
			return;
		}
		var path = this.getPath(node.parentNode);
		var options = {
			 url:this.renameUrl || this.url
			,method:this.method
			,scope:this
			,callback:this.cmdCallback
			,node:node
			,oldName:oldName
			,params:{
				 cmd:'rename'
				,oldname:path + '/' + oldName
				,newname:path + '/' + newName
			}
		};
		Ext.Ajax.request(options);
	}
	// }}}
	// {{{
	/**
	 * create new directory handler
	 * @private
	 * runs after editing of new directory name is completed
	 * @param {Ext.Editor} editor
	 */
	,onNewDir:function(editor) {
		var path = this.getPath(editor.editNode);
		var options = {
			 url:this.newdirUrl || this.url
			,method:this.method
			,scope:this
			,node:editor.editNode
			,callback:this.cmdCallback
			,params:{
				 cmd:'newdir'
				,dir:path
			}
		};
		Ext.Ajax.request(options);
	}
	// }}}
	// {{{
	/**
	 * called while dragging over, decides if drop is allowed
	 * @private
	 * @param {Object} dd event
	 */
	,onNodeDragOver:function(e) {
		e.cancel = e.target.disabled || e.dropNode.parentNode === e.target.parentNode && e.target.isLeaf();
	} // eo function onNodeDragOver
	// }}}
	// {{{
	/**
	 * called when node is dropped
	 * @private
	 * @param {Object} dd event
	 */
	,onNodeDrop:function(e) {

		// failure can be signalled by cmdCallback
		// put drop node to the original parent in that case
		if(true === e.failure) {
			e.oldParent.appendChild(e.dropNode);
			return;
		}

		// if we already have node with the same text, remove the duplicate
		var sameNode = e.dropNode.parentNode.findChild('text', e.dropNode.text);
		if(sameNode && sameNode !== e.dropNode) {
			sameNode.parentNode.removeChild(sameNode);
		}
	}
	// }}}
	// {{{
	/**
	 * Opens node
	 * @param {Ext.tree.AsyncTreeNode} node
	 * @param {String} mode Can be "_self", "_blank", or "popup". Defaults to (this.openMode)
	 */
	,openNode:function(node, mode) {

		if(!this.enableOpen) {
			return;
		}

		mode = mode || this.openMode;

		var url;
		var path;
		if(node.isLeaf()) {
			path = this.getPath(node);
			url = this.hrefPrefix + path + this.hrefSuffix;

			// fire beforeopen event
			if(true !== this.eventsSuspended && false === this.fireEvent('beforeopen', this, node.text, url, mode)) {
				return;
			}

			switch(mode) {
				case 'popup':
					if(!this.popup || this.popup.closed) {
						this.popup = window.open(url, this.hrefTarget, this.popupFeatures);
					}
					this.popup.location = url;
					if(this.focusPopup) {
						this.popup.focus();
					}
				break;

				case '_self':
					window.location = url;
				break;

				case '_blank':
					window.open(url);
				break;

				case 'download':
					this.downloadFile(path);
				break;
			}

			// fire open event
			if(true !== this.eventsSuspended) {
				this.fireEvent('open', this, node.text, url, mode);
			}
		}

	}
	// }}}
	// {{{
	/**
	 * Sets/Unsets delete of files/directories disabled/enabled
	 * @param {Boolean} disabled
	 * @return {Ext.ux.FileTreePanel} this
	 */
	,setDeleteDisabled:function(disabled) {
		disabled = !(!disabled);
		if(!this.enableDelete === disabled) {
			return this;
		}
		this.hideContextMenu();
		this.enableDelete = !disabled;
	} // eo function setDeleteDisabled
	// }}}
	// {{{
	/**
	 * Sets/Unsets creation of new directory disabled/enabled
	 * @param {Boolean} disabled
	 * @return {Ext.ux.FileTreePanel} this
	 */
	,setNewdirDisabled:function(disabled) {
		disabled = !(!disabled);
		if(!this.enableNewDir === disabled) {
			return this;
		}
		this.hideContextMenu();
		this.enableNewDir = !disabled;

	} // eo function setNewdirDisabled
	// }}}
	// {{{
	/**
	 * Sets/Unsets open files disabled/enabled
	 * @param {Boolean} disabled
	 * @return {Ext.ux.FileTreePanel} this
	 */
	,setOpenDisabled:function(disabled) {
		disabled = !(!disabled);
		if(!this.enableOpen === disabled) {
			return this;
		}
		this.hideContextMenu();
		this.enableOpen = !disabled;

		return this;
	} // eo function setOpenDisabled
	// }}}
	// {{{
	/**
	 * Sets/Unsets this tree to/from readOnly state
	 * @param {Boolean} readOnly
	 * @return {Ext.ux.FileTreePanel} this
	 */
	,setReadOnly:function(readOnly) {
		readOnly = !(!readOnly);
		if(this.readOnly === readOnly) {
			return this;
		}
		this.hideContextMenu();
		if(this.dragZone) {
			this.dragZone.locked = readOnly;
		}
		this.readOnly = readOnly;

		return this;

	} // eo function setReadOnly
	// }}}
	// {{{
	/**
	 * Sets/Unsets rename of files/directories disabled/enabled
	 * @param {Boolean} disabled
	 * @return {Ext.ux.FileTreePanel} this
	 */
	,setRenameDisabled:function(disabled) {
		disabled = !(!disabled);
		if(!this.enableRename === disabled) {
			return this;
		}
		this.hideContextMenu();
		if(this.dragZone) {
			this.dragZone.locked = disabled;
		}
		this.enableRename = !disabled;

		return this;
	} // eo function setRenameDisabled
	// }}}
	// {{{
	/**
	 * Sets/Unsets uploading of files disabled/enabled
	 * @param {Boolean} disabled
	 * @return {Ext.ux.FileTreePanel} this
	 */
	,setUploadDisabled:function(disabled) {
		disabled = !(!disabled);
		if(!this.enableUpload === disabled) {
			return this;
		}
		this.hideContextMenu();
		this.enableUpload = !disabled;

		return this;
	} // of function setUploadDisabled
	// }}}
	// {{{
	/**
	 * adjusts context menu depending on many things and shows it
	 * @private
	 * @param {Ext.tree.AsyncTreeNode} node Node on which was right-clicked
	 */
	,showContextMenu:function(node) {

		// setup node alignment
		var topAlign = false;
		var alignEl = this.topMenu ? this.topMenu.getEl() : this.body;

		if(!node) {
			node = this.getSelectionModel().getSelectedNode();
			topAlign = true;
		}
		else {
			alignEl = node.getUI().getEl();
		}
		if(!node) {
			return;
		}

		var menu = this.getContextMenu();
		menu.node = node;

		// set node name
		menu.getItemByCmd('nodename').setText(Ext.util.Format.ellipsis(node.text, 22));

		// enable/disable items depending on node clicked
		menu.setItemDisabled('open', !node.isLeaf());
		menu.setItemDisabled('reload', node.isLeaf());
		menu.setItemDisabled('expand', node.isLeaf());
		menu.setItemDisabled('collapse', node.isLeaf());
		menu.setItemDisabled('delete', node === this.root || node.disabled);
		menu.setItemDisabled('rename', this.readOnly || node === this.root || node.disabled);
		menu.setItemDisabled('newdir', this.readOnly || (node.isLeaf() ? node.parentNode.disabled : node.disabled));
		menu.setItemDisabled('upload', node.isLeaf() ? node.parentNode.disabled : node.disabled);
		menu.setItemDisabled('upload-panel', node.isLeaf() ? node.parentNode.disabled : node.disabled);

		// show/hide logic
		menu.getItemByCmd('open').setVisible(this.enableOpen);
		// CUSTOM TWEAK: NOTE (Goran): I added next line originaly reload context item  was alaways shown
		menu.getItemByCmd('reload').setVisible(this.enableReload);
		menu.getItemByCmd('delete').setVisible(this.enableDelete);
		menu.getItemByCmd('newdir').setVisible(this.enableNewDir);
		menu.getItemByCmd('rename').setVisible(this.enableRename);
		menu.getItemByCmd('upload').setVisible(this.enableUpload);
		menu.getItemByCmd('upload-panel').setVisible(this.enableUpload);
		menu.getItemByCmd('sep-upload').setVisible(this.enableUpload);
		menu.getItemByCmd('sep-collapse').setVisible(this.enableNewDir || this.enableDelete || this.enableRename);

		// select node
		node.select();

		// show menu
		if(topAlign) {
			menu.showAt(menu.getEl().getAlignToXY(alignEl, 'tl-bl?'));
		}
		else {
			menu.showAt(menu.getEl().getAlignToXY(alignEl, 'tl-tl?', [0, 18]));
		}
	} // eo function
	// }}}
	// {{{
	/**
	 * universal show error function
	 * @private
	 * @param {String} msg message
	 * @param {String} title title
	 */
	,showError:function(msg, title) {
		Ext.Msg.show({
			 title:title || this.errorText
			,msg:Ext.util.Format.ellipsis(msg, this.maxMsgLen)
			,fixCursor:true
			,icon:Ext.Msg.ERROR
			,buttons:Ext.Msg.OK
			,minWidth:1200 > String(msg).length ? 360 : 600
		});
	} // eo function showError
	// }}}
	// {{{
	/**
	 * updates class of leaf after rename
	 * @private
	 * @param {Ext.tree.AsyncTreeNode} node Node to update class of
	 * @param {String} oldName Name the node had before
	 */
	,updateCls:function(node, oldName) {
		if(node.isLeaf()) {
			Ext.fly(node.getUI().iconNode).removeClass(this.getFileCls(oldName));
			Ext.fly(node.getUI().iconNode).addClass(this.getFileCls(node.text));
		}
	}
	// }}}

}); // eo extend

// register xtype
Ext.reg('filetreepanel', Ext.ux.FileTreePanel);

// eof
// Aurigma Image Uploader Dual 5.x Embedding Script 
// Version 2.0.4.0 Feb 20, 2008
// Copyright(c) Aurigma Inc. 2002-2008

function __Browser(){
	var a=navigator.userAgent.toLowerCase();
	this.isOpera=(a.indexOf("opera")!=-1);
	this.isKonq=(a.indexOf('konqueror')!=-1);
	this.isSafari=(a.indexOf('safari')!=-1)&&(a.indexOf('mac')!=-1);
	this.isKhtml=this.isSafari||this.isKonq;
	this.isIE=(a.indexOf("msie")!=-1)&&!this.isOpera;
	this.isWinIE=this.isIE;
	this.isCSS1Compat=(!this.isIE)||(document.compatMode&&document.compatMode=="CSS1Compat");
}

var __browser=new __Browser();

//Create set/get expando methods for ActiveX
function _createExpandoMethods(id){
	var o=document.getElementById(id);
	var props=new Array();
	var hasPaneItemApiElements=false;
	for (propName in o){
		var c=propName.charAt(0);
		if (c==c.toUpperCase()){
			props.push(propName);
			if (propName=="PaneItemDesign"){
				hasPaneItemApiElements=true;
			}
		}
	}
	for (i=0;i<props.length;i++){
		//Check whether property is indexed
		if (typeof(o[props[i]])=="unknown"){
			eval("o.set"+props[i]+"=function(i,v){this."+props[i]+"(i)=v;};");
			eval("o.get"+props[i]+"=function(i){return this."+props[i]+"(i);};");
		}
		else{
			eval("o.set"+props[i]+"=function(v){this."+props[i]+"=v};");
			eval("o.get"+props[i]+"=function(){return this."+props[i]+"};");
		}
	}
	if (hasPaneItemApiElements){
		eval("o.setPaneItemDesign = function(Pane, Index, Value){this.PaneItemDesign(Pane, Index) = Value;};");
		eval("o.getPaneItemDesign = function(Pane, Index){return this.PaneItemDesign(Pane, Index);};");		
		//eval("o.getPaneItemCount = function(Pane){return this.PaneItemCount(Pane);};");
		//eval("o.getPaneItemChecked = function(Index){return this.PaneItemChecked(Index);};");
		//eval("o.getPaneItemCanBeUploaded = function(Index){return this.PaneItemCanBeUploaded(Index);};");
		eval("o.getPaneItemSelected = function(Pane, Index){return this.PaneItemSelected(Pane, Index);};");
		eval("o.setPaneItemEnabled = function(Pane, Index, Value){this.PaneItemEnabled(Pane, Index) = Value;};");
		eval("o.getPaneItemEnabled = function(Pane, Index){return this.PaneItemEnabled(Pane, Index);};");
	}
}

//Installation instructions
function _addInstructions(obj){
	obj.instructionsEnabled=true;
	if (obj.controlClass=="FileDownloader"){
		obj.instructionsCommon="<p>File Downloader ActiveX control is necessary to download "+
			"files quickly and easily. You will be able to select required files "+
			"in a user-friendly interface and simply click a <strong>Download</strong> button. "+
			"Installation will take up to few minutes, please be patient. To install File Downloader, ";
		obj.instructionsNotWinXPSP2="please reload the page and click the <strong>Yes</strong> button " +
			"when you see the control installation dialog.";
		obj.instructionsWinXPSP2="please click the <strong>Information Bar</strong>. After page reload click <strong>Install</strong> when "+
			"you see the control installation dialog.";
		obj.instructionsVista = "please click on the <strong>Information Bar</strong> and select <strong>Install ActiveX Control</strong> "+
			"from the dropdown menu. After page reload click <strong>Continue</strong> and then <strong>Install</strong> when "+
			"you see the control installation dialog.";
		obj.instructionsCommon2="</p><p><strong>NOTE:</strong> If control fails to be installed, it may mean that it has been inserted to the page incorrectly. "+
			"Refer File Downloader documentation for more details.</p>";
	} else {
		obj.instructionsCommon="<p>Image Uploader ActiveX control is necessary to upload "+
			"your files quickly and easily. You will be able to select multiple images "+
			"in user-friendly interface instead of clumsy input fields with <strong>Browse</strong> button. "+
			"Installation will take up to few minutes, please be patient. To install Image Uploader, ";
		obj.instructionsNotWinXPSP2="please reload the page and click the <strong>Yes</strong> button " +
			"when you see the control installation dialog.";
		obj.instructionsWinXPSP2="please click on the <strong>Information Bar</strong> and select " +
			"<strong>Install ActiveX Control</strong> from the dropdown menu. After page reload click <strong>Install</strong> when "+
			"you see the control installation dialog.";
		obj.instructionsVista = "please click on the <strong>Information Bar</strong> and select <strong>Install ActiveX Control</strong> "+
			"from the dropdown menu. After page reload click <strong>Continue</strong> and then <strong>Install</strong> when "+
			"you see the control installation dialog.";
		obj.instructionsCommon2="</p>";
	}
}

function ControlWriter(id,width,height){
	//Private
	this._params=new Array();
	this._events=new Array();
	
	_addInstructions(this);

	this._getObjectParamHtml=function(name,value){
		return "<param name=\""+name+"\" value=\""+value+"\" />";
	}

	this._getObjectParamsHtml=function(){
		var r="";
		var p=this._params;
		var i;
		for (i=0;i<p.length;i++){
			r+=this._getObjectParamHtml(p[i].name,p[i].value);
		}
		return r;
	}

	this._getObjectEventsHtml=function(){
		var r="";
		var e=this._events;
		for (i=0;i<e.length;i++){
			r+=this._getObjectParamHtml(e[i].name+"Listener",e[i].listener);
		}
		return r;
	}

	this._getEmbedParamHtml=function(name,value){
		return " "+name+"=\""+value+"\"";	
	}

	this._getEmbedParamsHtml=function(){
		var r="";
		var p=this._params;
		var i;
		for (i=0;i<p.length;i++){
			r+=this._getEmbedParamHtml(p[i].name,p[i].value);
		}
		return r;
	}

	this._getEmbedEventsHtml=function(){
		var r="";
		var e=this._events;
		for (i=0;i<e.length;i++){
			r+=this._getEmbedParamHtml(e[i].name+"Listener",e[i].listener);
		}
		return r;
	}

	//Public

	//Properties
	this.id=id;
	this.width=width;
	this.height=height;

	this.activeXControlEnabled=true;
	this.activeXControlVersion="";

	this.javaAppletEnabled=true;
	this.javaAppletCodeBase="./";
	this.javaAppletCached=true;
	this.javaAppletVersion="";

	this.fullPageLoadListenerName=null;

	//Methods
	this.addParam=function(paramName,paramValue){
		var p=new Object();
		p.name=paramName;
		p.value=paramValue;
		this._params.push(p);
	}

	this.addEventListener=function(eventName,eventListener){
		try
	        {
        	    	eval("var checkListener = " + eventListener + ";");
	            	if (checkListener == null)
			{
				return;
        		}
	        }
        	catch (e)
	        {
        	    	return;
	        }
		var p=new Object();
		p.name=eventName;
		p.listener=eventListener;
		this._events.push(p);
	}

	this.getActiveXInstalled=function(){
		if (this.activeXProgId){
			try{
				var a=new ActiveXObject(this.activeXProgId);
				return true;
			}
			catch(e){
				return false;
			}
		}
		return false;
	}

	this.getHtml=function(){
		var r="";
		if (this.fullPageLoadListenerName){
			r+="<" + "script type=\"text/javascript\">";
			r+="var __"+this.id+"_pageLoaded=false;";
			r+="var __"+this.id+"_controlLoaded=false;";
			r+="function __fire_"+this.id+"_fullPageLoad(){";
			r+="if (__"+this.id+"_pageLoaded&&__"+this.id+"_controlLoaded){";
			r+=this.fullPageLoadListenerName + "();";
			r+="}";
			r+="}";
			var pageLoadCode="new Function(\"__"+this.id+"_pageLoaded=true;__fire_"+this.id+"_fullPageLoad();\")";
			if (__browser.isWinIE){
				r+="window.attachEvent(\"onload\","+pageLoadCode+");";
			}
			else{
				r+="var r=window.addEventListener?window:document.addEventListener?document:null;";
				r+="if (r){r.addEventListener(\"load\","+pageLoadCode+",false);}";
			}
			r+="<"+"/script>";
		}		

		//ActiveX control
		if(__browser.isWinIE&&this.activeXControlEnabled){
			var v=this.activeXControlVersion.replace(/\./g,",")
			var cb=this.activeXControlCodeBase+(v==""?"":"#version="+v);

			r+="<" + "script for=\""+this.id+"\" event=\"InitComplete()\">";
			r+="_createExpandoMethods(\""+this.id+"\");";
			if (this.fullPageLoadListenerName){
				r+="__"+this.id+"_controlLoaded=true;";
				r+="__fire_"+this.id+"_fullPageLoad();";
			}
			r+="<"+"/script>";

			r+="<object id=\""+this.id+"\" name=\""+this.id+"\" classid=\"clsid:"+this.activeXClassId+"\" codebase=\""+cb+"\" width=\""+this.width+"\" height=\""+this.height+"\">";
			if (this.instructionsEnabled){
				r+=this.instructionsCommon;
				var isXPSP2 = (window.navigator.userAgent.indexOf("SV1") != -1) || (window.navigator.userAgent.indexOf("MSIE 7.0") != -1);
				var isVista = (window.navigator.userAgent.indexOf("Windows NT 6.0") != -1) && (window.navigator.userAgent.indexOf("MSIE 7.0") != -1);
				if (isVista) {
					r+=this.instructionsVista;
					//r+=this.instructionsWinXPSP2;
				} else if (isXPSP2) {
					r+=this.instructionsWinXPSP2;
				}
				else{
					r+=this.instructionsNotWinXPSP2;
				}
				r+=this.instructionsCommon2;
			}
			r+=this._getObjectParamsHtml();
			r+="</object>";

			//Event handlers
			var e=this._events;
			var eventParams;
			for (i=0;i<e.length;i++){
				if (this.controlClass=="FileDownloader"){
					switch (e[i].name){
						case "DownloadComplete":
							eventParams="Value";
							break;
						case "DownloadItemComplete":
							eventParams="Result, ErrorPage, Url, FileName, ContentType, FileSize";
							break;
						case "DownloadStep":
							eventParams="Step";
							break;
						case "Progress":
							eventParams="PercentTotal, PercentCurrent, Index";
							break;
						case "Error":
							eventParams="ErrorCode, HttpErrorCode, ErrorPage, Url, Index";
							break;
						default:
							eventParams="";
					}
					r+="<script for=\""+this.id+"\" event=\""+e[i].name+"("+eventParams+")\">";
					r+=e[i].listener+"("+eventParams+");";
					r+="<"+"/script>";
				}
				else {
					switch (e[i].name){
						case "Progress":
							eventParams="Status, Progress, ValueMax, Value, StatusText";
							break;
						case "InnerComplete":
							eventParams="Status, StatusText";
							break;
						case "AfterUpload":
							eventParams="htmlPage";
							break;
						case "ViewChange":
						case "SortModeChange":
							eventParams="Pane";
							break;
						case "Error":
							eventParams="ErrorCode, HttpResponseCode, ErrorPage, AdditionalInfo";
							break;
						case "PackageBeforeUpload":
							eventParams = "PackageIndex";
						    	break;
						case "PackageError":
						    	eventParams = "PackageIndex, ErrorCode, HttpResponseCode, ErrorPage, AdditionalInfo";
						    	break;
						case "PackageComplete":
						    	eventParams = "PackageIndex, ResponsePage";
						    	break;
						case "PackageProgress":
						    	eventParams = "PackageIndex, Status, Progress, ValueMax, Value, StatusText";
						    	break;
						default:
							eventParams="";
					}
				}
				r+="<" + "script for=\""+this.id+"\" event=\""+e[i].name+"("+eventParams+")\">";
				if (e[i].name=="BeforeUpload" || e[i].name=="PackageComplete"){
					r+="return ";
				}
				r+=e[i].listener+"("+eventParams+");";
				r+="<"+"/script>";
			}

		}
		else 
		//Java appplet
		if(this.javaAppletEnabled){
			if (this.fullPageLoadListenerName){
				r+="<" + "script type=\"text/javascript\">";
				r+="function __"+this.id+"_InitComplete(){";
				r+="__"+this.id+"_controlLoaded=true;";
				r+="__fire_"+this.id+"_fullPageLoad();";
				r+="}";
				r+="<"+"/script>";
			}

			//<object> for IE and <applet> for Safari
			if (__browser.isWinIE||__browser.isKhtml){
				if (__browser.isWinIE){
					r+="<object id=\""+this.id+"\" classid=\"clsid:8AD9C840-044E-11D1-B3E9-00805F499D93\" codebase=\""+window.location.protocol+"//java.sun.com/update/1.4.2/jinstall-1_4-windows-i586.cab#Version=1,4,0,0\" width=\""+this.width+"\" height=\""+this.height+"\">";
				}
				else{
					r+="<applet id=\""+this.id+"\" code=\""+this.javaAppletClassName+"\" java_codebase=\"../\" align=\"baseline\" archive=\""+this.javaAppletJarFileName+"\" mayscript=\"true\" scriptable=\"true\" width=\""+this.width+"\" height=\""+this.height+"\">";
				}

				if (this.javaAppletCached&&this.javaAppletVersion!=""){
					r+=this._getObjectParamHtml("cache_archive",this.javaAppletJarFileName);
					var v=this.javaAppletVersion.replace(/\,/g,".");
					//r+=this._getObjectParamHtml("cache_version",v+","+v);
					r+=this._getObjectParamHtml("cache_version",v);
				}

				r+=this._getObjectParamHtml("id",this.id);
				r+=this._getObjectParamHtml("type","application/x-java-applet;version=1.4");
				r+=this._getObjectParamHtml("codebase",this.javaAppletCodeBase);
				r+=this._getObjectParamHtml("archive",this.javaAppletJarFileName);
				r+=this._getObjectParamHtml("code",this.javaAppletClassName);
				r+=this._getObjectParamHtml("scriptable","true");
				r+=this._getObjectParamHtml("mayscript","true");

				r+=this._getObjectParamsHtml();

				r+=this._getObjectEventsHtml();

				if (this.fullPageLoadListenerName){
					r+=this._getObjectParamHtml("InitCompleteListener","__"+this.id+"_InitComplete");
				}
				if (__browser.isWinIE){
					r+="</object>";
				}
				else{
					r+="</applet>";
				}
			}
			//<embed> for all other browsers
			else{
				r+="<embed id=\""+this.id+"\" type=\"application/x-java-applet;version=1.4\" codebase=\""+this.javaAppletCodeBase+"\" code=\""+this.javaAppletClassName+"\" archive=\""+this.javaAppletJarFileName+"\" width=\""+this.width+"\" height=\""+this.height+"\" scriptable=\"true\" mayscript=\"true\" pluginspage=\""+window.location.protocol+"//java.sun.com/products/plugin/index.html#download\"";

				if (this.javaAppletCached&&this.javaAppletVersion!=""){
					r+=this._getEmbedParamHtml("cache_archive",this.javaAppletJarFileName);
					var v=this.javaAppletVersion.replace(/\,/g,".");
					//r+=this._getEmbedParamHtml("cache_version",v+","+v);
					r+=this._getEmbedParamHtml("cache_version",v);
				}

				r+=this._getEmbedParamsHtml();

				r+=this._getEmbedEventsHtml();

				if (this.fullPageLoadListenerName){
					r+=this._getEmbedParamHtml("InitCompleteListener","__"+this.id+"_InitComplete");
				}
				r+=">";
				r+="</embed>";
			}
		}
		else
		{
			r+="Your browser is not supported.";
		}
		
		//For backward compatibility
		this.controlType=this.getControlType();
			
		return r;
	}

	this.getControlType=function(){
		return (__browser.isWinIE&&this.activeXControlEnabled)?"ActiveX":(this.javaAppletEnabled?"Java":"None");
	}
	
	this.writeHtml=function(){
		document.write(this.getHtml());
	}
}

function ImageUploaderWriter(id,width,height){
	this._base=ControlWriter;
	this._base(id,width,height);
	//These properties should be modified for private-label versions only
	this.activeXControlCodeBase="ImageUploader5.cab";
	this.activeXClassId="5D637FAD-E202-48D1-8F18-5B9C459BD1E3";
	this.activeXProgId="Aurigma.ImageUploaderEx.5";
	this.javaAppletJarFileName="ImageUploader5.jar";
	this.javaAppletClassName="com.aurigma.imageuploader.ImageUploader.class";

	//Extend
	this.showNonemptyResponse="off";
	this._getHtml=this.getHtml;
	this.getHtml=function(){
		var r="";
		if (this.showNonemptyResponse!="off"){
			r+="<" + "script type=\"text/javascript\">";
			r+="function __"+this.id+"_InnerComplete(Status,StatusText){";
			r+="if (new String(Status)==\"COMPLETE\" && new String(StatusText).replace(/\\s*/g,\"\")!=\"\"){";
			if (this.showNonemptyResponse=="dump"){
				r+="var f=document.createElement(\"fieldset\");";
				r+="var l=f.appendChild(document.createElement(\"legend\"));";
				r+="l.appendChild(document.createTextNode(\"Server Response\"));";
				r+="var d=f.appendChild(document.createElement(\"div\"));";
				r+="d.innerHTML=StatusText;";
				r+="var b=f.appendChild(document.createElement(\"button\"));";
				r+="b.appendChild(document.createTextNode(\"Clear Server Response\"));";
				r+="b.onclick=function(){var f=this.parentNode;f.parentNode.removeChild(f)};";
				r+="document.body.appendChild(f);";
			}
			else{
				var s="";
				for (var i=0;i<80;i++){s+="-";}
				r+="alert(\""+s+"\\r\\nServer Response\\r\\n"+s+"\\r\\n\"+StatusText);";
			}
			r+="}";
			r+="}";
			r+="<"+"/script>";
			this.addEventListener("InnerComplete","__"+this.id+"_InnerComplete");
		}
		return r+this._getHtml();
	}
}

function ThumbnailWriter(id,width,height){
	this._base=ControlWriter;
	this._base(id,width,height);
	//These properties should be modified for private label versions only
	this.activeXControlCodeBase="ImageUploader5.cab";
	this.activeXClassId="83AC1413-FCE4-4A46-9DD5-4F31F306E71F";
	this.activeXProgId="Aurigma.ThumbnailEx.5";
	this.javaAppletJarFileName="ImageUploader5.jar";
	this.javaAppletClassName="com.aurigma.imageuploader.Thumbnail.class";
}

function ShellComboBoxWriter(id,width,height){
	this._base=ControlWriter;
	this._base(id,width,height);
	//These properties should be modified for private label versions only
	this.activeXControlCodeBase="ImageUploader5.cab";
	this.activeXClassId="3BF72F68-72D8-461D-A884-329D936C5581";
	this.activeXProgId="Aurigma.ShellComboEx.5";
	this.javaAppletJarFileName="ImageUploader5.jar";
	this.javaAppletClassName="com.aurigma.imageuploader.ShellComboBox.class";
}

function UploadPaneWriter(id,width,height){
	this._base=ControlWriter;
	this._base(id,width,height);
	//These properties should be modified for private label versions only
	this.activeXControlCodeBase="ImageUploader5.cab";
	this.activeXClassId="78E9D883-93CD-4072-BEF3-38EE581E2839";
	this.activeXProgId="Aurigma.UploadPaneEx.5";
	this.javaAppletJarFileName="ImageUploader5.jar";
	this.javaAppletClassName="com.aurigma.imageuploader.UploadPane.class";
}

function FileDownloaderWriter(id,width,height){
	this._base=ControlWriter;
	this._base(id,width,height);
	//These properties should be modified for private label versions only
	this.activeXControlCodeBase="FileDownloader2.cab";
	this.activeXClassId="AAB58191-AFBE-4366-93FD-1E45F7C97FA0";
	this.activeXProgId="Aurigma.FileDownloader.2";
	this.javaAppletEnabled=false;
	this.controlClass="FileDownloader";
}

function getControlObject(id){
	if (__browser.isSafari){
		return document[id];
	}
	else{
		return document.getElementById(id);
	}
}

function getImageUploader(id){
	return getControlObject(id);
}

function getFileDownloader(id){
	return getControlObject(id);
}/**
 * @class   Ext.ux.tree.CheckTreePanel
 * @extends Ext.tree.TreePanel
 *
 * <p>
 * I've taken Condor's work from
 * <a href="http://extjs.com/forum/showthread.php?t=44369">http://extjs.com/forum/showthread.php?t=44369</a>,
 * removed tri-state functionality and put all files into one extension.
 * </p>
 * <p>
 * CheckTreePanel has been designed to fully load nodes in one shot using
 * nested "children" array.
 * </p>
 *
 * @author   Ing. Jozef Sakáloš
 * @copyright (c) 2009, by Ing. Jozef Sakáloš
 * @version  1.0
 * @date     9. January 2009
 * @revision $Id: Ext.ux.tree.CheckTreePanel.js 531 2009-02-02 01:07:35Z jozo $
 *
 * @license Ext.ux.tree.CheckTreePanel is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
 * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
 *
 * @forum    56875
 * @demo     http://checktree.extjs.eu
 * @download
 * <ul>
 * <li><a href="http://checktree.extjs.eu/checktree.tar.bz2">checktree.tar.bz2</a></li>
 * <li><a href="http://checktree.extjs.eu/checktree.tar.gz">checktree.tar.gz</a></li>
 * <li><a href="http://checktree.extjs.eu/checktree.zip">checktree.zip</a></li>
 * </ul>
 */

Ext.ns('Ext.ux.tree');

// {{{
/**
 * Creates new CheckTreePanel
 * @constructor
 * @param {Object} config The configuration object
 */
Ext.ux.tree.CheckTreePanel = Ext.extend(Ext.tree.TreePanel, {

	// {{{
	// default config
	/**
	 * @cfg {String} grayDisabled
	 * Which part of node should be grayed. Valid values: 'all', 'checkbox'. Defaults to 'all'.
	 */
	 grayDisabled:'all'

	/**
	 * @cfg {String} bubbleCheck
	 * Check/uncheck also parent nodes. Valid values: none, checked, unchecked, all. Defaults to 'checked'.
	 */
	 ,bubbleCheck:'checked'

	/**
	 * @cfg {String} cascadeCheck
	 * Check/uncheck also child nodes. Valid values: none, checked, unchecked, all. Defaults to 'unchecked'.
	 */
	,cascadeCheck:'unchecked'

	/**
	 * @cfg {Boolean} deepestOnly
	 * Set this to true for getValue to return only deepest checked node ids. Defaults to false.
	 */
	,deepestOnly:false

	/**
	 * @cfg {Boolean} expandOnCheck
	 * Expand the node on check if true. Defaults to true;
	 */
	,expandOnCheck:true

	/**
	 * @cfg {Boolean/Object} filter
	 * Set it to false to not create tree filter or set a custom filter. Defaults to true.
	 */
	,filter:true

	/**
	 * @cfg {String} separator
	 * Separator for convertValue function. Defaults to ',' (comma).
	 */
	,separator:','

	/**
	 * @cfg {String} cls
	 * Class to add to CheckTreePanel. A suitable css file must be included. Defaults to 'ux-checktree'.
	 */
	,cls:'ux-checktree'

	/**
	 * @cfg {Object} baseAttrs
	 * baseAttrs for loader. Defaults to {} (empty object).
	 */
	,baseAttrs:{}
	// }}}
	// {{{
	,initComponent:function() {

		// use our event model
		this.eventModel = new Ext.ux.tree.CheckTreeEventModel(this);

		// call parent initComponent
		Ext.ux.tree.CheckTreePanel.superclass.initComponent.apply(this, arguments);

		// pass this.baseAttrs and uiProvider down the line
		var baseAttrs = Ext.apply({uiProvider:Ext.ux.tree.CheckTreeNodeUI}, this.baseAttrs);
		Ext.applyIf(this.loader, {baseAttrs:baseAttrs, preloadChildren:true});

		// make sure that nodes are deeply preloaded
		if(true === this.loader.preloadChildren) {
			this.loader.on('load', function(loader, node) {
				node.cascade(function(n) {
					loader.doPreload(n);
					n.loaded = true;
				});
			});
		}

		// create tree filter
		if(true === this.filter) {
			var Filter = Ext.ux.tree.TreeFilterX ? Ext.ux.tree.TreeFilterX : Ext.tree.TreeFilter;
			this.filter = new Filter(this, {autoClear:true});
		}

	} // eo function initComponent
	// }}}
	// {{{
	/*
	 * get "value" (array of checked nodes ids)
	 * @return {Array} Array of chcecked nodes ids
	 */
	,getValue:function() {
		var a = [];
		this.root.cascade(function(n) {
			if(true === n.attributes.checked) {
				if(false === this.deepestOnly || !this.isChildChecked(n)) {
					a.push(n.id);
				}
			}
		}, this);
		return a;
	} // eo function getValue
	// {{{
	/**
	 * Helper function for getValue
	 * @param {Ext.tree.TreeNode} node
	 * @return {Boolean} true if node has a child checked, false otherwise
	 * @private
	 */
	,isChildChecked:function(node) {
		var checked = false;
		Ext.each(node.childNodes, function(child) {
			if(child.attributes.checked) {
				checked = true;
			}
		});
		return checked;
	} // eo function isChildChecked
	// }}}
	// }}}
	// {{{
	/*
	 * clears "value", unchecks all nodes
	 * @return {Ext.ux.tree.CheckTreePanel} this
	 */
	,clearValue:function() {
		this.root.cascade(function(n) {
			var ui = n.getUI();
			if(ui && ui.setChecked) {
				ui.setChecked(false);
			}
		});
		this.value = [];
		return this;
	} // eo function clearValue
	// }}}
	// {{{
	/*
	 * converts passed value to array
	 * @param {Mixed} val variable number of arguments, e.g. convertValue('1,8,7', 3, [9,4])
	 * @return {Array} converted value
	 */
	,convertValue:function(val) {
		// init return array
		var a = [];

		// calls itself recursively if necessary
		if(1 < arguments.length) {
			for(var i = 0; i < arguments.length; i++) {
				a.push(this.convertValue(arguments[i]));
			}
		}

		// nothing to do for arrays
		else if(Ext.isArray(val)) {
			a = val;
		}

		// just push numbers
		else if('number' === typeof val) {
			a.push(val);
		}

		// split strings
		else if('string' === typeof val) {
			a = val.split(this.separator);
		}

		return a;
	} // eo function convertValue
	// }}}
	// {{{
	/*
	 * Set nodes checked/unchecked
	 * @param {Mixed} val variable number of arguments, e.g. setValue('1,8,7', 3, [9,4])
	 * @return {Array} value. Array of checked nodes
	 */
	,setValue:function(val) {

		// uncheck all first
		this.clearValue();

		// process arguments
		this.value = this.convertValue.apply(this, arguments);

		// check nodes
		Ext.each(this.value, function(id) {
			var n = this.getNodeById(id);
			if(n) {
				var ui = n.getUI();
				if(ui && ui.setChecked) {
					ui.setChecked(true);

					// expand checked nodes
					if(true === this.expandOnCheck) {
						n.bubbleExpand();
					}
				}
			}
		}, this);

		return this.value;
	} // eo function setValue
	// }}}
	// {{{
	/*
	 * arbitrary attribute of checked nodes (text by default) is joined and separated with this.separator
	 * @param {String} attr Attribute to serialize
	 * @return {String} serialized attr of checked nodes
	 */
	,serialize:function(attr) {
		attr = attr || 'text';
		var a = [];
		this.root.cascade(function(n) {
			if(true === n.attributes.checked) {
				if(false === this.deepestOnly || !this.isChildChecked(n)) {
					a.push(n[attr]);
				}
			}
		}, this);
		return a.join(this.separator + ' ');
	} // eo function serialize
	// }}}
	// {{{
	/*
	 * alias for serialize function
	 * @param {String} attr Attribute to serialize
	 * @return {String} serialized attr of checked nodes
	 */
	,getText:function(attr) {
		return this.serialize(attr);
	} // eo function getText
	// }}}

}) // eo extend

Ext.reg('checktreepanel', Ext.ux.tree.CheckTreePanel);
// }}}
// {{{
/**
 * @private
 * @ignore
 */
Ext.override(Ext.tree.TreeNode, {
	/**
	 * Expands all parent nodes of this node
	 * @private
	 */
	bubbleExpand:function() {
		var root = this.getOwnerTree().root;
		var branch = [];
		var p = this;
		while(p !== root) {
			p = p.parentNode;
			branch.push(p);
		}
		branch.reverse();
		Ext.each(branch, function(n) {
			n.expand(false, false);
		});
	}
});
// }}}
// {{{
/**
 * @class Ext.ux.tree.CheckTreeNodeUI
 * @extends Ext.tree.TreeNodeUI
 *
 * Adds checkbox to the tree node UI. This class is not intended
 * to be instantiated explicitly; it is used internally in CheckTreePanel.
 *
 * @author   Ing. Jozef Sakáloš
 * @copyright (c) 2009, by Ing. Jozef Sakáloš
 * @version  1.0
 * @date     9. January 2009
 *
 * @license Ext.ux.tree.CheckTreeNodeUI is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
 * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
 */
/**
 * @constructor
 * @Creates new CheckTreeNodeUI
 * @param {Ext.tree.TreeNode} node Node to create the UI for
 */
Ext.ux.tree.CheckTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
	onDisableChange : function(node, state){
		Ext.ux.tree.CheckTreeNodeUI.superclass.onDisableChange.apply(this, arguments);

		var tree = this.node.getOwnerTree();
		var grayDisabled = tree.grayDisabled;

        if(state){
        	if ('checkbox' == grayDisabled) {
        		this.removeClass("x-tree-node-disabled");
            	this.addClass("x-tree-node-checkbox-disabled-only");
        	}
        }else{
        	if ('checkbox' == grayDisabled) {
            	this.removeClass("x-tree-node-checkbox-disabled-only");
        	}
        }
    },
    initEvents : function(){
    	Ext.ux.tree.CheckTreeNodeUI.superclass.initEvents.apply(this, arguments);

    	var tree = this.node.getOwnerTree();
		var grayDisabled = tree.grayDisabled;

        if(this.node.disabled){
        	if ('checkbox' == grayDisabled) {
        		this.removeClass("x-tree-node-disabled");
            	this.addClass("x-tree-node-checkbox-disabled-only");
        	}
        }
    },
	// {{{
	/**
	 * This is slightly adjusted original renderElements method
	 * @private
	 */
	renderElements:function(n, a, targetNode, bulkRender){

		this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() :'';
		var checked = n.attributes.checked;
		var href = a.href ? a.href : Ext.isGecko ? "" :"#";
        var buf = [
			 '<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">'
			,'<span class="x-tree-node-indent">',this.indentMarkup,"</span>"
			,'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />'
			,'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" :""),(a.iconCls ? " "+a.iconCls :""),'" unselectable="on" />'
			,'<img src="'+this.emptyIcon+'" class="x-tree-checkbox'+(true === checked ? ' x-tree-node-checked' :'')+'" />'
			,'<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" '
			,a.hrefTarget ? ' target="'+a.hrefTarget+'"' :"", '><span unselectable="on">',n.text,"</span></a></div>"
			,'<ul class="x-tree-node-ct" style="display:none;"></ul>'
			,"</li>"
		].join('');
		var nel;
		if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
			this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
		}else{
			this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
		}
		this.elNode = this.wrap.childNodes[0];
		this.ctNode = this.wrap.childNodes[1];
		var cs = this.elNode.childNodes;
		this.indentNode = cs[0];
		this.ecNode = cs[1];
		this.iconNode = cs[2];
		this.checkbox = cs[3];
		this.cbEl = Ext.get(this.checkbox);
		this.anchor = cs[4];
		this.textNode = cs[4].firstChild;
	} // eo function renderElements
	// }}}
	// {{{
	/**
	 * Sets iconCls
	 * @param {String} iconCls
	 * @private
	 */
	,setIconCls:function(iconCls) {
		Ext.fly(this.iconNode).set({cls:'x-tree-node-icon ' + iconCls});
	} // eo function setIconCls
	// }}}
	// {{{
	/**
	 * @return {Boolean} true if the node is checked, false otherwise
	 * @private
	 */
	,isChecked:function() {
		return this.node.attributes.checked === true;
	} // eo function isChecked
	// }}}
	// {{{
	/**
	 * Called when check changes
	 * @private
	 */
	,onCheckChange:function(propagationDirection) {
		var checked = this.isChecked();
		var tree = this.node.getOwnerTree();
		var bubble = tree.bubbleCheck;
		var cascade = tree.cascadeCheck;

		if ('undefined' == typeof(propagationDirection)) {
			propagationDirection = 'both';
		}

		if ('both' == propagationDirection || 'up' == propagationDirection) {
			if('all' === bubble || (checked && 'checked' === bubble) || (!checked && 'unchecked' === bubble)) {
				this.updateParent(checked, 'up');
			}
		}
		if ('both' == propagationDirection || 'down' == propagationDirection) {
			if('all' === cascade || (checked && 'checked' === cascade) || (!checked && 'unchecked' === cascade)) {
				this.updateChildren(checked, 'down');
			}
		}

		this.fireEvent('checkchange', this.node, checked);
	} // eo function onCheckChange
	// }}}
	// {{{
	/**
	 * Sets node UI checked/unchecked
	 * @param {Boolean} checked true to set node checked, false to uncheck
	 * @return {Boolean} checked
	 */
	,setChecked:function(checked, propagationDirection) {
		checked = true === checked ? checked : false;
		var cb = this.cbEl || false;
		if(cb) {
			true === checked ? cb.addClass('x-tree-node-checked') : cb.removeClass('x-tree-node-checked');
		}
		this.node.attributes.checked = checked;
		if(true === this.node.getOwnerTree().expandOnCheck && checked) {
			this.node.expand();
		}
		this.onCheckChange(propagationDirection);
		return checked;
	} // eo function setChecked
	// }}}
	// {{{
	/**
	 * Toggles check
	 * @return {Boolean} value after toggle
	 */
	,toggleCheck:function() {
		if(!this.node.disabled) {
			var checked = !this.isChecked();
			this.setChecked(checked);
			return checked;
		} else {
			return this.isChecked();
		}
	} // eo function toggleCheck
	// }}}
	// {{{
	/**
	 * Sets parents checked/unchecked. Used if bubbleCheck is not 'none'
	 * @param {Boolean} checked
	 * @private
	 */
	,updateParent:function(checked, propagationDirection) {
		var p = this.node.parentNode;
		var ui = p ? p.getUI() : false;

		if(ui && ui.setChecked) {
			ui.setChecked(checked, propagationDirection);
		}
	} // eo function updateParent
	// }}}
	// {{{
	/**
	 * Sets children checked/unchecked. Used if cascadeCheck is not 'none'
	 * @param {Boolean} checked
	 * @private
	 */
	,updateChildren:function(checked, propagationDirection) {
		this.node.eachChild(function(n) {
			var ui = n.getUI();
			if(ui && ui.setChecked) {
				ui.setChecked(checked, propagationDirection);
			}
		});
	} // eo function updateChildren
	// }}}
	// {{{
	/**
	 * Checkbox click event handler
	 * @private
	 */
	,onCheckboxClick:function() {
		if(!this.node.disabled) {
			this.toggleCheck();
		}
	} // eo function onCheckboxClick
	// }}}
	// {{{
	/**
	 * Checkbox over event handler
	 * @private
	 */
	,onCheckboxOver:function() {
		this.addClass('x-tree-checkbox-over');
	} // eo function onCheckboxOver
	// }}}
	// {{{
	/**
	 * Checkbox out event handler
	 * @private
	 */
	,onCheckboxOut:function() {
		this.removeClass('x-tree-checkbox-over');
	} // eo function onCheckboxOut
	// }}}
	// {{{
	/**
	 * Mouse down on checkbox event handler
	 * @private
	 */
	,onCheckboxDown:function() {
		this.addClass('x-tree-checkbox-down');
	} // eo function onCheckboxDown
	// }}}
	// {{{
	/**
	 * Mouse up on checkbox event handler
	 * @private
	 */
	,onCheckboxUp:function() {
		this.removeClass('x-tree-checkbox-down');
	} // eo function onCheckboxUp
	// }}}

}); // eo extend
// }}}
// {{{
/**
 * @class   Ext.ux.tree.CheckTreeEventModel
 * @extends Ext.tree.TreeEventModel
 *
 * Tree event model suitable for use with CheckTreePanel.
 * This class is not intended to be instantiated explicitly by a user
 * but it is used internally by CheckTreePanel.
 *
 * @author   Ing. Jozef Sakáloš
 * @copyright (c) 2009, by Ing. Jozef Sakáloš
 * @version  1.0
 * @date     9. January 2009
 *
 * @license Ext.ux.tree.CheckTreeEventModel is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
 * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
 */
/**
 * Create new CheckTreeEventModel
 * @constructor
 * @param {Ext.ux.tree.CheckTreePanel} tree The tree to apply this event model to
 */
Ext.ux.tree.CheckTreeEventModel = Ext.extend(Ext.tree.TreeEventModel, {
     initEvents:function(){
        var el = this.tree.getTreeEl();
        el.on('click', this.delegateClick, this);
        if(this.tree.trackMouseOver !== false){
            el.on('mouseover', this.delegateOver, this);
            el.on('mouseout', this.delegateOut, this);
        }

		el.on('mousedown', this.delegateDown, this);
		el.on('mouseup', this.delegateUp, this);

        el.on('dblclick', this.delegateDblClick, this);
        el.on('contextmenu', this.delegateContextMenu, this);

    }
	,delegateOver:function(e, t){
        if(!this.beforeEvent(e)){
            return;
        }
        if(this.lastEcOver){
            this.onIconOut(e, this.lastEcOver);
            delete this.lastEcOver;
        }
        if(this.lastCbOver){
            this.onCheckboxOut(e, this.lastCbOver);
            delete this.lastCbOver;
        }
        if(e.getTarget('.x-tree-ec-icon', 1)){
            this.lastEcOver = this.getNode(e);
            this.onIconOver(e, this.lastEcOver);
        }
        else if(e.getTarget('.x-tree-checkbox', 1)){
            this.lastCbOver = this.getNode(e);
            this.onCheckboxOver(e, this.lastCbOver);
        }
        if(t = this.getNodeTarget(e)){
            this.onNodeOver(e, this.getNode(e));
        }
    }
	,delegateOut:function(e, t){
        if(!this.beforeEvent(e)){
            return;
        }
        if(e.getTarget('.x-tree-ec-icon', 1)){
            var n = this.getNode(e);
            this.onIconOut(e, n);
            if(n == this.lastEcOver){
                delete this.lastEcOver;
            }
        }
        else if(e.getTarget('.x-tree-checkbox', 1)){
            var n = this.getNode(e);
            this.onCheckboxOut(e, n);
            if(n == this.lastCbOver){
                delete this.lastCbOver;
            }
        }
        if((t = this.getNodeTarget(e)) && !e.within(t, true)){
            this.onNodeOut(e, this.getNode(e));
        }
    }
	,delegateDown:function(e, t){
        if(e.getTarget('.x-tree-checkbox', 1)){
        	if(!this.beforeEvent(e)){
            	return false;
        	}
            this.onCheckboxDown(e, this.getNode(e));
        }
    }
	,delegateUp:function(e, t){
        if(e.getTarget('.x-tree-checkbox', 1)){
        	if(!this.beforeEvent(e)){
            	return false;
        	}
            this.onCheckboxUp(e, this.getNode(e));
        }
    }
	,delegateOut:function(e, t){
        if(!this.beforeEvent(e)){
            return;
        }
        if(e.getTarget('.x-tree-ec-icon', 1)){
            var n = this.getNode(e);
            this.onIconOut(e, n);
            if(n == this.lastEcOver){
                delete this.lastEcOver;
            }
        }
        else if(e.getTarget('.x-tree-checkbox', 1)){
            var n = this.getNode(e);
            this.onCheckboxOut(e, n);
            if(n == this.lastCbOver){
                delete this.lastCbOver;
            }
        }
        if((t = this.getNodeTarget(e)) && !e.within(t, true)){
            this.onNodeOut(e, this.getNode(e));
        }
    }
	,delegateClick:function(e, t){
		if(!this.beforeEvent(e)){
			return;
		}
		if(e.getTarget('.x-tree-checkbox', 1)){
			this.onCheckboxClick(e, this.getNode(e));
		}
		else if(e.getTarget('.x-tree-ec-icon', 1)){
			this.onIconClick(e, this.getNode(e));
		}
		else if(this.getNodeTarget(e)){
			this.onNodeClick(e, this.getNode(e));
		}
	}
	,onCheckboxClick:function(e, node){
		node.ui.onCheckboxClick();
	}
	,onCheckboxOver:function(e, node){
		node.ui.onCheckboxOver();
	}
	,onCheckboxOut:function(e, node){
		node.ui.onCheckboxOut();
	}
	,onCheckboxDown:function(e, node){
		node.ui.onCheckboxDown();
	}
	,onCheckboxUp:function(e, node){
		node.ui.onCheckboxUp();
	}
}); // eo extend
// }}}

// eof
