/*
 * SmartForms
 * Copyright(c) 2005-2008, ResLogic Inc. All rights reserved.
 */

Function.prototype.interceptResult = function(fcn, scope){
    if(typeof fcn != "function"){
        return this;
    }
    var method = this;
    var interception=function() {
        var retval = method.apply(this || window, arguments);
        var callArgs = Array.prototype.slice.call(arguments, 0);
		var args=[retval].concat(callArgs);
        var newRetval=fcn.apply(scope || this || window, args);
        return newRetval;
    };
    return interception;
};

Function.prototype.trapFunction = function(fcn, scope){
    var method = this, e, retval;
    var interception=function() {
    	try {
        	retval = method.apply(this || window, arguments);
    	}catch(e){
		    if(typeof fcn == "function"){
		        var callArgs = Array.prototype.slice.call(arguments, 0);
				var args=[e].concat(callArgs);
		        retval=fcn.apply(scope || this || window, args);
		    }
    	}
    	return retval;
    };
    return interception;
};

Ext.applyIf(Array.prototype, {
	each: function(f){
		for(var i = 0, len = this.length; i < len; i++){
			f(this[i]);
		}
	}
})

Ext.namespace('Ext.ux.panel');
 

Ext.ux.GMapPanel = Ext.extend(Ext.Panel, {
    
    border: false,

    
    respErrors: [
//    	{
//            code: 400, // G_GEO_BAD_REQUEST
//            msg: 'A directions request could not be successfully parsed. For example, the request may have been rejected if it contained more than the maximum number of waypoints allowed.' 
//        },{
//            code: G_GEO_SERVER_ERROR,
//            msg: 'A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known.'
//        },{
//            code: 601, // G_GEO_MISSING_QUERY
//            msg: 'The HTTP q parameter was either missing or had no value. For geocoding requests, this means that an empty address was specified as input. For directions requests, this means that no query was specified in the input.'
//        },{
//            code: G_GEO_MISSING_ADDRESS,
//            msg: 'Synonym for G_GEO_MISSING_QUERY.' 
//        },{
//            code: G_GEO_UNKNOWN_ADDRESS,
//            msg: 'No corresponding geographic location could be found for the specified address. This may be due to the fact that the address is relatively new, or it may be incorrect.' 
//        },{
//            code: G_GEO_UNAVAILABLE_ADDRESS,
//            msg: 'The geocode for the given address or the route for the given directions query cannot be returned due to legal or contractual reasons.' 
//        },{
//            code: 604, // G_GEO_UNKNOWN_DIRECTIONS
//            msg: 'The GDirections object could not compute directions between the points mentioned in the query. This is usually because there is no route available between the two points, or because we do not have data for routing in that region.'
//        },{
//            code: G_GEO_BAD_KEY,
//            msg: 'The given key is either invalid or does not match the domain for which it was given.' 
//        },{
//            code: G_GEO_TOO_MANY_QUERIES,
//            msg: 'The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. If you\'re sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don\'t send the requests too quickly.' 
//    	}	
    ],
    
    respErrorTitle : 'Error',
    
    geoErrorMsgUnable : 'Unable to Locate the Address you provided',
    
    geoErrorTitle : 'Address Location Error',
    
    geoErrorMsgAccuracy : 'The address provided has a low accuracy.<br><br>Level {0} Accuracy (8 = Exact Match, 1 = Vague Match)',
    
    gmapType : 'map',
    
    
    zoomLevel: 3,
    
    yaw: 180,
    
    pitch: 0,
    
    displayGeoErrors: false,
    
    minGeoAccuracy: 7,
    
    
    
    // private
    mapDefined: false,
    // private
    mapDefinedGMap: false,
    // private
    initComponent : function(){
        
        this.addEvents(
            
            'mapready'
        );
        Ext.ux.GMapPanel.superclass.initComponent.call(this);        

    },
    // private
    afterRender : function(){
        
        var wh = this.ownerCt.getSize();
        Ext.applyIf(this, wh);
        
        Ext.ux.GMapPanel.superclass.afterRender.call(this);    
        
        if (this.gmapType === 'map'){
            this.gmap = new GMap2(this.body.dom);
			this.mapDefined = true;
			this.mapDefinedGMap = true;
        }
        
        if (this.gmapType === 'panorama'){
            this.gmap = new GStreetviewPanorama(this.body.dom);
			this.mapDefined = true;
        }

		if (!this.mapDefined && this.gmapType){
			this.gmap = new GMap2(this.body.dom);
			this.gmap.setMapType(this.gmapType);
			this.mapDefined = true;
			this.mapDefinedGMap = true;
		}
        
        GEvent.bind(this.getMap(), 'load', this, this.onMapReady);
        
        if (typeof this.setCenter === 'object') {
            if (typeof this.setCenter.geoCodeAddr === 'string'){
            	
//            	//center map to default location while we are geolocation the address
//            	this.gmap.setCenter(new GLatLng(-0.010677, 51.47879), this.zoom);
                
                this.geoCodeLookup(this.setCenter.geoCodeAddr, this.setCenter.marker, false, true, this.setCenter.listeners);
            }else{
                if (this.gmapType === 'map'){
                    var point = this.fixLatLng(new GLatLng(this.setCenter.lat,this.setCenter.lng));
                    this.getMap().setCenter(point, this.zoomLevel);    
                }
                if (typeof this.setCenter.marker === 'object' && typeof point === 'object') {
                    this.addMarker(point, this.setCenter.marker, this.setCenter.marker.clear);
                }
            }
            if (this.gmapType === 'panorama') {
            	var setCenter = this.setCenter
            	
            	//if center point is not supplied, user is responsible to set it up himself later
                if (setCenter.lat && setCenter.lng)
                	this.getMap().setLocationAndPOV(new GLatLng(setCenter.lat, setCenter.lng), {yaw: this.yaw, pitch: this.pitch, zoom: this.zoomLevel});
            }
        }

    },
    // private
    onMapReady : function(){
        
        this.addMapControls();
        this.addOptions();
        
        this.addMarkers(this.markers);
        this.addKMLOverlay(this.autoLoadKML);
        
        this.fireEvent('mapready', this, this.getMap());
        
    },
    // private
    onResize : function(w, h){
        
        Ext.ux.GMapPanel.superclass.onResize.call(this, w, h);

        // check for the existance of the google map in case the onResize fires too early
        if (typeof this.getMap() == 'object') {
            this.getMap().checkResize();
        }

    },
    // private
    setSize : function(width, height, animate){
        
        Ext.ux.GMapPanel.superclass.setSize.call(this, width, height, animate);
        
        // check for the existance of the google map in case setSize is called too early
        if (typeof this.getMap() == 'object') {
            this.getMap().checkResize();
        }
        
    },
    
    getMap : function(){
        
        return this.gmap;
        
    },
    
    getCenter : function(){
        
        return this.fixLatLng(this.getMap().getCenter());
        
    },
    
    getCenterLatLng : function(){
        
        var ll = this.getCenter();
        return {lat: ll.lat(), lng: ll.lng()};
        
    },
    
    addMarkers : function(markers) {
        
        if (Ext.isArray(markers)){
            for (var i = 0; i < markers.length; i++) {
                if (typeof markers[i].geoCodeAddr == 'string') {
                    this.geoCodeLookup(markers[i].geoCodeAddr, markers[i].marker, false, markers[i].setCenter, markers[i].listeners);
                }else{
                    var mkr_point = this.fixLatLng(new GLatLng(markers[i].lat, markers[i].lng));
                    this.addMarker(mkr_point, markers[i].marker, false, markers[i].setCenter, markers[i].listeners);
                }
            }
        }
        
    },
    
    addMarker : function(point, marker, clear, center, listeners){
        
        Ext.applyIf(marker,G_DEFAULT_ICON);

        if (clear === true){
            this.getMap().clearOverlays();
        }
        if (center === true) {
            this.getMap().setCenter(point, this.zoomLevel);
        }

        var mark = new GMarker(point,marker);
        if (typeof listeners === 'object'){
            for (evt in listeners) {
                GEvent.bind(mark, evt, this, listeners[evt]);
            }
        }
        this.getMap().addOverlay(mark);

    },
    // private
    addMapControls : function(){
        
        if (this.gmapType === 'map') {
            if (Ext.isArray(this.mapControls)) {
                for(i=0;i<this.mapControls.length;i++){
                    this.addMapControl(this.mapControls[i]);
                }
            }else if(typeof this.mapControls === 'string'){
                this.addMapControl(this.mapControls);
            }else if(typeof this.mapControls === 'object'){
                this.getMap().addControl(this.mapControls);
            }
        }
        
    },
    
    addMapControl : function(mc){
        
        if (Ext.isObject(mc)) {
            this.getMap().addControl(mc);
        } else {
            var mcf = window[mc];
            if (typeof mcf === 'function') {
                this.getMap().addControl(new mcf());
            }
        }
        
    },
    // private
    addOptions : function(){
        
        if (Ext.isArray(this.mapConfOpts)) {
            var mc;
            for(i=0;i<this.mapConfOpts.length;i++){
                this.addOption(this.mapConfOpts[i]);
            }
        }else if(typeof this.mapConfOpts === 'string'){
            this.addOption(this.mapConfOpts);
        }        
        
    },
    
    addOption : function(mo){
        
        var mof = this.getMap()[mo];
        if (typeof mof === 'function') {
            this.getMap()[mo]();
        }    
        
    },
    
    addKMLOverlay : function(kmlfile){
        
        if (typeof kmlfile === 'string' && kmlfile !== '') {
            var geoXml = new GGeoXml(kmlfile);
            this.getMap().addOverlay(geoXml);
        }
        
    },
    
    geoCodeLookup : function(addr, marker, clear, center, listeners) {
        
        if (!this.geocoder) {
            this.geocoder = new GClientGeocoder();
        }
        this.geocoder.getLocations(addr, this.addAddressToMap.createDelegate(this, [addr, marker, clear, center, listeners], true));
        
    },
    // private
    addAddressToMap : function(response, addr, marker, clear, center, listeners){
        if (!response || response.Status.code != 200) {
            this.respErrorMsg(response.Status.code);
        }else{
            place = response.Placemark[0];
            addressinfo = place.AddressDetails;
            accuracy = addressinfo.Accuracy;
            if (accuracy === 0) {
                this.geoErrorMsg(this.geoErrorTitle, this.geoErrorMsgUnable);
            }else{
                if (accuracy < this.minGeoAccuracy) {
                    this.geoErrorMsg(this.geoErrorTitle, String.format(this.geoErrorMsgAccuracy, accuracy));
                }else{
                    point = this.fixLatLng(new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]));
                    if (center){
                        this.getMap().setCenter(point, this.zoomLevel);
                    }
                    if (typeof marker === 'object') {
                        if (!marker.title){
                            marker.title = place.address;
                        }
                        Ext.applyIf(marker, G_DEFAULT_ICON);
                        this.addMarker(point, marker, clear, false, listeners);
                    }
                }
            }
        }
        
    },
    // private
    geoErrorMsg : function(title,msg){
        if (this.displayGeoErrors) {
            Ext.MessageBox.alert(title,msg);
        }
    },
    // private
    respErrorMsg : function(code){
        Ext.each(this.respErrors, function(obj){
            if (code == obj.code){
                Ext.MessageBox.alert(this.respErrorTitle, obj.msg);
            }
        }, this);
    },
 	// private
	// used to inverse the lat/lng coordinates to correct locations on the sky map
	fixLatLng : function(llo){
        if (this.getMap().getCurrentMapType()) {
            if (this.getMap().getCurrentMapType().QO == 'visible') {
                llo.lat(180 - llo.lat());
                llo.lng(180 - llo.lng());
            }
        }
		return llo;
	}
});

Ext.reg('gmappanel',Ext.ux.GMapPanel); 

Ext.ux.SliderTip = Ext.extend(Ext.Tip, {
    minWidth: 10,
    offsets : [0, -10],
    init : function(slider){
        slider.on('dragstart', this.onSlide, this);
        slider.on('drag', this.onSlide, this);
        slider.on('dragend', this.hide, this);
        slider.on('destroy', this.destroy, this);
    },

    onSlide : function(slider){
        this.show();
        this.body.update(this.getText(slider));
        this.doAutoWidth();
        this.el.alignTo(slider.thumb, 'b-t?', this.offsets);
    },

    getText : function(slider){
        return slider.getValue();
    }
});

Ext.ux.GoogleMapIcons={
	saved:{},
	getBaseIcon:function(){
		if (this.baseIcon){
			return this.baseIcon;
		}
		var baseIcon= new GIcon();
		baseIcon.iconSize=new GSize(32,32);
		baseIcon.shadowSize=new GSize(56,32);
		baseIcon.iconAnchor=new GPoint(16,32);
		baseIcon.infoWindowAnchor=new GPoint(16,0);
		return this.baseIcon=baseIcon;
	},
	getShadowImage:function(iconName){
		iconName=iconName.toLowerCase();
		var image=this.common[iconName];
		var imageTemp=image.substr(0,image.length-4);
		imageTemp+='s.png';
		return imageTemp;
	},
	get:function(iconName){
		iconName=iconName.toLowerCase();
		if (this.saved[iconName]){
			return this.saved[iconName];
		}
		var icon=this.saved[iconName]=new GIcon(this.getBaseIcon());
		icon.image=this.common[iconName];
		icon.shadow=this.getShadowImage(iconName);
		return icon;
	},
	common:{
		airport: 'http://maps.google.com/mapfiles/kml/pal2/icon48.png',
		golf: 'http://maps.google.com/mapfiles/kml/pal2/icon5.png',
		golf2: 'http://maps.google.com/mapfiles/kml/pal2/icon13.png',
		car:'http://maps.google.com/mapfiles/kml/pal4/icon7.png',
		mountain:'http://maps.google.com/mapfiles/kml/pal3/con29.png',
		building:'http://maps.google.com/mapfiles/kml/pal3/icon21.png',
		tack:'http://maps.google.com/mapfiles/kml/pal5/icon14.png',
		downarrow:'http://maps.google.com/mapfiles/kml/pal5/icon6.png',
		1:'http://maps.google.com/mapfiles/kml/pal3/icon0.png',
		2:'http://maps.google.com/mapfiles/kml/pal3/icon1.png',
		3:'http://maps.google.com/mapfiles/kml/pal3/icon2.png',
		4:'http://maps.google.com/mapfiles/kml/pal3/icon3.png',
		5:'http://maps.google.com/mapfiles/kml/pal3/icon4.png',
		6:'http://maps.google.com/mapfiles/kml/pal3/icon5.png',
		7:'http://maps.google.com/mapfiles/kml/pal3/icon6.png',
		8:'http://maps.google.com/mapfiles/kml/pal3/icon7.png',
		9:'http://maps.google.com/mapfiles/kml/pal3/icon16.png',
		10:'http://maps.google.com/mapfiles/kml/pal3/icon17.png',
		a:'http://maps.google.com/mapfiles/kml/pal5/icon48.png',
		b:'http://maps.google.com/mapfiles/kml/pal5/icon49.png',
		c:'http://maps.google.com/mapfiles/kml/pal5/icon50.png',
		d:'http://maps.google.com/mapfiles/kml/pal5/icon51.png',
		e:'http://maps.google.com/mapfiles/kml/pal5/icon52.png',
		f:'http://maps.google.com/mapfiles/kml/pal5/icon53.png',
		g:'http://maps.google.com/mapfiles/kml/pal5/icon54.png',
		h:'http://maps.google.com/mapfiles/kml/pal5/icon55.png',
		i:'http://maps.google.com/mapfiles/kml/pal5/icon32.png',
		j:'http://maps.google.com/mapfiles/kml/pal5/icon33.png',
		k:'http://maps.google.com/mapfiles/kml/pal5/icon34.png',
		l:'http://maps.google.com/mapfiles/kml/pal5/icon35.png',
		m:'http://maps.google.com/mapfiles/kml/pal5/icon36.png',
		n:'http://maps.google.com/mapfiles/kml/pal5/icon37.png',
		o:'http://maps.google.com/mapfiles/kml/pal5/icon38.png',
		p:'http://maps.google.com/mapfiles/kml/pal5/icon39.png',
		q:'http://maps.google.com/mapfiles/kml/pal5/icon16.png',
		r:'http://maps.google.com/mapfiles/kml/pal5/icon17.png',
		s:'http://maps.google.com/mapfiles/kml/pal5/icon18.png',
		t:'http://maps.google.com/mapfiles/kml/pal5/icon19.png',
		u:'http://maps.google.com/mapfiles/kml/pal5/icon20.png',
		v:'http://maps.google.com/mapfiles/kml/pal5/icon21.png',
		w:'http://maps.google.com/mapfiles/kml/pal5/icon22.png',
		x:'http://maps.google.com/mapfiles/kml/pal5/icon23.png',
		y:'http://maps.google.com/mapfiles/kml/pal5/icon0.png',
		z:'http://maps.google.com/mapfiles/kml/pal5/icon1.png'
		
	}
	
}


Ext.ux.GoogleMapPanel = Ext.extend(Ext.Panel, {
	getMapTypes:function(){
		return this.mapTypeList || (this.mapTypeList={
			map: 		{ obj: G_NORMAL_MAP, defaultType:true },
			satellite: 	{ obj: G_SATELLITE_MAP, defaultType:true },
			hybrid: 	{ obj: G_HYBRID_MAP, defaultType:true },
			terrain: 	{ obj: G_PHYSICAL_MAP, defaultType:false }
		});		
	},

	controlTypes:function(){
		return this.controlTypeList || (this.controlTypeList={
			large: 		GLargeMapControl, //a large pan/zoom control used on Google Maps. Appears in the top left corner of the map by default
			small: 		GSmallMapControl, //a smaller pan/zoom control used on Google Maps. Appears in the top left corner of the map by default.
			zoom: 		GSmallZoomControl, //a small zoom control (no panning controls) used in the small map blowup windows used to display driving directions steps on Google Maps
			scale: 		GScaleControl, //a map scale
			maptype: 	GMapTypeControl, //buttons that let the user toggle between map types (such as Map and Satellite)
			menumaptype:GMenuMapTypeControl,
			hierarchicalmaptype: GHierarchicalMapTypeControl, //a selection of nested buttons and menu items for placing many map type selectors
			overview: 	GOverviewMapControl //a collapsible overview map in the corner of the screen
		});		
	},

	geocodingStatusMessage:function(){
		return this.geocodingStatusMessages || (this.geocodingStatusMessages={
        	G_GEO_SUCCESS:  			"Success",
        	G_GEO_MISSING_ADDRESS: 		"Missing Address: The address was either missing or had no value.",
        	G_GEO_UNKNOWN_ADDRESS: 		"Unknown Address:  No corresponding geographic location could be found for the specified address.",
        	G_GEO_UNAVAILABLE_ADDRESS: 	"Unavailable Address:  The geocode for the given address cannot be returned due to legal or contractual reasons.",
        	G_GEO_BAD_KEY: 				"Bad Key: The API key is either invalid or does not match the domain for which it was given",
        	G_GEO_TOO_MANY_QUERIES: 	"Too Many Queries: The daily geocoding quota for this site has been exceeded.",
        	G_GEO_SERVER_ERROR:			"Server error: The geocoding request could not be successfully processed."
		});		
	},


    constructor: function(config) {
        if (config.showZoomer){
            if (!config.tbar) config.tbar = [];
            config.tbar.unshift({
                xtype: 'tbtext',
                text: 'Zoom'
            }, this.zoomer = new Ext.Slider({
                width: 200,
                minValue: 1,
                maxValue: 17,
                listeners: {
                   change: this.onZoomSlide,
                   scope: this
                },
                plugins: new Ext.ux.SliderTip({
                    getText: function(slider){
                        return String.format('<b>Zoom: {0}</b>', slider.getValue());
                    }
                })
            }));
        }
        if (config.showNavPane){            	
            config.layout = 'border';
            config.items = [
            	this.navPanel = new Ext.Panel({
	                region: 'west',
	                title: 'Options',
	                plain: true,
	                collapsible: true,
	                width: 200,
	                collapseMode: 'mini',
	                split: true,
	                margins: '5 0 5 5',
	                cmargins: '5 0 5 0'
            	}), 
            	this.getMapPanel()
            ];
        }else{
			config.layout='anchor';
        	config.items=this.getMapPanel();
        }
        Ext.ux.GoogleMapPanel.superclass.constructor.apply(this, arguments);
    },
	getMapPanel:function(){
		return this.mapPanel = new Ext.Panel({
	        region: 'center',
	        anchor:'100% 100%',
	        margins: '5 5 5 0',
	        listeners: {
	            render: this.createMap,
	            resize: this.onMapContainerResize,
	            scope: this
	        }
	    });
		
	},

    createMap: function() {
		if (!window.GMap2){
			alert('Google maps are not loaded');
			return;
		}
        this.map = new GMap2(this.mapPanel.body.dom);
        if (!this.center) {
            this.center = {address: 'Stevensville, Md. 21666'};
        }

        this.zoom = this.zoom || 11;
        if (this.zoomer){
	        this.zoomer.suspendEvents();
	        this.zoomer.setValue(this.zoom);
	        this.zoomer.resumeEvents();
	    }

//      If we're going to have to geolocate the center, we must first
//      center the map on a default location
        if (typeof this.center == 'string') {
        	this.center={address:this.center};
        }
        if (this.center.address) {
            this.map.setCenter(new GLatLng(-0.010677, 51.47879), this.zoom);
            this.centerOn(this.center);
        } else {
        	var pos=this.getLatLng(this.center);
        	if (pos){
            	this.map.setCenter(pos, this.zoom);
            }
        }
        var mapTypeList=this.getMapTypes();

        if (this.mapType){ 
			if (typeof this.mapType == 'string'){
        		this.mapType=mapTypeList[this.mapType.toLowerCase()];
        		this.map.setMapType(this.mapType.obj);
        		if (!this.mapType.defaultType){ this.map.addMapType(this.mapType.obj) }
        		
        	}else{
        		this.map.setMapType( this.mapType );
        	}
        }

		if (this.mapTypes){

			var defaultMapTypes={};
			for (var mapTypeName in mapTypeList){
        		var mapType=mapTypeList[mapTypeName];
        		if (mapType.defaultType){ defaultMapTypes[mapTypeName]=false }				
			}
			
			Ext.each(this.mapTypes, function(mapTypeName){
        		var mapType=mapTypeList[mapTypeName.toLowerCase()];
        		if (typeof defaultMapTypes[mapTypeName] != 'undefined' ){
        			defaultMapTypes[mapTypeName]=true;
        		}else if (this.mapType != mapType ){
        			this.map.addMapType(mapType.obj)
        		}        						
			},this);			

			for (var mapTypeName in defaultMapTypes){
        		if (!defaultMapTypes[mapTypeName]){
	        		var mapType=mapTypeList[mapTypeName];
        			this.map.removeMapType(mapType.obj)
        		}
			}

		}
		
		if (this.controls){
			Ext.each(this.controls, function(control){
				if (typeof control == 'string'){
					this.map.addControl( new (this.controlTypes()[control.toLowerCase()]) );					
				}else{
					this.map.addControl(control);
				}
			},this);
		}

	    if (this.zoomer){
	        GEvent.addListener(this.map, 'zoomend', (function() {
	    	        this.zoomer.setValue(this.zoom = this.map.getZoom());
	        }).createDelegate(this));
    	}
        if (this.updateMarkers){
	        this.mapPanel.body.on({
	            contextmenu: this.onMapContextMenu,
	            click: function() {
	                if (this.mapContextMenu && this.mapContextMenu.isVisible()) {
	                    this.mapContextMenu.hide();
	                }
	            },
	            scope: this
	        });
	    }
        if (this.markers){
        	if (this.markers.length==1){
        		this.centerOn(this.markers[0]);
        	}
        	this.addMarkers(this.markers);
        }
    },

    onRender: function() {
        Ext.ux.GoogleMapPanel.superclass.onRender.apply(this, arguments);
        this.el.addClass('x-map-panel');
    },

    onMarkerWindowShow: function (marker, options) {
        this.getMapContextMenu().hide();
    },

    addMarker: function(latLng, options) {

    	if (typeof options.icon =='string'){
    		options.icon=Ext.ux.GoogleMapIcons.get(options.icon);
    	}
    	
        var marker = new GMarker(latLng, options);
        if (options.panel) {
            options.panel = this.lookupComponent(options.panel);
            if (!options.panel.rendered) {
                options.panel.render(Ext.getBody());
                options.panel.doLayout();
                options.panel.el.setStyle('margin-top', '10px');
                document.body.removeChild(options.panel.el.dom);
            }
            marker.bindInfoWindow(options.panel.el.dom, {
                onOpenFn: this.onMarkerWindowShow.createDelegate(this, marker, options)
            });
        }else if (options.html){
	        GEvent.addListener(marker, "click", function() {
	          marker.openInfoWindowHtml(options.html, options);
	        });
	    }


        this.map.addOverlay(marker);
        if (this.updateMarkers){
        	GEvent.addListener(marker, 'contextmenu', this.onMarkerContextMenu.createDelegate(this, [marker]));
        }
    },

    addGeolocatedMarker: function(result, markerSpec) {
        if (result.Status.code == G_GEO_SUCCESS) {
            var placemark = result.Placemark;
            for (var i = 0; i < placemark.length; i++) {
                var p = placemark[i].Point.coordinates;
                this.addMarker(new GLatLng(p[1], p[0]), markerSpec);
            }
        } else {
            //alert('Could not find "' + markerSpec.address + '"\n' + this.geocodingStatusMessage[result.Status.code]);
        }
    },


    onMapContainerResize: function() {
        if (this.map) {
            this.map.checkResize();
        }
    },

    onZoomSlide: function(s) {
        this.map.setZoom(this.zoom = s.getValue());
    },

    getMapContextMenu: function() {
        if (this.updateMarkers && !this.mapContextMenu) {
            this.mapContextMenu = new Ext.menu.Menu({
                items: [{
                    text: 'Add Marker',
                    iconCls: 'x-map-point-icon',
                    handler: function () {
                        var latLong = this.map.fromContainerPixelToLatLng({x: this.mapContextMenu.xy[0], y:this.mapContextMenu.xy[1]});
                        var m = new GMarker(latLong, {
                            draggable: true
                        });
                        this.addMarkers(m);
                    },
                    scope: this
                }, {
                    text: 'Center Map Here',
                    iconCls: 'x-cross-icon',
                    handler: function() {
                        var latLong = this.map.fromContainerPixelToLatLng({x: this.mapContextMenu.xy[0], y:this.mapContextMenu.xy[1]});
                        this.map.panTo(latLong);
                    },
                    scope: this
                }]
            });
	        return this.mapContextMenu;
        }
    },

    onMapContextMenu:function(e) {
        e.stopEvent();
        if (!e.getTarget('div.gmnoprint')) {
            this.getMapContextMenu();
            this.mapContextMenu.showAt(this.mapContextMenu.xy = e.getXY());
            this.mapContextMenu.xy[0] -= this.mapPanel.body.getX();
            this.mapContextMenu.xy[1] -= this.mapPanel.body.getY();
        }
    },

    onMarkerContextMenu:function(marker) {
        var p = marker.getPoint();
        p = this.map.fromLatLngToContainerPixel(p);
        p = [p.x + this.mapPanel.body.getX(), p.y + this.mapPanel.body.getY()];
        if (!this.markerContextMenu) {
            this.markerContextMenu = new Ext.menu.Menu({
                items: [{
                    text: 'Delete Marker',
                    iconCls: 'x-delete-icon',
                    handler: function() {
                        this.map.removeOverlay(this.markerContextMenu.marker);
                    },
                    scope: this
                }]
            });
        }
        this.markerContextMenu.marker = marker;
        this.markerContextMenu.showAt(this.markerContextMenu.xy = p);
    },

    addMarkers: function() {
        for (var i = 0, l = arguments.length; i < l; i++) {
            var m = arguments[i];
            if (Ext.isArray(m)) {
                this.addMarkers.apply(this, m);
            } else if (m) {
                if (typeof m == 'string') {
                    m = {address: m};
                }
                if (m instanceof GMarker) {
                    this.map.addOverlay(m);
                    if (this.updateMarkers){
                    	GEvent.addListener(m, 'contextmenu', this.onMarkerContextMenu.createDelegate(this, [m]));
                    }
                } else if (m.address) {
                    var g = new GClientGeocoder();
                    g.getLocations(m.address, this.addGeolocatedMarker.createDelegate(this, [m], true));
                } else {
                    var pos = this.getLatLng(m);
                    if (pos) {
                        this.addMarker(pos, m);
                    }
                }
            }
        }
    },

    
    centerOn: function(location, address) {
        if (typeof location == 'string') {
            location = { address: location };
        }
        if (location.address) {
            new GClientGeocoder().getLocations(location.address, this.centerOn.createDelegate(this, [location.address], true));
        } else {
	        var pos=this.getLatLng(location);
			if (!pos){
				this.hide();
				return;
			}
            this.map.panTo(pos);
        }
    },

    getLatLng: function(location) {
        if (location.Status) {
            if (location.Status.code == G_GEO_SUCCESS) {
               var p = location.Placemark[0].Point.coordinates;
               return new GLatLng(p[1], p[0]);
            } else {
				return;
                //Ext.MessageBox.alert('Could not find "' + location.name + '"\n' + this.geocodingStatusMessage()[location.Status.code]);
            }
        } else if ((typeof location.lat == 'number') && (typeof location.long == 'number')) {
            return new GLatLng(location.lat, location.long);
        } else if ((location.length > 1) && (typeof location[0] == 'number') && (typeof location[1] == 'number')) {
            return new GLatLng(location[0], location[1]);
        }
    }
});

Ext.reg('googlemap',Ext.ux.GoogleMapPanel);

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

Ext.ux.layout.Coolcard = Ext.extend(Ext.layout.CardLayout, {
    
    actionsQueue : undefined,
    
    constructor: function(config) {        
        var outFx = {
            method : 'raw',
            anchor : 't',
            
            easing: 'easeOut',
            duration: 0.6,
            remove: false,
            useDisplay: true
        };
        
        var inFx = {
            method : 'raw',
            anchor : 'b',
            
            easing: 'easeOut',
            duration: 0.8,
            remove: false,
            useDisplay: false
        };
        
        this.actionsQueue = [];
        
        if (!config) config = {};
        if (!config.outFx) config.outFx = {};
        if (!config.inFx) config.inFx = {};
        
        Ext.applyIf(config.outFx,outFx);
        Ext.applyIf(config.inFx,inFx);
        
        Ext.ux.layout.Coolcard.superclass.constructor.call(this, config);
    },
    
    
    setActiveItem: function(item,outFx,inFx){
        if (!outFx) outFx = {};
        if (!inFx) inFx = {};

        var cfg = {};

        cfg.item = item;        
        cfg.outFx = Ext.applyIf(outFx,this.outFx);
        cfg.inFx = Ext.applyIf(inFx,this.inFx);
        
        this.actionsQueue.push(cfg);
        
        if (this.actionsQueue.length == 1) {
            this.setActiveItemInternal();
        }
    },
    
    processQueue : function(){
        this.actionsQueue.shift();
        if (this.actionsQueue.length > 0) {
            this.setActiveItemInternal.defer(1,this);
        }
    },
    
    applyEffect : function (el,config) {
        var params = [];
        
        if (config.method == 'raw') {
            if (config.callback) {
            	config.callback.call(config.scope);
            }
            return;
        }
        
        if ( config.method == 'slideIn' || config.method == 'slideOut' || config.method == 'ghost') {
            params.push(config.anchor);
        }
        params.push(config);
        el[config.method].apply(el,params);
    },
    
    setActiveItemInternal : function(){
        var item = this.actionsQueue[0];
        var outFx = item.outFx;
        var inFx = item.inFx;
        item = item.item;
        
        item = this.container.getComponent(item);
        if ( this.activeItem != item ) {
            
            var ai = this.activeItem;
//	            var clipped_out = null;
//	            var clipped_in = null;

            if(ai){
                var aiIndex = this.container.items.indexOf(ai);
                
//	                this.container.remove(ai,false);
                this.container.items.remove(ai);
                
//	                clipped_out = ai.getEl().select('div{overflow-y=scroll}',true);
//	                clipped_out.clip();
//	                
//	                clipped_in = item.getEl().select('div{overflow-y=scroll}',true);
//	                clipped_in.clip();

                if (outFx.method != 'raw') {
                
	                var outLayer = Ext.getBody().createChild({
		            	tag : 'div',
		            	cls : 'x-background-underlay'
		            });
		            
//			            outLayer.applyStyles({
//			            	'z-index' : 
//			            });
	                
	                outLayer.alignTo(ai.getEl(),'tl-tl');
	                outLayer.appendChild(ai.getEl());
	                
	                this.applyEffect(outLayer,outFx);
                } else {
                	ai.hide();
                } 
                
            }
            
            this.activeItem = item;

            item.show();

            this.layout();
            
            if (item.doLayout) item.doLayout();
            
            inFx.scope = this;
            inFx.callback = function () {
                
                if (ai) {
                	ai.hide();
                	
//                    	this.container.add(ai);
                	this.container.items.insert(aiIndex, ai);
                	
//                    	this.container.getEl().appendChild(ai.getEl());
                	if (outFx.method != 'raw') {
                		this.container.getLayoutTarget().appendChild(ai.getEl());
                		outLayer.remove();
                	}
                }
                
                this.processQueue();
            };
            
            this.applyEffect(item.getEl(),inFx);                
            
            
//	            if (alreadyPresent) {
//	                
//	                var cb = function () {
////	                    clipped_out.unclip();
////	                    clipped_in.unclip();
////	                    ai.hide();
//	                    this.container.add(ai);
//						
//						//нужно после unclip в частности для тумбнэйлов
////						if (item.doLayout) item.doLayout();
//	                    
//	                    ai.hide();
//	                    this.container.getEl().appendChild(ai.getEl());
//	                    
//	                    if (outFx.method != 'raw') outLayer.remove();
//	                    
//	                    this.processQueue();
//	                };
//	                
//	                inFx.scope = this;
//	                inFx.callback = cb;
//	                
//	                this.applyEffect(item.getEl(),inFx);                
//	                
//	            } else {
//	                this.processQueue();
//	            }
        } else {
            this.processQueue();
        }
        
    },
    

    setContainer : function(ct){
    	Ext.ux.layout.Coolcard.superclass.setContainer.call(this, ct);
    	
        ct.on('beforeadd', this.setupItem, this);
    },
    
    
    setupItem : function (cont, comp, index) {
    	if (cont.items.items.length) {
    		comp.hidden = true;
    	}
    },

    
    onLayout : function(ct, target){
        Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);
        if(!this.container.collapsed){
            var item = this.activeItem || ct.items.itemAt(0);
            
            if (item) {
	            var size = target.getViewSize();
	            size.width -= target.getPadding('lr') + item.getEl().getMargins('lr');
	            size.height -= target.getPadding('tb') + item.getEl().getMargins('tb');
	            
	            this.setItemSize(item, size);
            }
        }
    }


}); //eof extend
Ext.Container.LAYOUTS['coolcard'] = Ext.ux.layout.Coolcard;

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

Ext.ux.layout.ColumnFit = Ext.extend(Ext.layout.ColumnLayout, {
    
    // private
    onLayout : function(ct, target){
        var cs = ct.items.items, len = cs.length, c, i;

        if(!this.innerCt){
            target.addClass('x-column-layout-ct');

            // the innerCt prevents wrapping and shuffling while
            // the container is resizing
            this.innerCt = target.createChild({cls:'x-column-inner'});
            this.innerCt.createChild({cls:'x-clear'});
            this.innerCt.setStyle( { 'height' : '100%', width : '100%' } );
        }
        this.renderAll(ct, this.innerCt);

        if (this.container.hidden) return;
        
        var size = target.getViewSize();

//	        if(size.width < 1 && size.height < 1){ // display none?
//	            return;
//	        }

        var w = size.width - target.getPadding('lr') - this.scrollOffset,
            h = size.height - target.getPadding('tb'),
            pw = w;

        for(i = 0; i < len; i++){
            c = cs[i];
            
            if (Ext.isIE6 && c.getEl().getMargins('lr')) c.el.setStyle({ display : 'inline' });
            
            if(!c.columnWidth){
                pw -= (c.getSize().width + c.getEl().getMargins('lr'));
            }
        }

        pw = pw < 0 ? 0 : pw;

        for(i = 0; i < len; i++){
            c = cs[i];
            if(c.columnWidth){
                c.setWidth(Math.floor(c.columnWidth*pw) - c.getEl().getMargins('lr'));
            }
        }
        
        //defer for IE6
        (function() {
	        var size = target.getViewSize();
	        var h = size.height - target.getPadding('tb');
	        
	        for(i = 0; i < len; i++){
	            c = cs[i];
	            
	            var heightValue = c.el.getStyle('height');
	            if ( heightValue != '100%' ) c.setHeight(h - c.getEl().getMargins('tb'));
	        }
        }).defer(1, this);
        
    }

}); //eof extend
Ext.Container.LAYOUTS['columnfit'] = Ext.ux.layout.ColumnFit;

(function () {

	var intercepted = {
	    'Ext.Container.prototype.initComponent' : Ext.Container.prototype.initComponent,
		'Ext.Component.prototype.beforeDestroy' : Ext.Component.prototype.beforeDestroy
	};
	
	
	Ext.override(Ext.Component, {
		beforeDestroy : function (){
			if (this.__SLOT__) {
				delete this.__COLLECTOR__.slots[this.__SLOT__];
				
				delete this.__SLOT__;
				delete this.__COLLECTOR__;
				delete this.siblingSlots;
			}
			
			intercepted['Ext.Component.prototype.beforeDestroy'].call(this);
		}
	});
	
	
    var setupSlots = function(){
        var self = this;
        
        var setup_slot_func = function (component){
            if (component.slot && component != self) {
                var has_slots = function (container, component) {
                    return Boolean(container.slots);
                }
                
                var parent_with_slots = component.findParentBy(has_slots);
                if (!parent_with_slots) return;
                
				parent_with_slots.slots[component.slot] = component;
				component.siblingSlots = parent_with_slots.slots; 
				component.__SLOT__ = component.slot; 
				component.__COLLECTOR__ = parent_with_slots;
				delete component.slot;
            }
        };
        
        this.cascade(setup_slot_func);
    };
        
    
	
    Ext.ns('Ext.ux.reference');
    
    Ext.ux.reference.Slot = {
	    
    	siblingSlots : undefined,
    	
        
    	slot : undefined,
    	
    	
    	slots : undefined
    };
    
    
    Ext.override(Ext.Container, {
    	slot : undefined,        
    	siblingSlots : undefined,        
        slots : undefined,        
        
        initComponent : function () {
            if (this.slots) this.slots = {};
            
            this.addEvents('add');
            this.on('add',setupSlots,this);

            intercepted['Ext.Container.prototype.initComponent'].call(this);
        }
	}); //eof override

})();
Ext.ns('Ext.ux.event'); 

Ext.override(Ext.util.Observable, { 

    subscribe: function(eventName, fn, scope, o) {
        Ext.ux.event.Broadcast.addEvents(eventName);
        Ext.ux.event.Broadcast.on(eventName, fn, scope, o);
    },
    
    publish : function() {        
        if(Ext.ux.event.Broadcast.eventsSuspended !== true){
            var ce = Ext.ux.event.Broadcast.events ? Ext.ux.event.Broadcast.events[arguments[0].toLowerCase()] : false;
            if(typeof ce == "object"){
                return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
            }
        }
        return true;
    },
    
    removeSubscriptionsFor : function(eventName) {        
        for(var evt in Ext.ux.event.Broadcast.events) {
            if ( (evt == eventName) || (!eventName) ) {            
                if(typeof Ext.ux.event.Broadcast.events[evt] == "object"){
                    Ext.ux.event.Broadcast.events[evt].clearListeners();
                }
            }
        }
    },
    
     hasSubscription : function (eventName) {
     	return Ext.ux.event.Broadcast.hasListener(eventName);
     }
    
});

Ext.ux.event.Broadcast = new Ext.util.Observable;

Ext.namespace('Ext.ux.form');

Ext.ux.form.SuperBoxSelect = function(config) {
    Ext.ux.form.SuperBoxSelect.superclass.constructor.call(this,config);
    this.addEvents(
        
        'beforeadditem',

        
        'additem',

        
        'newitem',

        
        'beforeremoveitem',

        
        'removeitem',
        
        'clear'
    );
    
};

Ext.ux.form.SuperBoxSelect = Ext.extend(Ext.ux.form.SuperBoxSelect,Ext.form.ComboBox,{
    
    allowAddNewData: false,

    
    backspaceDeletesLastItem: true,

    
    classField: null,

    
    clearBtnCls: '',

    
    displayFieldTpl: null,

    
    extraItemCls: '',

    
    extraItemStyle: '',

    
    expandBtnCls: '',

    
    fixFocusOnTabSelect: true,

    
    navigateItemsWithTab: true,

    
    pinList: true,

    
    preventDuplicates: true,
    
    
    queryValuesDelimiter: '|',
    
    
    queryValuesInidicator: 'valuesqry',

    
    removeValuesFromStore: true,

    
    renderFieldBtns: true,

    
    stackItems: false,

    
    styleField : null,
    
     
    supressClearValueRemoveEvents : false,
    
    
	validationEvent : 'blur',
	
    
    valueDelimiter: ',',
    initComponent:function() {
       Ext.apply(this, {
            items           : new Ext.util.MixedCollection(false),
            usedRecords     : new Ext.util.MixedCollection(false),
            addedRecords	: [],
            remoteLookup	: [],
            hideTrigger     : true,
            grow            : false,
            resizable       : false,
            multiSelectMode : false,
            preRenderValue  : null
        });
        
        if(this.transform){
            this.doTransform();
        }
        
        Ext.ux.form.SuperBoxSelect.superclass.initComponent.call(this);
        if(this.mode === 'remote' && this.store){
        	this.store.on('load', this.onStoreLoad, this);
        }
    },
    onRender:function(ct, position) {
        Ext.ux.form.SuperBoxSelect.superclass.onRender.call(this, ct, position);
        
        this.el.dom.removeAttribute('name');
        
        var extraClass = (this.stackItems === true) ? 'x-superboxselect-stacked' : '';
        if(this.renderFieldBtns){
            extraClass += ' x-superboxselect-display-btns';
        }
        this.el.removeClass('x-form-text').addClass('x-superboxselect-input-field');
        
        this.wrapEl = this.el.wrap({
            tag : 'ul'
        });
        
        this.outerWrapEl = this.wrapEl.wrap({
            tag : 'div',
            cls: 'x-form-text x-superboxselect ' + extraClass
        });
       
        this.inputEl = this.el.wrap({
            tag : 'li',
            cls : 'x-superboxselect-input'
        });
        
        if(this.renderFieldBtns){
            this.setupFieldButtons().manageClearBtn();
        }
        
        this.setupFormInterception();
        
        if(this.preRenderValue){
            this.setValue(this.preRenderValue);
            this.preRenderValue = null;
        }
    },
    onStoreLoad : function(store, records, options){
    	//accomodating for bug in Ext 3.0.0 where options.params are empty
    	var q = options.params[this.queryParam] || store.baseParams[this.queryParam] || "",
    		isValuesQuery = options.params[this.queryValuesInidicator] || store.baseParams[this.queryValuesInidicator];
    	
    	if(this.removeValuesFromStore){
    		this.store.each(function(record) {
				if(this.usedRecords.containsKey(record.get(this.valueField))){
					this.store.remove(record);
				}
			}, this);
    	}
    	//queried values
    	if(isValuesQuery){
    		var params = q.split(this.queryValuesDelimiter);
    		Ext.each(params,function(p){
    			this.remoteLookup.remove(p);
    			var rec = this.findRecord(this.valueField,p);
    			if(rec){
    				this.addRecord(rec);
    			}
    		},this);
    		
    		if(this.setOriginal){
    			this.setOriginal = false;
    			this.originalValue = this.getValue();
    		}
    	}

    	//queried display (autocomplete) & addItem
    	if(q !== '' && this.allowAddNewData){
    		Ext.each(this.remoteLookup,function(r){
    			if(typeof r == "object" && r[this.displayField] == q){
    				this.remoteLookup.remove(r);
					if(records.length && records[0].get(this.displayField) === q) {
						this.addRecord(records[0]);
						return;
					}
					var rec = this.createRecord(r);
					this.store.add(rec);
		        	this.addRecord(rec);
		        	this.addedRecords.push(rec); //keep track of records added to store
		        	(function(){
		        		if(this.isExpanded()){
			        		this.collapse();
		        		}
		        	}).defer(10,this);
		        	return;
    			}
    		},this);
    	}
    	
    	var toAdd = [];
    	if(q === ''){
	    	Ext.each(this.addedRecords,function(rec){
	    		if(this.preventDuplicates && this.usedRecords.containsKey(rec.get(this.valueField))){
					return;	    			
	    		}
	    		toAdd.push(rec);
	    		
	    	},this);
	    	
    	}else{
    		var re = new RegExp(Ext.escapeRe(q) + '.*','i');
    		Ext.each(this.addedRecords,function(rec){
	    		if(this.preventDuplicates && this.usedRecords.containsKey(rec.get(this.valueField))){
					return;	    			
	    		}
	    		if(re.test(rec.get(this.displayField))){
	    			toAdd.push(rec);
	    		}
	    	},this);
	    }
    	this.store.add(toAdd);
    	this.store.sort(this.displayField, 'ASC');
    	
		if(this.store.getCount() === 0 && this.isExpanded()){
			this.collapse();
		}
		
	},
    doTransform : function() {
    	var s = Ext.getDom(this.transform), transformValues = [];
            if(!this.store){
                this.mode = 'local';
                var d = [], opts = s.options;
                for(var i = 0, len = opts.length;i < len; i++){
                    var o = opts[i],
                        value = (Ext.isIE6 || Ext.isIE7 ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text,
                        cls = (Ext.isIE6 || Ext.isIE7 ? o.getAttributeNode('class').specified : o.hasAttribute('class')) ? o.className : '',
                        style = (Ext.isIE6 || Ext.isIE7 ? o.getAttributeNode('style').specified : o.hasAttribute('style')) ? o.style : '';
                    if(o.selected) {
                        transformValues.push(value);
                    }
                    d.push([value, o.text, cls, style.cssText]);
                }
                this.store = new Ext.data.SimpleStore({
                    'id': 0,
                    fields: ['value', 'text', 'cls', 'style'],
                    data : d
                });
                Ext.apply(this,{
                    valueField: 'value',
                    displayField: 'text',
                    classField: 'cls',
                    styleField: 'style'
                });
            }
           
            if(transformValues.length){
                this.value = transformValues.join(',');
            }
    },
    setupFieldButtons : function(){
        this.buttonWrap = this.outerWrapEl.createChild({
            cls: 'x-superboxselect-btns'
        });
        
        this.buttonClear = this.buttonWrap.createChild({
            tag:'div',
            cls: 'x-superboxselect-btn-clear ' + this.clearBtnCls
        });
        
        this.buttonExpand = this.buttonWrap.createChild({
            tag:'div',
            cls: 'x-superboxselect-btn-expand ' + this.expandBtnCls
        });
        
        this.initButtonEvents();
        
        return this;
    },
    initButtonEvents : function() {
        this.buttonClear.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
            e.stopEvent();
            if (this.disabled) {
                return;
            }
            this.clearValue();
            this.el.focus();
        }, this);

        this.buttonExpand.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
            e.stopEvent();
            if (this.disabled) {
                return;
            }
            if (this.isExpanded()) {
                this.multiSelectMode = false;
            } else if (this.pinList) {
                this.multiSelectMode = true;
            }
            this.onTriggerClick();
        }, this);
    },
    removeButtonEvents : function() {
        this.buttonClear.removeAllListeners();
        this.buttonExpand.removeAllListeners();
        return this;
    },
    clearCurrentFocus : function(){
        if(this.currentFocus){
            this.currentFocus.onLnkBlur();
            this.currentFocus = null;
        }  
        return this;        
    },
    initEvents : function() {
        var el = this.el;

        el.on({
            click   : this.onClick,
            focus   : this.clearCurrentFocus,
            blur    : this.onBlur,

            keydown : this.onKeyDownHandler,
            keyup   : this.onKeyUpBuffered,

            scope   : this
        });

        this.on({
            collapse: this.onCollapse,
            expand: this.clearCurrentFocus,
            scope: this
        });

        this.wrapEl.on('click', this.onWrapClick, this);
        this.outerWrapEl.on('click', this.onWrapClick, this);
        
        this.inputEl.focus = function() {
            el.focus();
        };

        Ext.ux.form.SuperBoxSelect.superclass.initEvents.call(this);

        Ext.apply(this.keyNav, {
            tab: function(e) {
                if (this.fixFocusOnTabSelect && this.isExpanded()) {
                    e.stopEvent();
                    el.blur();
                    this.onViewClick(false);
                    this.focus(false, 10);
                    return true;
                }

                this.onViewClick(false);
                if (el.dom.value !== '') {
                    this.setRawValue('');
                }

                return true;
            },

            down: function(e) {
                if (!this.isExpanded() && !this.currentFocus) {
                    this.onTriggerClick();
                } else {
                    this.inKeyMode = true;
                    this.selectNext();
                }
            },

            enter: function(){}
        });
    },

    onClick: function() {
        this.clearCurrentFocus();
        this.collapse();
        this.autoSize();
    },

    beforeBlur: Ext.form.ComboBox.superclass.beforeBlur,

    onFocus: function() {
        this.outerWrapEl.addClass(this.focusClass);

        Ext.ux.form.SuperBoxSelect.superclass.onFocus.call(this);
    },

    onBlur: function() {
        this.outerWrapEl.removeClass(this.focusClass);

        this.clearCurrentFocus();

        if (this.el.dom.value !== '') {
            this.applyEmptyText();
            this.autoSize();
        }

        Ext.ux.form.SuperBoxSelect.superclass.onBlur.call(this);
    },

    onCollapse: function() {
    	this.view.clearSelections();
        this.multiSelectMode = false;
    },

    onWrapClick: function(e) {
        e.stopEvent();
        this.collapse();
        this.el.focus();
        this.clearCurrentFocus();
    },
    markInvalid : function(msg) {
        var elp, t;

        if (!this.rendered || this.preventMark ) {
            return;
        }
        this.outerWrapEl.addClass(this.invalidClass);
        msg = msg || this.invalidText;

        switch (this.msgTarget) {
            case 'qtip':
                Ext.apply(this.el.dom, {
                    qtip    : msg,
                    qclass  : 'x-form-invalid-tip'
                });
                Ext.apply(this.wrapEl.dom, {
                    qtip    : msg,
                    qclass  : 'x-form-invalid-tip'
                });
                if (Ext.QuickTips) { // fix for floating editors interacting with DND
                    Ext.QuickTips.enable();
                }
                break;
            case 'title':
                this.el.dom.title = msg;
                this.wrapEl.dom.title = msg;
                this.outerWrapEl.dom.title = msg;
                break;
            case 'under':
                if (!this.errorEl) {
                    elp = this.getErrorCt();
                    if (!elp) { // field has no container el
                        this.el.dom.title = msg;
                        break;
                    }
                    this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
                    this.errorEl.setWidth(elp.getWidth(true) - 20);
                }
                this.errorEl.update(msg);
                Ext.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
                break;
            case 'side':
                if (!this.errorIcon) {
                    elp = this.getErrorCt();
                    if (!elp) { // field has no container el
                        this.el.dom.title = msg;
                        break;
                    }
                    this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
                }
                this.alignErrorIcon();
                Ext.apply(this.errorIcon.dom, {
                    qtip    : msg,
                    qclass  : 'x-form-invalid-tip'
                });
                this.errorIcon.show();
                this.on('resize', this.alignErrorIcon, this);
                break;
            default:
                t = Ext.getDom(this.msgTarget);
                t.innerHTML = msg;
                t.style.display = this.msgDisplay;
                break;
        }
        this.fireEvent('invalid', this, msg);
    },
    clearInvalid : function(){
        if(!this.rendered || this.preventMark){ // not rendered
            return;
        }
        this.outerWrapEl.removeClass(this.invalidClass);
        switch(this.msgTarget){
            case 'qtip':
                this.el.dom.qtip = '';
                this.wrapEl.dom.qtip ='';
                break;
            case 'title':
                this.el.dom.title = '';
                this.wrapEl.dom.title = '';
                this.outerWrapEl.dom.title = '';
                break;
            case 'under':
                if(this.errorEl){
                    Ext.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
                }
                break;
            case 'side':
                if(this.errorIcon){
                    this.errorIcon.dom.qtip = '';
                    this.errorIcon.hide();
                    this.un('resize', this.alignErrorIcon, this);
                }
                break;
            default:
                var t = Ext.getDom(this.msgTarget);
                t.innerHTML = '';
                t.style.display = 'none';
                break;
        }
        this.fireEvent('valid', this);
    },
    alignErrorIcon : function(){
        if(this.wrap){
            this.errorIcon.alignTo(this.wrap, 'tl-tr', [Ext.isIE ? 5 : 2, 3]);
        }
    },
    expand : function(){
        if (this.isExpanded() || !this.hasFocus) {
            return;
        }
        this.list.alignTo(this.outerWrapEl, this.listAlign).show();
        this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
        Ext.getDoc().on({
            mousewheel: this.collapseIf,
            mousedown: this.collapseIf,
            scope: this
        });
        this.fireEvent('expand', this);
    },
    restrictHeight : function(){
        var inner = this.innerList.dom,
            st = inner.scrollTop, 
            list = this.list;
        
        inner.style.height = '';
        
        var pad = list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight,
            h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
            ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
            hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
            space = Math.max(ha, hb, this.minHeight || 0)-list.shadowOffset-pad-5;
        
        h = Math.min(h, space, this.maxHeight);
        this.innerList.setHeight(h);

        list.beginUpdate();
        list.setHeight(h+pad);
        list.alignTo(this.outerWrapEl, this.listAlign);
        list.endUpdate();
        
        if(this.multiSelectMode){
            inner.scrollTop = st;
        }
    },
    validateValue: function(val){
        if(this.items.getCount() === 0){
             if(this.allowBlank){
                 this.clearInvalid();
                 return true;
             }else{
                 this.markInvalid(this.blankText);
                 return false;
             }
        }else{
            this.clearInvalid();
            return true;
        }
    },
    setupFormInterception : function(){
        var form;
        this.findParentBy(function(p){ 
            if(p.getForm){
                form = p.getForm();
            }
        });
        if(form){
            var formGet = form.getValues;
            form.getValues = function(asString){
                if(this.items.getCount() > 0){
                    this.el.dom.disabled = true;
                }
                var oldVal = this.el.dom.value;
                this.setRawValue('');
                var vals = formGet.call(form, asString);
                this.el.dom.disabled = false;
                this.setRawValue(oldVal);
                return vals;
            }.createDelegate(this);
        }
    },
    onResize : function(w, h, rw, rh) {
        var reduce = Ext.isIE6 ? 4 : Ext.isIE7 ? 1 : Ext.isIE8 ? 1 : 0;
        
        this._width = w;
        this.outerWrapEl.setWidth(w - reduce);
        if (this.renderFieldBtns) {
            reduce += (this.buttonWrap.getWidth() + 20);
            this.wrapEl.setWidth(w - reduce);
        }
        Ext.ux.form.SuperBoxSelect.superclass.onResize.call(this, w, h, rw, rh);
        this.autoSize();
    },
    onEnable: function(){
        Ext.ux.form.SuperBoxSelect.superclass.onEnable.call(this);
        this.items.each(function(item){
            item.enable();
        });
        if (this.renderFieldBtns) {
            this.initButtonEvents();
        }
    },
    onDisable: function(){
        Ext.ux.form.SuperBoxSelect.superclass.onDisable.call(this);
        this.items.each(function(item){
            item.disable();
        });
        if(this.renderFieldBtns){
            this.removeButtonEvents();
        }
    },
    
    clearValue : function(supressRemoveEvent){
        Ext.ux.form.SuperBoxSelect.superclass.clearValue.call(this);
        this.preventMultipleRemoveEvents = supressRemoveEvent || this.supressClearValueRemoveEvents || false;
    	this.removeAllItems();
        this.fireEvent('clear',this);
        return this;
    },
    onKeyUp : function(e) {
        if (this.editable !== false && !e.isSpecialKey() && (!e.hasModifier() || e.shiftKey)) {
            this.lastKey = e.getKey();
            this.dqTask.delay(this.queryDelay);
        }        
    },
    onKeyDownHandler : function(e,t) {
    	    	
        var toDestroy,nextFocus,idx;
        if ((e.getKey() === e.DELETE || e.getKey() === e.SPACE) && this.currentFocus){
            e.stopEvent();
            toDestroy = this.currentFocus;
            this.on('expand',function(){this.collapse();},this,{single: true});
            idx = this.items.indexOfKey(this.currentFocus.key);
            
            this.clearCurrentFocus();
            
            if(idx < (this.items.getCount() -1)){
                nextFocus = this.items.itemAt(idx+1);
            }
            
            toDestroy.preDestroy(true);
            if(nextFocus){
                (function(){
                    nextFocus.onLnkFocus();
                    this.currentFocus = nextFocus;
                }).defer(200,this);
            }
        
            return true;
        }
        
        var val = this.el.dom.value, it, ctrl = e.ctrlKey;
        if(e.getKey() === e.ENTER){
            e.stopEvent();
            if (val !== "") {
                if (ctrl || !this.isExpanded())  {  //ctrl+enter for new items
                	this.view.clearSelections();
                    this.collapse();
                    this.setRawValue('');
                    this.fireEvent('newitem', this, val);
                }
                else {
                	this.onViewClick();
                    //removed from 3.0.1
                    if(this.unsetDelayCheck){
                        this.delayedCheck = true;
                        this.unsetDelayCheck.defer(10, this);
                    }
                }
            }else{
                if(!this.isExpanded()){
                    return;
                }
                this.onViewClick();
                //removed from 3.0.1
                if(this.unsetDelayCheck){
                    this.delayedCheck = true;
                    this.unsetDelayCheck.defer(10, this);
                }
            }
            return true;
        }
        
        if(val !== '') {
            this.autoSize();
            return;
        }
        
        //select first item
        if(e.getKey() === e.HOME){
            e.stopEvent();
            if(this.items.getCount() > 0){
                this.collapse();
                it = this.items.get(0);
                it.el.focus();
                
            }
            return true;
        }
        //backspace remove
        if(e.getKey() === e.BACKSPACE){
            e.stopEvent();
            if(this.currentFocus) {
                toDestroy = this.currentFocus;
                this.on('expand',function(){
                    this.collapse();
                },this,{single: true});
                
                idx = this.items.indexOfKey(toDestroy.key);
                
                this.clearCurrentFocus();
                if(idx < (this.items.getCount() -1)){
                    nextFocus = this.items.itemAt(idx+1);
                }
                
                toDestroy.preDestroy(true);
                
                if(nextFocus){
                    (function(){
                        nextFocus.onLnkFocus();
                        this.currentFocus = nextFocus;
                    }).defer(200,this);
                }
                
                return;
            }else{
                it = this.items.get(this.items.getCount() -1);
                if(it){
                    if(this.backspaceDeletesLastItem){
                        this.on('expand',function(){this.collapse();},this,{single: true});
                        it.preDestroy(true);
                    }else{
                        if(this.navigateItemsWithTab){
                            it.onElClick();
                        }else{
                            this.on('expand',function(){
                                this.collapse();
                                this.currentFocus = it;
                                this.currentFocus.onLnkFocus.defer(20,this.currentFocus);
                            },this,{single: true});
                        }
                    }
                }
                return true;
            }
        }
        
        if(!e.isNavKeyPress()){
            this.multiSelectMode = false;
            this.clearCurrentFocus();
            return;
        }
        //arrow nav
        if(e.getKey() === e.LEFT || (e.getKey() === e.UP && !this.isExpanded())){
            e.stopEvent();
            this.collapse();
            //get last item
            it = this.items.get(this.items.getCount()-1);
            if(this.navigateItemsWithTab){ 
                //focus last el
                if(it){
                    it.focus(); 
                }
            }else{
                //focus prev item
                if(this.currentFocus){
                    idx = this.items.indexOfKey(this.currentFocus.key);
                    this.clearCurrentFocus();
                    
                    if(idx !== 0){
                        this.currentFocus = this.items.itemAt(idx-1);
                        this.currentFocus.onLnkFocus();
                    }
                }else{
                    this.currentFocus = it;
                    if(it){
                        it.onLnkFocus();
                    }
                }
            }
            return true;
        }
        if(e.getKey() === e.DOWN){
            if(this.currentFocus){
                this.collapse();
                e.stopEvent();
                idx = this.items.indexOfKey(this.currentFocus.key);
                if(idx == (this.items.getCount() -1)){
                    this.clearCurrentFocus.defer(10,this);
                }else{
                    this.clearCurrentFocus();
                    this.currentFocus = this.items.itemAt(idx+1);
                    if(this.currentFocus){
                        this.currentFocus.onLnkFocus();
                    }
                }
                return true;
            }
        }
        if(e.getKey() === e.RIGHT){
            this.collapse();
            it = this.items.itemAt(0);
            if(this.navigateItemsWithTab){ 
                //focus first el
                if(it){
                    it.focus(); 
                }
            }else{
                if(this.currentFocus){
                    idx = this.items.indexOfKey(this.currentFocus.key);
                    this.clearCurrentFocus();
                    if(idx < (this.items.getCount() -1)){
                        this.currentFocus = this.items.itemAt(idx+1);
                        if(this.currentFocus){
                            this.currentFocus.onLnkFocus();
                        }
                    }
                }else{
                    this.currentFocus = it;
                    if(it){
                        it.onLnkFocus();
                    }
                }
            }
        }
    },
    onKeyUpBuffered : function(e){
        if(!e.isNavKeyPress()){
            this.autoSize();
        }
    },
    reset :  function(){
        Ext.ux.form.SuperBoxSelect.superclass.reset.call(this);
        this.addedRecords = [];
        this.autoSize().setRawValue('');
        this.el.focus();
    },
    applyEmptyText : function(){
        if(this.items.getCount() > 0){
            this.el.removeClass(this.emptyClass);
            this.setRawValue('');
            return this;
        }
        if(this.rendered && this.emptyText && this.getRawValue().length < 1){
            this.setRawValue(this.emptyText);
            this.el.addClass(this.emptyClass);
        }
        return this;
    },
    
    removeAllItems: function(){
    	this.items.each(function(item){
            item.preDestroy(true);
        },this);
        this.manageClearBtn();
        return this;
    },
    resetStore: function(){
        this.store.clearFilter();
        if(!this.removeValuesFromStore){
            return this;
        }
        this.usedRecords.each(function(rec){
            this.store.add(rec);
        },this);
        this.sortStore();
        return this;
    },
    sortStore: function(){
        var ss = this.store.getSortState();
        if(ss && ss.field){
            this.store.sort(ss.field, ss.direction);
        }
        return this;
    },
    getCaption: function(dataObject){
        if(typeof this.displayFieldTpl === 'string') {
            this.displayFieldTpl = new Ext.XTemplate(this.displayFieldTpl);
        }
        var caption, recordData = dataObject instanceof Ext.data.Record ? dataObject.data : dataObject;
      
        if(this.displayFieldTpl) {
            caption = this.displayFieldTpl.apply(recordData);
        } else if(this.displayField) {
            caption = recordData[this.displayField];
        }
        
        return caption;
    },
    addRecord : function(record) {
        var display = record.data[this.displayField],
            caption = this.getCaption(record),
            val = record.data[this.valueField],
            cls = this.classField ? record.data[this.classField] : '',
            style = this.styleField ? record.data[this.styleField] : '';

        if (this.removeValuesFromStore) {
            this.usedRecords.add(val, record);
            this.store.remove(record);
        }
        
        this.addItemBox(val, display, caption, cls, style);
        this.fireEvent('additem', this, val, record);
    },
    createRecord : function(recordData){
        if(!this.recordConstructor){
            var recordFields = [
                {name: this.valueField},
                {name: this.displayField}
            ];
            if(this.classField){
                recordFields.push({name: this.classField});
            }
            if(this.styleField){
                recordFields.push({name: this.styleField});
            }
            this.recordConstructor = Ext.data.Record.create(recordFields);
        }
        return new this.recordConstructor(recordData);
    },
    
    addItems : function(newItemObjects){
    	if (Ext.isArray(newItemObjects)) {
			Ext.each(newItemObjects, function(item) {
				this.addItem(item);
			}, this);
		} else {
			this.addItem(newItemObjects);
		}
    },
    
    addItem : function(newItemObject){
        
        var val = newItemObject[this.valueField];

        if(this.disabled) {
            return false;
        }
        if(this.preventDuplicates && this.hasValue(val)){
            return;
        }
        
        //use existing record if found
        var record = this.findRecord(this.valueField, val);
        if (record) {
            this.addRecord(record);
            return;
        } else if (!this.allowAddNewData) { // else it's a new item
            return;
        }
        
        if(this.mode === 'remote'){
        	this.remoteLookup.push(newItemObject); 
        	this.doQuery(val,false,false);
        	return;
        }
        
        var rec = this.createRecord(newItemObject);
        this.store.add(rec);
        this.addRecord(rec);
        
        return true;
    },
    addItemBox : function(itemVal,itemDisplay, itemCaption, itemClass, itemStyle) {
        var parseStyle = function(s){
            var ret = '';
            if(typeof s == 'function'){
                ret = s.call();
            }else if(typeof s == 'object'){
                for(var p in s){
                    ret+= p +':'+s[p]+';';
                }
            }else if(typeof s == 'string'){
                ret = s + ';';
            }
            return ret;
        };
        var itemKey = Ext.id(null,'sbx-item');
        var box = new Ext.ux.form.SuperBoxSelectItem({
            owner: this,
            disabled: this.disabled,
            renderTo: this.wrapEl,
            cls: this.extraItemCls + ' ' + itemClass,
            style: parseStyle(this.extraItemStyle) + ' ' + itemStyle,
            caption: itemCaption,
            display: itemDisplay,
            value:  itemVal,
            key: itemKey,
            listeners: {
                'remove': function(item){
                    if(this.fireEvent('beforeremoveitem',this,item.value) === false){
                        return;
                    }
                    this.items.removeKey(item.key);
                    if(this.removeValuesFromStore){
                        if(this.usedRecords.containsKey(item.value)){
                            this.store.add(this.usedRecords.get(item.value));
                            this.usedRecords.removeKey(item.value);
                            this.sortStore();
                            if(this.view){
                                this.view.render();
                            }
                        }
                    }
                    if(!this.preventMultipleRemoveEvents){
                    	this.fireEvent.defer(250,this,['removeitem',this,item.value, this.findInStore(item.value)]);
                    }
                    this.preventMultipleRemoveEvents = false;
                },
                destroy: function(){
                    this.collapse();
                    this.autoSize().manageClearBtn().validateValue();
                },
                scope: this
            }
        });
        box.render();

        box.hidden = this.el.insertSibling({
            tag:'input', 
            type:'hidden', 
            value: itemVal,
            name: (this.hiddenName || this.name)
        },'before');

        this.items.add(itemKey,box);
        this.applyEmptyText().autoSize().manageClearBtn().validateValue();
    },
    manageClearBtn : function() {
        if (!this.renderFieldBtns || !this.rendered) {
            return this;
        }
        var cls = 'x-superboxselect-btn-hide';
        if (this.items.getCount() === 0) {
            this.buttonClear.addClass(cls);
        } else {
            this.buttonClear.removeClass(cls);
        }
        return this;
    },
    findInStore : function(val){
        var index = this.store.find(this.valueField, val.trim());
        if(index > -1){
            return this.store.getAt(index);
        }
        return false;
    },
    
    getValue : function() {
        var ret = [];
        this.items.each(function(item){
            ret.push(item.value);
        });
        return ret.join(this.valueDelimiter);
    },
    
    getValueEx : function() {
        var ret = [];
        this.items.each(function(item){
            var newItem = {};
            newItem[this.valueField] = item.value;
            newItem[this.displayField] = item.display;
            newItem[this.classField]= item.cls;
            ret.push(newItem);
        },this);
        return ret;
    },
    // private
    initValue : function(){
 
        Ext.ux.form.SuperBoxSelect.superclass.initValue.call(this);
        if(this.mode === 'remote') {
        	this.setOriginal = true;
        }
    },
    
    setValue : function(value){
        if(!this.rendered){
            this.preRenderValue = value;
            return;
        }
        
        var values = Ext.isArray(value) ? value : value.split(this.valueDelimiter);
        this.removeAllItems().resetStore();
        
        //reset remoteLookup because setValue should overwrite everything
        //inc pending data
        this.remoteLookup = [];
        
        Ext.each(values,function(val){
            var record = this.findRecord(this.valueField, val);
            if(record){
                this.addRecord(record);
            }else if(this.mode === 'remote'){
				this.remoteLookup.push(val);            	
            }
        },this);
        
        if(this.mode === 'remote'){
      		var q = this.remoteLookup.join(this.queryValuesDelimiter); 
      		this.doQuery(q,false, true); //3rd param to specify a values query
        }
        
    },
    
    setValueEx : function(data){
        this.removeAllItems().resetStore();
        
        if(!Ext.isArray(data)){
            data = [data];
        }
        Ext.each(data,function(item){
            this.addItem(item);
        },this);
    },
    
    hasValue: function(val){
        var has = false;
        this.items.each(function(item){
            if(item.value == val){
                has = true;
                return false;
            }
        },this);
        return has;
    },
    onSelect : function(record, index) {
        var val = record.data[this.valueField];
        
        if(this.preventDuplicates && this.hasValue(val)){
            return;
        }
        
        this.setRawValue('');
        this.lastSelectionText = '';
        
        if(this.fireEvent('beforeadditem',this,val) !== false){
            this.addRecord(record);
        }
        if(this.store.getCount() === 0 || !this.multiSelectMode){
            this.collapse();
        }else{
            this.restrictHeight();
        }
    },
    onDestroy : function() {
        this.items.each(function(item) {
            item.preDestroy(true);
        }, this);

        if (this.renderFieldBtns) {
            Ext.destroy(
                this.buttonClear,
                this.buttonExpand,
                this.buttonWrap
            );
        }

        Ext.destroy(
            this.inputEl,
            this.wrapEl,
            this.outerWrapEl
        );

        Ext.ux.form.SuperBoxSelect.superclass.onDestroy.call(this);
    },
    autoSize : function(){
        if(!this.rendered){
            return this;
        }
        if(!this.metrics){
            this.metrics = Ext.util.TextMetrics.createInstance(this.el);
        }
        var el = this.el,
            v = el.dom.value,
            d = document.createElement('div');

        if(v === "" && this.emptyText && this.items.getCount() < 1){
            v = this.emptyText;
        }
        d.appendChild(document.createTextNode(v));
        v = d.innerHTML;
        d = null;
        v += "&#160;";
        var w = Math.max(this.metrics.getWidth(v) +  24, 24);
        if(typeof this._width != 'undefined'){
            w = Math.min(this._width, w);
        }
        this.el.setWidth(w);
        
        if(Ext.isIE){
            this.el.dom.style.top='0';
        }
        return this;
    },
    doQuery : function(q, forceAll,valuesQuery){
        q = Ext.isEmpty(q) ? '' : q;
        var qe = {
            query: q,
            forceAll: forceAll,
            combo: this,
            cancel:false
        };
        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
            return false;
        }
        q = qe.query;
        forceAll = qe.forceAll;
        if(forceAll === true || (q.length >= this.minChars)){
            if(this.lastQuery !== q){
            	this.lastQuery = q;
                if(this.mode == 'local'){
                    this.selectedIndex = -1;
                    if(forceAll){
                        this.store.clearFilter();
                    }else{
                        this.store.filter(this.displayField, q);
                    }
                    this.onLoad();
                }else{
                	
                    this.store.baseParams[this.queryParam] = q;
                    this.store.baseParams[this.queryValuesInidicator] = valuesQuery;
                    this.store.load({
                        params: this.getParams(q)
                    });
                    this.expand();
                }
            }else{
                this.selectedIndex = -1;
                this.onLoad();
            }
        }
    }
});
Ext.reg('superboxselect', Ext.ux.form.SuperBoxSelect);

Ext.ux.form.SuperBoxSelectItem = function(config){
    Ext.apply(this,config);
    Ext.ux.form.SuperBoxSelectItem.superclass.constructor.call(this); 
};

Ext.ux.form.SuperBoxSelectItem = Ext.extend(Ext.ux.form.SuperBoxSelectItem,Ext.Component, {
    initComponent : function(){
        Ext.ux.form.SuperBoxSelectItem.superclass.initComponent.call(this); 
    },
    onElClick : function(e){
        var o = this.owner;
        o.clearCurrentFocus().collapse();
        if(o.navigateItemsWithTab){
            this.focus();
        }else{
            o.el.dom.focus();
            var that = this;
            (function(){
                this.onLnkFocus();
                o.currentFocus = this;
            }).defer(10,this);
        }
    },
    
    onLnkClick : function(e){
        if(e) {
            e.stopEvent();
        }
        this.preDestroy();
        if(!this.owner.navigateItemsWithTab){
            this.owner.el.focus();
        }
    },
    onLnkFocus : function(){
        this.el.addClass("x-superboxselect-item-focus");
        this.owner.outerWrapEl.addClass("x-form-focus");
    },
    
    onLnkBlur : function(){
        this.el.removeClass("x-superboxselect-item-focus");
        this.owner.outerWrapEl.removeClass("x-form-focus");
    },
    
    enableElListeners : function() {
        this.el.on('click', this.onElClick, this, {stopEvent:true});
       
        this.el.addClassOnOver('x-superboxselect-item x-superboxselect-item-hover');
    },

    enableLnkListeners : function() {
        this.lnk.on({
            click: this.onLnkClick,
            focus: this.onLnkFocus,
            blur:  this.onLnkBlur,
            scope: this
        });
    },
    
    enableAllListeners : function() {
        this.enableElListeners();
        this.enableLnkListeners();
    },
    disableAllListeners : function() {
        this.el.removeAllListeners();
        this.lnk.un('click', this.onLnkClick, this);
        this.lnk.un('focus', this.onLnkFocus, this);
        this.lnk.un('blur', this.onLnkBlur, this);
    },
    onRender : function(ct, position){
        
        Ext.ux.form.SuperBoxSelectItem.superclass.onRender.call(this, ct, position);
        
        var el = this.el;
        if(el){
            el.remove();
        }
        
        this.el = el = ct.createChild({ tag: 'li' }, ct.last());
        el.addClass('x-superboxselect-item');
        
        var btnEl = this.owner.navigateItemsWithTab ? ( Ext.isSafari ? 'button' : 'a') : 'span';
        var itemKey = this.key;
        
        Ext.apply(el, {
            focus: function(){
                var c = this.down(btnEl +'.x-superboxselect-item-close');
                if(c){
                	c.focus();
                }
            },
            preDestroy: function(){
                this.preDestroy();
            }.createDelegate(this)
        });
        
        this.enableElListeners();

        el.update(this.caption);

        var cfg = {
            tag: btnEl,
            'class': 'x-superboxselect-item-close',
            tabIndex : this.owner.navigateItemsWithTab ? '0' : '-1'
        };
        if(btnEl === 'a'){
            cfg.href = '#';
        }
        this.lnk = el.createChild(cfg);
        
        
        if(!this.disabled) {
            this.enableLnkListeners();
        }else {
            this.disableAllListeners();
        }
        
        this.on({
            disable: this.disableAllListeners,
            enable: this.enableAllListeners,
            scope: this
        });

        this.setupKeyMap();
    },
    setupKeyMap : function(){
        new Ext.KeyMap(this.lnk, [
            {
                key: [
                    Ext.EventObject.BACKSPACE, 
                    Ext.EventObject.DELETE, 
                    Ext.EventObject.SPACE
                ],
                fn: this.preDestroy,
                scope: this
            }, {
                key: [
                    Ext.EventObject.RIGHT,
                    Ext.EventObject.DOWN
                ],
                fn: function(){
                    this.moveFocus('right');
                },
                scope: this
            },
            {
                key: [Ext.EventObject.LEFT,Ext.EventObject.UP],
                fn: function(){
                    this.moveFocus('left');
                },
                scope: this
            },
            {
                key: [Ext.EventObject.HOME],
                fn: function(){
                    var l = this.owner.items.get(0).el.focus();
                    if(l){
                        l.el.focus();
                    }
                },
                scope: this
            },
            {
                key: [Ext.EventObject.END],
                fn: function(){
                    this.owner.el.focus();
                },
                scope: this
            },
            {
                key: Ext.EventObject.ENTER,
                fn: function(){
                }
            }
        ]).stopEvent = true;
    },
    moveFocus : function(dir) {
        var el = this.el[dir == 'left' ? 'prev' : 'next']() || this.owner.el;
	
        el.focus.defer(100,el);
    },

    preDestroy : function(supressEffect) {
    	if(this.fireEvent('remove', this) === false){
	    	return;
	    }	
    	var actionDestroy = function(){
            if(this.owner.navigateItemsWithTab){
                this.moveFocus('right');
            }
            this.hidden.remove();
            this.hidden = null;
            this.destroy();
        };
        
        if(supressEffect){
            actionDestroy.call(this);
        } else {
            this.el.hide({
                duration: 0.2,
                callback: actionDestroy,
                scope: this
            });
        }
        return this;
    },
    onDestroy : function() {
        Ext.destroy(
            this.lnk,
            this.el
        );
        
        Ext.ux.form.SuperBoxSelectItem.superclass.onDestroy.call(this);
    }
});
Ext.ns('Ext.ux.layout');

Ext.ux.layout.Slide = Ext.extend(Ext.layout.FitLayout, {
    
    deferredRender : false,
    
    renderHidden : false,
    easing: 'none',
    duration: .5,
    opacity: 1,
	activeItemNo : 0,
	compatMode: false,
	
	
    setContainer : function(ct){
        Ext.ux.layout.Slide.superclass.setContainer.call(this, ct);
        
        ct.addEvents('activeitemchanged');
    },


    onLayout : function (ct, target) {
		Ext.ux.layout.Slide.superclass.onLayout.call(this, ct, target);
		
		if (ct.items.getCount() && !this.activeItem) {
			this.activeItem = ct.items.itemAt(0);
		}
	},
    
    
    setActiveItem : function(itemInt){
        if (typeof(itemInt) == 'string') { 
        	itemInt = this.container.items.keys.indexOf(itemInt); 
        } else if (typeof(itemInt) == 'object') { 
        	itemInt = this.container.items.items.indexOf(itemInt); 
        }
        
        var item = this.container.getComponent(itemInt);
        
        if (this.activeItem != item) {
			if (this.activeItem) {
				if (this.compatMode || Ext.isOpera){
					this.activeItem.el.setStyle('display','none');
					item.el.setStyle('display','block');
				}else{
				
						
					if(item && (!item.rendered || !this.isValidParent(item, this.container))){
						this.renderItem(item, itemInt, this.container.getLayoutTarget()); item.show();
					}
					var s = [this.container.body.getX() - this.container.body.getWidth(), this.container.body.getX() + this.container.body.getWidth()];
					this.activeItem.el.shift({ duration: this.duration, easing: this.easing, opacity: this.opacity, x:(this.activeItemNo < itemInt ? s[0] : s[1] )});
					item.el.setY(this.container.body.getY());
					item.el.setX((this.activeItemNo < itemInt ? s[1] : s[0] ));
					item.el.shift({ duration: this.duration, easing: this.easing, opacity: 1, x:this.container.body.getX()});
				}
			}
            
            this.activeItemNo = itemInt;
            this.activeItem = item;
            this.layout();
            this.container.fireEvent('activeitemchanged', this.activeItem);
        }
    },

    
    renderAll : function(ct, target){
        if(this.deferredRender){
            this.renderItem(this.activeItem, undefined, target);
        }else{
            Ext.ux.layout.Slide.superclass.renderAll.call(this, ct, target);
        }
    }
    
}); //eof extend

Ext.Container.LAYOUTS['slide'] = Ext.ux.layout.Slide; 
//layout:'template'   sample implementation

//baseCls: 'x-plain',
//labelWidth: 55,
//layout: 'template',
//tpl: '<table>' + 
//    '<tr><td>Name (First/Last)</td><td>{first:field} &nbsp; {last:field}</td></tr>' +
//    '<tr><td>{[values.company.fieldLabel]}</td><td>{company:field}</td></tr>' +
//    '<tr><td>Email</td><td>{email:field}</td></tr>' +
//    '<tr><td>Time</td><td>{time:field}</td></tr>' +
//    '</table>',
//defaults: {width: 230},
//defaultType: 'textfield',
//items: [{
//        fieldLabel: 'First Name',
//        name: 'first',
//        allowBlank:false
//    },{
//        fieldLabel: 'Last Name',
//        name: 'last'
//    },{
//        fieldLabel: 'Company',
//        name: 'company'


Ext.util.Format.field = function(fld) { 
    return (!fld ? '' : '<span id="' + (fld.name || fld.id) + '-tpl-wrap"></span>')
};

Ext.ux.TemplateLayout = Ext.extend(Ext.layout.ContainerLayout, {

    setContainer : function(ct){
        Ext.ux.TemplateLayout.superclass.setContainer.apply(this, arguments);
        ct.el.addClass('x-form-template-layout');
    },

    renderAll : function(ct, target){

        var it = ct.items.items;

//		this allows a beforeRender event to change the tpl		
        if (!this.tpl) {
            this.tpl = ct.tpl;
        }
        if (typeof this.tpl == 'string') {
            this.tpl = new Ext.XTemplate(this.tpl);
        }else if (Ext.isArray(this.tpl)) {
			this.tpl = new Ext.XTemplate(this.tpl.join(''));
        }

//      Create the templated output. Fields are represented by
//      spans before which they are then rendered.
        var fields = {};
        for (var i = 0, l = it.length; i < l; i++) {
            var fld = it[i];
            fields[fld.name || fld.id] = fld;
        }
        this.tpl.overwrite(ct.body, fields);

//      Render the fields before the spans.
//      If they have already been rendered, the field's "el" will
//      be re-inserted if the rendered flag is cleared.
//      Remove each field's span once the field has been rendered.
        for (var i = 0, l = it.length; i < l; i++) {
            var fld = it[i];
            fld.rendered = false; 
            
			

            var pos = Ext.getDom((fld.name || fld.id) + '-tpl-wrap');
            if (pos){
	            fld.render(pos.parentNode, pos);
	            Ext.fly(pos).remove();
	        }
        }
    }
});
Ext.Container.LAYOUTS['template'] = Ext.ux.TemplateLayout;
Ext.namespace('Ext.ux.layout');



Ext.ux.layout.RowFitLayout = Ext.extend(Ext.layout.ContainerLayout, {
    // private
    monitorResize: true,

    // private
    trackChildEvents: ['collapse', 'expand', 'hide', 'show'],
    
    
    monitorChildren : true,
    disableCollapseAnimation : true,
    

    // private
    splitHeight: 5,
    rendered: false,

    // private
    renderItem: function(c, position, target) {
        Ext.ux.layout.RowFitLayout.superclass.renderItem.apply(this, arguments);

        // add event listeners
        for (var i = 0, n = this.trackChildEvents.length; i < n; i++) {
            var ev = this.trackChildEvents[i];
            //c.on(this.trackChildEvents[i], this.itemListener, this);
            c.on(ev, this['_item_' + ev], this);
        }
        if (this.disableCollapseAnimation) c.animCollapse = false; // looks ugly together with row-fit layout

        this.checkRelHeight(c);
    },

    checkRelHeight: function(c) {
    	
    	if (c.rowFit && c.height !== c.rowFit.height) {
    		delete c.rowFit;
    	}

        // store some layout-specific calculations
        if (!c.rowFit) {
            c.rowFit = {
                hasAbsHeight: false,
                // whether the component has absolute height (in pixels)
                relHeight: 0,
                // relative height, in pixels (if applicable)
                calcRelHeight: 0,
                // calculated relative height (used when element is resized)
                calcAbsHeight: 0,
                // calculated absolute height
                height: c.height // save height config
            };
            
            var height = c.height;

            if (typeof height == "string" && /\%/.test(height) ) {
//            // store relative (given in percent) height
                c.rowFit.relHeight = parseInt(height);
            } else if (typeof height == "number") { // set absolute height
//                c.setHeight(c.height);
                //c.rowFit.hasAbsHeight = true;
                c.rowFit.hasAbsHeight = !Boolean(c.split);
            }
            
        }

//        // process height config option
//        if (c.height !== c.rowFit.height) {
//        	c.rowFit.height = c.height;
//        	
//            var height = c.height;
//            if (typeof height == "string" && height.indexOf("%")) {
//                c.rowFit.relHeight = parseInt(height);
//            } else { // set absolute height
//                c.setHeight(c.height);
//                //c.rowFit.hasAbsHeight = true;
//                c.rowFit.hasAbsHeight = !Boolean(c.split);
//            }
//        }
        //if(c.split) c.rowFit.hasAbsHeight = false;
        c.isResizable = c.isResizable || Boolean(c.split) || !c.rowFit.hasAbsHeight;
    },

    // private
    onLayout: function(ct, target) {

        Ext.ux.layout.RowFitLayout.superclass.onLayout.call(this, ct, target);

        if (this.container.collapsed || !ct.items || !ct.items.length) {
            return;
        }

        // first loop: determine how many elements with relative
        // height are there,
        // sums of absolute and relative heights etc.
        
        var absHeightSum = 0,
        // sum of elements' absolute heights
        
        relHeightSum = 0,
        // sum of all percent heights given in children configs
        
        relHeightRatio = 1,
        // "scale" ratio used in case sum <> 100%
        
        noHeightCount = 0,
        // number of elements with no height given
        
        relHeightElements = []; // array of elements with 'relative' height for the second loop

//        var targetSize = target.getStyleSize();
		var targetSize = target.getViewSize();
        targetSize.width -= target.getPadding('lr');
        
        for (var i = 0, n = ct.items.length; i < n; i++) {
            var c = ct.items.itemAt(i);
            
            this.checkRelHeight(c);

            if (!c.isVisible()) continue;

            // collapsed panel is treated as an element with absolute height
            if (c.collapsed) {
                var h = c.getEl().getHeight();//c.getFrameHeight();
                absHeightSum += h;
                c.setWidth(targetSize.width - c.getEl().getMargins('lr'));
            } else if (c.rowFit.hasAbsHeight) { // element that has an absolute height
            	
                absHeightSum += c.height;
                
                c.setSize({
                    width: targetSize.width - c.getEl().getMargins('lr'),
                    height: c.height
                });

            } else if (c.rowFit.relHeight) { // 'relative-heighted'
            	
                relHeightSum += c.rowFit.relHeight;
                relHeightElements.push(c);
                
            } else if (c.autoHeight) {
            	
            	absHeightSum += c.getEl().getHeight() + c.getEl().getMargins('tb');
            	c.setWidth(targetSize.width - c.getEl().getMargins('lr'));
            	
            } else {// element with no height given
                
                noHeightCount++;
                absHeightSum += c.getFrameHeight ? c.getFrameHeight() : 0;
                if (Ext.isIE6) {
                    c.setSize({
                        width: targetSize.width - c.getEl().getMargins('lr'),
                        height: 0
                    });
                } else 
                	c.setWidth(targetSize.width - c.getEl().getMargins('lr'));
                
                relHeightElements.push(c);
                
            }
            
        }

        // if sum of relative heights <> 100% (e.g. error in config or consequence of collapsing/removing panels), scale 'em so it becomes 100%
        if (noHeightCount == 0 && relHeightSum != 100) {
            relHeightRatio = 100 / relHeightSum;
        }
        
        targetSize = target.getViewSize();
        targetSize.height -= target.getPadding('tb');
        
        var freeHeight = targetSize.height - absHeightSum,       // "unallocated" height we have
        absHeightLeft = freeHeight; // track how much free space we have

        while (relHeightElements.length) {
            var c = relHeightElements.shift();
            // element we're working with
            
            var relH = c.rowFit.relHeight * relHeightRatio;
            // height of this element in percent
            
            var absH = 0; // height in pixels

            // no height in config
            if (!relH) {
                relH = (100 - relHeightSum) / noHeightCount;
            } 

            // last element takes all remaining space
            if (!relHeightElements.length) {
                absH = absHeightLeft;
            } else {
                absH = Math.floor(freeHeight * relH / 100);
            }

            // anyway, height can't be negative
            if (absH < 0) absH = 0;

            c.rowFit.calcAbsHeight = absH;
            c.rowFit.calcRelHeight = relH;
            c.setSize({
                width: targetSize.width - c.getEl().getMargins('lr'),
                height: absH
            });

            absHeightLeft -= absH;
        }

        for (var i = 0,n = ct.items.length; i < n; i++) {
            
            var c = ct.items.itemAt(i);
            
            if (c.isSlider && c.el2resize) {
                // this.checkRelHeight(c);
                var split = new Ext.SplitBar(c.el, c.el2resize.el, Ext.SplitBar.VERTICAL, Ext.SplitBar.TOP);
                split.setAdapter(new Ext.ux.layout.RowFitLayout.SplitAdapter(split));
                c.el2resize.sliderId = c.getId();
                c.el2resize = false;
            }
        }

        if (!this.rendered) { // keep watching for changes of items
            ct.on('add', this.ctAddItem, this);
            ct.on('remove', this.ctDelItem, this);
            this.rendered = true;
        }
    },
    // private - called from Ext.Container
    setContainer: function(ct) {
        Ext.ux.layout.RowFitLayout.superclass.setContainer.call(this, ct);
        this._addSliders(ct);
    },

    // private
    _addSliders: function(ct) {
        var sh = ct.splitHeight || this.splitHeight;
        var skip1 = true;
        var n = ct.items.length;
        for (var i = n - 1; i >= 0; i--) {
            var c = ct.items.itemAt(i);

            this.checkRelHeight(c);
            if (c.isResizable) { // !c.rowFit.hasAbsHeight) {
                if (skip1) {
                    skip1 = false;
                    continue;
                }

                if (c.split) {
                    var slider = new Ext.Panel({
                        height: sh,
                        isSlider: true
                    });
                    slider.el2resize = c;
                    slider.addClass('x-splitbar-y');
                    ct.insert(i + 1, slider);
                }
            }
        }
    },

    
    itemListener: function(item) {
        item.ownerCt.doLayout();
    },
    _item_show: function(comp) {
    	if (!this.monitorChildren) return;
    	
        if (!comp.isSlider && comp.sliderId) {
            var sl = comp.ownerCt.findById(comp.sliderId);
            if (!sl.isVisible()) {
                sl.show();
                return;
            }
        }
        comp.ownerCt.doLayout();
    },
    _item_hide: function(comp) {
    	if (!this.monitorChildren) return;
    	
        if (!comp.isSlider && comp.sliderId) {
            var sl = comp.ownerCt.findById(comp.sliderId);
            if (sl.isVisible()) {
                sl.hide();
                return;
            }
        }
        comp.ownerCt.doLayout();
    },
    _item_expand: function(comp) {
    	if (!this.monitorChildren) return;
    	
        this._item_show(comp);
    },
    _item_collapse: function(comp) {
    	if (!this.monitorChildren) return;
    	
        this._item_hide(comp);
    },

    
    ctAddItem: function(ct, comp, idx) {
        // TODO: ev. add slider & splitbar
        ct.doLayout();
    },
    ctDelItem: function(ct, comp) {
        // TODO: ev. remove slider & splitbar
        ct.doLayout();
    }

});

// Split adapter
if (Ext.SplitBar.BasicLayoutAdapter) {

    
    Ext.ux.layout.RowFitLayout.SplitAdapter = function(splitbar) {
        if (splitbar && splitbar.el.dom.nextSibling) {
            var next = Ext.getCmp(splitbar.el.dom.nextSibling.id),
            resized = Ext.getCmp(splitbar.resizingEl.id);

            // skip abs-height non-resizable components
            while (next && (next.collapsed || !next.isVisible() || !next.isResizable)) {
                next = Ext.getCmp(next.el.dom.nextSibling.id);
            }

            if (next) {
                //splitbar.maxSize = (resized.height || resized.rowFit.calcAbsHeight) +
                splitbar.maxSize = (resized.rowFit.hasAbsHeight ? resized.rowFit.calcAbsHeight: resized.getSize().height) + next.getInnerHeight() - 1; // seems can't set height=0 in IE, "1" works fine
            }
            splitbar.minSize = resized.getFrameHeight() + 1;
        }
    };

    Ext.extend(Ext.ux.layout.RowFitLayout.SplitAdapter, Ext.SplitBar.BasicLayoutAdapter, {

        setElementSize: function(splitbar, newSize, onComplete) {
            var resized = Ext.getCmp(splitbar.resizingEl.id);

            // can't resize absent, collapsed or hidden panel
            if (!resized || resized.collapsed || !resized.isVisible()) {
                return;
            }

            // resizingEl has absolute height: just change it
            if (resized.rowFit.hasAbsHeight) {
                resized.setHeight(newSize);
            }
            // resizingEl has relative height: affects next sibling
            else {
                if (splitbar.el.dom.nextSibling) {
                    var nextSibling = Ext.getCmp(splitbar.el.dom.nextSibling.id);
                    // skip abs-height non-resizable components
                    while (nextSibling && (nextSibling.collapsed || !nextSibling.isVisible() || !nextSibling.isResizable)) {
                        nextSibling = Ext.getCmp(nextSibling.el.dom.nextSibling.id);
                    }

                    var deltaAbsHeight = newSize - resized.rowFit.calcAbsHeight,
                    // pixels
                    nsRf = nextSibling.rowFit,
                    // shortcut
                    rzRf = resized.rowFit,
                    // pixels in a percent
                    pctPxRatio = rzRf.calcRelHeight / rzRf.calcAbsHeight,
                    deltaRelHeight = pctPxRatio * deltaAbsHeight; // change in height in percent

                    rzRf.relHeight = rzRf.calcRelHeight + deltaRelHeight;

                    if (nsRf.hasAbsHeight) {
                        var newHeight = nextSibling.height - deltaAbsHeight;
                        nextSibling.height = newHeight;
                        nextSibling.setHeight(newHeight);
                    } else {
                        nsRf.relHeight = nsRf.calcRelHeight - deltaRelHeight;
                    }
                }
            }
            // recalculate heights
            resized.ownerCt.doLayout();
        } // of setElementSize

    }); // of SplitAdapter
}

Ext.Container.LAYOUTS['row-fit'] = Ext.ux.layout.RowFitLayout;
Ext.ns('Ext.ux.window'); 


Ext.ux.window.MessageWindowGroup = function (config) {
    config = config || {};
    var mgr = new Ext.WindowGroup();
    mgr.positions = [];
    Ext.apply(mgr, config);
    return mgr;
};


Ext.ux.window.MessageWindowMgr = Ext.ux.window.MessageWindowGroup(); 


Ext.ux.window.MessageWindow = Ext.extend(Ext.Window, {

    
    hideAction: 'close',

    
    autoHide: true,

    
    autoHeight: false,
			
    
    bodyStyle: {
		'text-align':'left',
		padding:'10px'
	},

    

    
    buttonAlign: 'center',

    
    cls: 'x-notification',

    
    constrain: true,

    
    constrainHeader: true,

    
    draggable: true,

    
    
    floating: true,

    

    
    
    frame: true,
    
    

    
    handleHelp: Ext.emptyFn,

    
    help: true,

    
    hideFx: {
        delay: 5000
    },

    
    hoverCls: 'msg-over',

    
    iconCls: 'x-icon-information',

    

    

    
    minHeight: 40,

    
    minWidth: 200,

    

    
    msgs: [],

    
    monitorResize : true,

    

    

    
    pinOnClick: true,

    
    pinState: 'unpin',

    
    plain: false,
    
    
    resizable: false,

    

    
    textHelp: 'Get help',

    
    textPin: 'Pin this to prevent closing',

    
    textUnpin: 'Unpin this to close',

    

    

    
    initHidden : true,

    
    initComponent : function () {

        Ext.apply(this, {
            collapsible: false,
            footer: false,
            minHeight: 20,
            stateful: false
        });
        
        //if interval is specified automatically show message windows
        if (this.interval) {
            this.startAutoRefresh();
        } 
        //set up automatic hide/close of window if so configured
        if (this.autoHide) {
            if (this.pinState === 'unpin') {
                this.task = new Ext.util.DelayedTask( this[this.hideAction], this, [this.animateTarget]);
            }
        } 
//new
        else {
			this.closable = true;
		}
//added this.closable
        
        //call parent
        Ext.ux.window.MessageWindow.superclass.initComponent.call(this);

        //add listeners
        this.on({
            mouseout: {
                scope: this,
                fn: this.onMouseout
            }
        });

        //add events
        this.addEvents(
            
            
            
            
            
            
            
            'afterpin',
            
            'afterunpin',
            
            'click');

        this.initFx();
    },

    //override
    
    initEvents: function () {
        
        //use a slighly enhanced Ext.ux.window.MessageWindowMgr instead of the default WindowMgr
        this.manager = this.manager || Ext.ux.window.MessageWindowMgr;

        //the parent class will register, so no need to do it here:
        //this.manager = this.manager || Ext.WindowMgr;
        
        Ext.ux.window.MessageWindow.superclass.initEvents.call(this);
    },

    focus: function () {
        Ext.ux.window.MessageWindow.superclass.focus.call(this);
    },
    
    
    toFront: function () {
        if(this.manager.bringToFront(this)){
            //only focus if configured as such
            if(this.focusOnShow){
                this.focus();
            }
        }
        return this;
    },

    
    initTools: function () {
        if (this.pinOnClick) {
            this.addTool({
                id: 'unpin', // image points left
                handler: this.handlePin,
                //set initial visibility (also check if pinState is null)
                hidden: (!this.pinState || this.pinState === 'pin'), 
                qtip: this.textPin,
                scope: this
            });
    
            this.addTool({
                id: 'pin',// image points down
                handler: this.handleUnpin,
                hidden: (!this.pinState || this.pinState === 'unpin'), 
                qtip: this.textUnpin,
                scope: this
            });
        }
        if (this.help) {
            this.addTool({
                id: 'help',
                handler: this.handleHelp,
                qtip: this.textHelp,
                scope: this
            });
        }

        // let the default tools be applied farthest to the right
        Ext.ux.window.MessageWindow.superclass.initTools.apply(this, arguments);
    },

    
    onRender: function (ct, position) {
        Ext.ux.window.MessageWindow.superclass.onRender.call(this, ct, position);
        //after call to parent class onRender this.el exists.

        //clip part of the window (for example the recurring messages that
        //eject from a border have the bottom rounded edge, etc. clipped off.
        if (this.clip) {
            switch (this.clip) {
            case 'bottom':
                Ext.destroy(this.getEl().child('.' + this.baseCls + '-bl'));
                break;
            }
        }

        //add a class when hovering over in order to disable
        //any updates to the window while hovering over
        if (true) {
            this.el.addClassOnOver(this.hoverCls);
        }
        
        //add click listener to body
        Ext.fly(this.body.dom).on('click', this.handleClick, this);
    },

////////////////////////////////
// Temporary override?
    
//    close : function(){
    close : function(animateTarget){
        if(this.fireEvent("beforeclose", this) !== false){
//            this.hide(null, function(){
            this.hide(animateTarget, function(){
                this.fireEvent('close', this);
                this.destroy();
            }, this);
        }
    },
/////////////////////////////////

    
    togglePinState: function (event) {
        if (this.pinOnClick) {
            //check which tool is visible
            if (this.tools.unpin.isVisible()) {
                this.handlePin(event, this.tools.unpin, this);
            } else {
                this.handleUnpin(event, this.tools.pin, this);
            }
        }
    },

    //override panel class method:
    // private
    createElement : function (name, pnode) {
        if (this.shiftHeader) {
            switch (name) {
            case 'header':
                //don't create header yet if putting inside mc, do it when tbar is done
                return;
            case 'tbar':
                Ext.ux.window.MessageWindow.superclass.createElement.call(this, 'header', pnode);
                Ext.ux.window.MessageWindow.superclass.createElement.call(this, name, pnode);
                return;
            }
        }
        //caught the ones we needed to, call the default implementation
        Ext.ux.window.MessageWindow.superclass.createElement.call(this, name, pnode);
    },

    //override/disable focus, see above.
    focus: Ext.emptyFn, 

    
    getState : function () {
        return Ext.apply(Ext.ux.window.MessageWindow.superclass.getState.call(this) || {}, this.getBox());
    },

    
    handleClick: function (event) {
        this.fireEvent('click', this, this.msg);
        this.togglePinState(event);
    },

    
    handlePin: function (event, toolEl, panel) {
        //hide the unpin button
        toolEl.hide();
        
        //show the pin button
        this.tools.pin.show();

        this.cancelHiding();
        
        this.fireEvent('afterpin', this);
    },

    
    handleUnpin: function (event, toolEl, panel) {

        //hide the pin button
        toolEl.hide();
        
        //show the unpin button
        this.tools.unpin.show();

        this[this.hideAction](this.animateTarget);

        this.fireEvent('afterunpin', this);
    },

    
    cancelHiding: function () {
        this.addClass('fixed');
        if (this.autoHide) {
            if (this.pinState === 'unpin') {
                this.task.cancel();
            }
        }
        if (this.tools.pin) {
            //show the pin button
            this.tools.pin.show();
        }
        if (this.tools.unpin) {
            //make sure the unpin button is hidden
            this.tools.unpin.hide();
        }
 },

    
    initFx : function () {
        this.showFx = this.showFx || {};
        Ext.applyIf(this.showFx, {
            align: 'b',
            duration: 1,
            callback: this.afterShow,
            scope: this
        });

        this.hideFx = this.hideFx || {};
        Ext.applyIf(this.hideFx, {
            block: false,//default for window is true
            callback: this.afterHide,
            easing: 'easeOut',//'easeNone';
            remove: true,
            scope: this
        });

        this.origin = this.origin || {};
        Ext.applyIf(this.origin, {
            el: Ext.getDoc(), //defaults to document
            increment: true, //whether to increment position of subsequent messages
            pos: "br-br",//position to align to (see {@link Ext.Element#alignTo} for more details defaults to "br-br").
            offX: -20, //amount to offset horizontally
            offY: -20, //amount to offset vertically
            spaY: 5    //vertical spacing between adjacent messages
        });
    },


    getAnimEl : function (fx) {
        var animEl;

        //animate using a proxy instead of actual element if so configured
        if (fx.useProxy) {
            animEl = this.proxy;
            this.proxy.setOpacity(0.5);
            this.proxy.show();
            var tb = this.getBox(false);
            this.proxy.setBox(tb);
            this.el.hide();
            //Ext.apply(fx, tb);
        } else {
            animEl = this.el;
        }
        
        return animEl;
    },

    //override parent method
    
    animHide : function () {

        //remove the position of this element from the manager
        this.manager.positions.remove(this.pos);

        // configured Fx and element to hide
        var fx = this.hideFx, w = this.getAnimEl(fx);
        
        switch (fx.mode) {
            case 'none':
                break;
            case 'slideIn':
                w[fx.mode]("b", fx);
                //w.slideIn("b", fx);
                break;
            case 'custom':
                Ext.callback(fx.callback, fx.scope, [this, w, fx]);//callback(cb,scope,args,delay)
                break;
            case 'standard':
                fx.duration = fx.duration || 0.25;
                fx.opacity = 0;
                w.shift(fx);
                break;
            default:
                fx.duration = fx.duration || 1;
                w.ghost("b", fx);
                break;
        }
    },

    //override parent method
    
    afterShow: function () {
        Ext.ux.window.MessageWindow.superclass.afterShow.call(this);
        
        //if user moves remove from position manager and cancel hiding
		this.on('move', function(){
            //remove the position of this element from the manager
            this.manager.positions.remove(this.pos);
            this.cancelHiding();
		}, this);

        if (this.autoHide) {
           if (this.pinState === 'unpin') {
                this.task.delay(this.hideFx.delay);
            }
        }
    },

    
    animShow: function () {

        //don't update if hovering over message
        //check if visible so it will show initially
        if (this.el.isVisible() && this.el.hasClass(this.hoverCls)) {
            return;
        }

        if (this.msgs.length > 1) {
            this.updateMsg();
        }

        //element to hide and configured Fx
        var fx = this.showFx, el = this.el;

        this.position(el);
        el.slideIn(fx.align, fx);
    },

    
    position : function (el) {

        var y,
            // push down instead of up if align top
            dir = (this.showFx.align.substr(0,1) == 't') ? 1 : -1;            

        //track positions of each instance
        this.pos = 0;

        if (this.origin.increment) {
            while (this.manager.positions.indexOf(this.pos) > -1) {
                this.pos++;
            }
            this.manager.positions.push(this.pos);
        }

        //set the window size
        this.setSize(this.width || this.minWidth, this.height || this.minHeight);

        //increment the vertical position of the window
        if (this.origin.increment) {
            y = this.origin.offY + ((this.getSize().height + this.origin.spaY) * this.pos * dir);            
        } else {
            y = 0;
        }

        el.alignTo(
            this.origin.el, // element to align to.
            this.origin.pos,        // position to align to (see {@link Ext.Element#alignTo} for more details).
            [ this.origin.offX, y ] // Offset the positioning by [x, y]: 
        );
    },

    onMouseout: function () {
        //console.info('in onMouseout');
        //console.info(arguments);
    },


    
	positionPanel: function (el, x, y) {
        if(x && typeof x[1] == 'number'){
            y = x[1];
            x = x[0];
        }
        el.pageX = x;
        el.pageY = y;
       	
        if(x === undefined || y === undefined){ // cannot translate undefined points
            return;
        }
        
        if(y < 0){ y = 10; }
        
        var p = el.translatePoints(x, y);
        el.setLocation(p.left, p.top);
        return el;
    },

    
    setMessage: function (msg) {
        this.body.update(msg);
    },

    
    setTitle: function (title, iconCls) {
        Ext.ux.window.MessageWindow.superclass.setTitle.call(this, title, iconCls || this.iconCls);
    },

    

    startAutoRefresh : function(update){
        if(update){
            this.updateMsg(true);
        }
        if(this.autoRefreshProcId){
            clearInterval(this.autoRefreshProcId);
        }
        // native javascript function to delay for a specified time before triggering the
        // execution of a specific function. After triggering the called function the command
        // doesn't complete. Instead it waits for the specified time again and then triggers
        // the function again and continues to repeat this process of triggering the function
        // at the specified intervals until either the web page is unloaded or the clearInterval
        // function is called.
        this.autoRefreshProcId = setInterval(this.animShow.createDelegate(this, []), this.interval);
    },

    
    stopAutoRefresh : function(){
        if(this.autoRefreshProcId){
            clearInterval(this.autoRefreshProcId);
        }
    },

    
    updateMsg: function (msg) {

        //don't update if hovering over message
        if (this.el && !this.el.hasClass(this.hoverCls)) {

            if (msg) {
//                console.info('message passed');   
            } else {
                this.msgIndex = this.msgs[this.msgIndex + 1] ? this.msgIndex + 1 : 0;
                this.msg = this.msgs[this.msgIndex];
            }

            //update the innerHTML of element
            //this.el.dom.update(this.msg.text);
            this.body.update(this.msg.text);
        } else {
            //console.info('hovering');
        }
    }    
});

//register the xtype
Ext.reg('message-window', Ext.ux.window.MessageWindow);

Ext.namespace('Ext.ux.layout');

Ext.ux.layout.FieldTriggerFormLayout = function(config) {
    Ext.ux.layout.FieldTriggerFormLayout.superclass.constructor.call(this,config);    
};

Ext.ux.layout.FieldTriggerFormLayout = Ext.extend(Ext.layout.FormLayout,{
	
	fieldTpl: (function() {
		var t = new Ext.Template(
			'<div class="x-form-item {itemCls}" tabIndex="-1">',
				'<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}{labelTriggerBtn}</label>',
				'<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
				'</div><div class="{clearCls}"></div>',
			'</div>'
		);
		t.disableFormats = true;
		return t.compile();
	})(),						
	
    getTemplateArgs: function(field) {
        var noLabelSep = !field.fieldLabel || field.hideLabel;
        return {
            id: field.id,
            label: field.fieldLabel,
            labelTriggerBtn: field.labelTriggerBtn,
            labelStyle: field.labelStyle||this.labelStyle||'',
            elementStyle: this.elementStyle||'',
            labelSeparator: noLabelSep ? '' : (typeof field.labelSeparator == 'undefined' ? this.labelSeparator : field.labelSeparator),
            itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''),
            clearCls: field.clearCls || 'x-form-clear-left' 
        };
    }
});

Ext.Container.LAYOUTS['triggerform'] = Ext.ux.layout.FieldTriggerFormLayout;

Ext.lib.Ajax.isCrossDomain = function(u) {
	var match = /(?:(\w*:)\/\/)?([\w\d\-\.]*(?::\d*)?)/.exec(u);
	if (!match[1]) return false; // No protocol, not cross-domain
	return (match[1] != location.protocol) || (match[2] != location.host);
};

Ext.override(Ext.data.Connection, {

	isUrlGetSafe : function(url, padding){
		if (!padding){
			padding=0;
		}
		return ((Ext.isIE && url.length < 2083-padding) || (Ext.isOpera && url.length < 4050-padding ) || true );
				
		//http://classicasp.aspfaq.com/forms/what-is-the-limit-on-querystring/get/url-parameters.html
		//Windows: Opera supports ~4050 characters, 
		//IE 4.0+ supports exactly 2083 characters,  http://support.microsoft.com/kb/208427
		//Netscape 3 -> 4.78 support up to 8192 characters before causing errors on shut-down, 
		//Netscape 6 supports ~2000 before causing errors on start-up		
	},
    request : function(o){    	
        if(this.fireEvent("beforerequest", this, o) !== false){
            var p = o.params;

            if(typeof p == "function"){
                p = p.call(o.scope||window, o);
            }
            if(typeof p == "object"){
                p = Ext.urlEncode(p);
            }
            if(this.extraParams){
                var extras = Ext.urlEncode(this.extraParams);
                p = p ? (p + '&' + extras) : extras;
            }

            var url = o.url || this.url;
            if(typeof url == 'function'){
                url = url.call(o.scope||window, o);
            }

            if(o.form){
                var form = Ext.getDom(o.form);
                url = url || form.action;

                var enctype = form.getAttribute("enctype");
                if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
                    return this.doFormUpload(o, p, url);
                }
                var f = Ext.lib.Ajax.serializeForm(form);
                p = p ? (p + '&' + f) : f;
            }

            var hs = o.headers;
            if(this.defaultHeaders){
                hs = Ext.apply(hs || {}, this.defaultHeaders);
                if(!o.headers){
                    o.headers = hs;
                }
            }

            var timeout=this.timeout;
            if (o.timeout && o.timeout > 0){
            	timeout=o.timeout*1000;
            }
            
            var cb = {
                success: this.handleResponse,
                failure: this.handleFailure,
                scope: this,
                argument: {options: o},
                timeout : timeout
            };

            o.method = o.method||this.method||(p ? "POST" : "GET");

            if(o.method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
                url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
            }

            if(typeof o.autoAbort == 'boolean'){ // options gets top priority
                if(o.autoAbort){
                    this.abort();
                }
            }else if(this.autoAbort !== false){
                this.abort();
            }
            if((o.method == 'GET' && p) || o.xmlData || o.jsonData){
                url += (url.indexOf('?') != -1 ? '&' : '?') + p;
                p = '';
            }
            if (o.scriptTag || this.scriptTag || Ext.lib.Ajax.isCrossDomain(url)) {
               this.transId = this.scriptRequest(o.method, url, cb, p, o);
            }else{
               this.transId = Ext.lib.Ajax.request(o.method, url, cb, p, o);
            }
            if (this.transId===false){
				this.handleFailure.defer(1, this, o);
            }
            return this.transId;
            
        }else{

            Ext.callback(o.callback, o.scope, [o, null, null]);
            return null;
        }
    },
    
    scriptRequest : function(method, url, cb, data, options) {
        var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;
        var trans = {
            id : transId,
            cb : options.callbackName || "stcCallback"+transId,
            scriptId : "stcScript"+transId,
            options : options
        };

        url += (url.indexOf("?") != -1 ? "&" : "?") + data + String.format("&{0}={1}", options.callbackParam || this.callbackParam || 'callback', trans.cb);
        if (!this.isUrlGetSafe(url)){
        	alert('Error the get URL is too long.');
        	//options.method='POST';
        	return false;
        }

        var conn = this;
        window[trans.cb] = function(o){
            conn.handleScriptResponse(o, trans);
        };

//      Set up the timeout handler
        trans.timeoutId = this.handleScriptFailure.defer(cb.timeout, this, [trans]);

        var script = document.createElement("script");
        script.setAttribute("src", url);
        script.setAttribute("type", "text/javascript");
        script.setAttribute("id", trans.scriptId);
        document.getElementsByTagName("head")[0].appendChild(script);

        return trans;
    },

    handleScriptResponse : function(o, trans){
        this.transId = false;
        this.destroyScriptTrans(trans, true);
        var options = trans.options;
        
//      Attempt to parse a string parameter as XML.
        var doc;
        if (typeof o == 'string') {
            if (window.ActiveXObject) {
                doc = new ActiveXObject("Microsoft.XMLDOM");
                doc.async = "false";
                doc.loadXML(o);
            } else {
                doc = new DOMParser().parseFromString(o,"text/xml");
            }
        }

//      Create the bogus XHR
        response = {
            responseObject: o,
            responseText: (typeof o == "object") ? Ext.util.JSON.encode(o) : String(o),
            responseXML: doc,
            argument: options.argument
        }
        this.fireEvent("requestcomplete", this, response, options);
        Ext.callback(options.success, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, true, response]);
    },
    
    handleScriptFailure: function(trans) {
        this.transId = false;
        this.destroyScriptTrans(trans, false);
        var options = trans.options;
        response = {
            argument:  options.argument,
            status: 500,
            statusText: 'Server failed to respond',
            responseText: ''
        };
        this.fireEvent("requestexception", this, response, options, {
            status: -1,
            statusText: 'communication failure'
        });
        Ext.callback(options.failure, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, false, response]);
    },
    
    // private
    destroyScriptTrans : function(trans, isLoaded){
        document.getElementsByTagName("head")[0].removeChild(document.getElementById(trans.scriptId));
        clearTimeout(trans.timeoutId);
        if(isLoaded){
            window[trans.cb] = undefined;
            try{
                delete window[trans.cb];
            }catch(e){}
        }else{
            // if hasn't been loaded, wait for load to remove it to prevent script error
            window[trans.cb] = function(){
                window[trans.cb] = undefined;
                try{
                    delete window[trans.cb];
                }catch(e){}
            };
        }
    }
});



Date.prototype.getFirstDateOfWeek = function(startDay) {
//set startDay to Sunday by default
	if (typeof startDay === "undefined") {
		startDay=(Ext.DatePicker?Ext.DatePicker.prototype.startDay:0);
	}
	var dayDiff = this.getDay()-startDay;
	if (dayDiff<0) {
		dayDiff+=7;
	}
	return this.add(Date.DAY,-dayDiff);
};

Array.prototype.sortDates = function() {
	return this.sort(function(a,b){
		return a.getTime() - b.getTime();		
	});
};


if (!Ext.util.EasterDate) {
	Ext.util.EasterDate = function(year, plusDays) {
		if (typeof year === "undefined") {
			year = new Date().getFullYear();
		}
		year = parseInt(year,10);
	
		if (typeof plusDays === "undefined") {
			plusDays = 0;
		}
		plusDays = parseInt(plusDays,10);
		
	//difference to first sunday after first fullmoon after beginning of spring
		var a = year % 19;
		var d = (19 * a + 24) % 30;
		var diffDay = d + (2 * (year % 4) + 4 * (year % 7) + 6 * d + 5) % 7;
		if ((diffDay == 35) || ((diffDay == 34) && (d == 28) && (a > 10))) {
			diffDay -= 7;
		}
	
		var EasterDate = new Date(year, 2, 22);	//beginning of spring
		EasterDate.setTime(EasterDate.getTime() + 86400000 * diffDay + 86400000 * plusDays);
		return EasterDate;
	};
}


Ext.namespace('Ext.ux','Ext.ux.form');


Ext.ux.DatePickerPlus = Ext.extend(Ext.DatePicker, {
								   
	version: "1.4",
    
    noOfMonth : 1,
	    
    noOfMonthPerRow : 3,
        
	fillupRows : true,
        
    eventDates : function(year) {
		return [];
	},
	
	styleDisabledDates: false,
	eventDatesSelectable : true,

	defaultEventDatesText : '',
	defaultEventDatesCls : 'x-datepickerplus-eventdates',
	
	setEventDates : function(edArray,update) {
		if (typeof update === "undefined") {
			update=true;
		}
		this.edArray = [];
		for (var i=0,il=edArray.length;i<il;++i) {
			if (Ext.isDate(edArray[i])) {
				this.edArray.push({
					date:edArray[i],
					text:this.defaultEventDatesText,
					cls:this.defaultEventDatesCls
				});
			}
			else if (edArray[i].date) {
				edArray[i].date = this.jsonDate(edArray[i].date);
				this.edArray.push(edArray[i]);				
			}
		}
		this.eventDates = function(year) {
			return this.edArray;
		};
		if (this.rendered && update) {
			this.eventDatesNumbered = this.convertCSSDatesToNumbers(this.eventDates(this.activeDate.getFullYear()));
			this.update(this.activeDate);
		}
	},
	
	eventDatesRE : false,
	
	
	eventDatesRECls : '',
	
	
	eventDatesREText : '',	
	
	
	showWeekNumber : true,
	
	weekName : "Wk.",
	
	selectWeekText : "Click to select all days of this week",
	
	selectMonthText : "Click to select all weeks of this month",

	
	multiSelection : false,
	
	
	multiSelectByCTRL : true,

    
    selectedDates : [],


    
	prevNextDaysView: "mark",
	
	
	preSelectedDates : [], 

	    
	lastSelectedDate : false, 

	
	markNationalHolidays :true,

	
	nationalHolidaysCls : 'x-datepickerplus-nationalholidays',
	
	  

	nationalHolidays : function(year) {
		year = (typeof year === "undefined" ? (this.lastRenderedYear ? this.lastRenderedYear : new Date().getFullYear()) : parseInt(year,10));
//per default the US national holidays are calculated (according to http://en.wikipedia.org/wiki/Public_holidays_of_the_United_States) 
//override this function in your local file to calculate holidays for your own country
//but remember to include the locale file _AFTER_ datepickerplus !
		var dayOfJan01 = new Date(year,0,1).getDay();
		var dayOfFeb01 = new Date(year,1,1).getDay();
		var dayOfMay01 = new Date(year,4,1).getDay();
		var dayOfSep01 = new Date(year,8,1).getDay();
		var dayOfOct01 = new Date(year,9,1).getDay();
		var dayOfNov01 = new Date(year,10,1).getDay();		

		var holidays = 
		[{
			text: "New Year's Day",
			date: new Date(year,0,1)
		},
		{
			text: "Martin Luther King Day", //(every third monday in january)
			date: new Date(year,0,(dayOfJan01>1?16+7-dayOfJan01:16-dayOfJan01))
		},
		{
			text: "Washington's Birthday", //(every third monday in february)
			date: new Date(year,1,(dayOfFeb01>1?16+7-dayOfFeb01:16-dayOfFeb01))
		},
		{
			text: "Memorial Day",//(last Monday in May)
			date: new Date(year,4,(dayOfMay01==6?31:30-dayOfMay01))
		},
		{
			text: "Independence Day",
			date: new Date(year,6,4)
		},
		{
			text: "Labor Day",//(every first monday in September)
			date: new Date(year,8,(dayOfSep01>1?2+7-dayOfSep01:2-dayOfSep01))
		},
		{
			text: "Columbus Day",//(every second monday in october)
			date: new Date(year,9,(dayOfOct01>1?9+7-dayOfOct01:9-dayOfOct01))
		},
		{
			text: "Veterans Day",
			date: new Date(year,10,11)
		},
		{
			text: "Thanksgiving Day",//(Fourth Thursday in November)
			date: new Date(year,10,(dayOfNov01>4?26+7-dayOfNov01:26-dayOfNov01))
		},
		{
			text: "Christmas Day",
			date: new Date(year,11,25)
		}];
		
		return holidays;
	},
	
	
	markWeekends :true,
	
	weekendCls : 'x-datepickerplus-weekends',
	
	weekendText :'',
	
	weekendDays: [6,0],
	
	
	useQuickTips : true,
	
	
	pageKeyWarp : 1,

	
	maxSelectionDays : false,
	
	maxSelectionDaysTitle : 'Datepicker',
	maxSelectionDaysText : 'You can only select a maximum amount of %0 days',
	undoText : "Undo",
	
	
	
	stayInAllowedRange: true,

	
	summarizeHeader:false,
	
	
	resizable: false,
	
	
	renderOkUndoButtons : true,

	
	renderTodayButton : true,
	
	disablePartialUnselect: true,
	
	allowedDates : false,
	allowedDatesText : '',

	strictRangeSelect : false,

	
	displayMask:3,
	
	displayMaskText: 'Please wait...',
	
	renderPrevNextButtons: true,
	renderPrevNextYearButtons: false,
	disableMonthPicker:false,
	
	nextYearText: "Next Year (Control+Up)",
	prevYearText: "Previous Year (Control+Down)",
	
	showActiveDate: false,
	shiftSpaceSelect: true,
	disabledLetter: false,
	
	allowMouseWheel: true,
	
//this is accidently called too often in the original (when hovering over monthlabel or bottombar..there is no need to update the cells again and just leaks performance)
	focus: Ext.emptyFn,
	
	initComponent : function(){
		Ext.ux.DatePickerPlus.superclass.initComponent.call(this);
		this.noOfMonthPerRow = this.noOfMonthPerRow > this.noOfMonth ?this.noOfMonth : this.noOfMonthPerRow;
        this.addEvents(
            
            'beforeyearchange',
            
            'afteryearchange',
            
            'beforemonthchange',
            
            'aftermonthchange',
            
            'beforemonthclick',
            
            'beforeweekclick',
            
            'beforedateclick',
            
            'aftermonthclick',
            
            'afterweekclick',
            
            'afterdateclick',
            
            'undo',
            
			'beforemousewheel',
            
			'beforemaxdays');
	},  
	
	activeDateKeyNav: function(direction) {
		if (this.showActiveDate) {
			this.activeDate = this.activeDate.add("d", direction);
			var adCell = this.activeDateCell.split("#");
			var tmpMonthCell = parseInt(adCell[0],10);
			var tmpDayCell = parseInt(adCell[1],10);
			var currentGetCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
//cursor gets out of visible range?
			if (	(tmpDayCell+direction>41 && tmpMonthCell+1>=this.cellsArray.length)	||
					(tmpDayCell+direction<0 && tmpMonthCell-1<0)	){
				this.update(this.activeDate);
			}
			else {
				currentGetCell.removeClass("x-datepickerplus-activedate");
				tmpDayCell+=direction;
				if (tmpDayCell>41) {
					tmpDayCell-=42;
					tmpMonthCell++;
				}
				else if (tmpDayCell<0) {
					tmpDayCell+=42;
					tmpMonthCell--;
				}
				currentGetCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
				currentGetCell.addClass("x-datepickerplus-activedate");
				this.activeDateCell = tmpMonthCell+"#"+tmpDayCell;
			}
		}
	},

    handleMouseWheel : function(e){
        if(this.fireEvent("beforemousewheel", this,e) !== false){
			var oldStartMonth = (this.activeDate ? this.activeDate.getMonth() : 99);
			var oldStartYear = (this.activeDate ? this.activeDate.getFullYear() : 0);			
			Ext.ux.DatePickerPlus.superclass.handleMouseWheel.call(this,e);
			var newStartMonth = (this.activeDate ? this.activeDate.getMonth() : 999);
			var newStartYear = (this.activeDate ? this.activeDate.getFullYear() : 9999);
			if (oldStartMonth!=newStartMonth) {
				this.fireEvent("aftermonthchange", this, oldStartMonth, newStartMonth);
			}
			if (oldStartYear!=newStartYear) {
				this.fireEvent("afteryearchange", this, oldStartYear, newStartYear);
			}
		}
	},
	
    // private
	onRender : function(container, position){
		if (this.noOfMonthPerRow===0) {
			this.noOfMonthPerRow = 1;
		}
		if (this.fillupRows && this.noOfMonthPerRow > 1 && this.noOfMonth % this.noOfMonthPerRow!==0) {
			this.noOfMonth+= (this.noOfMonthPerRow - (this.noOfMonth % this.noOfMonthPerRow));
		}
		var addIEClass = (Ext.isIE?' x-datepickerplus-ie':'');
		var m = ['<table cellspacing="0"',(this.multiSelection?' class="x-date-multiselect'+addIEClass+'" ':(addIEClass!==''?'class="'+addIEClass+'" ':'')),'>'];

		m.push("<tr>");

		var widfaker = (Ext.isIE?'<img src="'+Ext.BLANK_IMAGE_URL+'" />':'');
		var weekNumberQuickTip = (this.multiSelection ? (this.useQuickTips? ' ext:qtip="'+this.selectWeekText+'" ' :' title="'+this.selectWeekText+'" ') : '');
//as weekends (or defined weekly cycles) are displayed on every month at the same place, we can render the quicktips here to save time in update process
		var weekEndQuickTip = (this.markWeekends && this.weekendText!==''? (this.useQuickTips? ' ext:qtip="'+this.weekendText+'" ' :' title="'+this.weekendText+'" '):'');


//calculate the HTML of one month at first to gain some speed when rendering many calendars
		var mpre = ['<thead><tr>'];
		if (this.showWeekNumber) {
			mpre.push('<th class="x-date-weeknumber-header"><a href="#" hidefocus="on" class="x-date-weeknumber" tabIndex="1"><em><span ',(this.multiSelection ? (this.useQuickTips? ' ext:qtip="'+this.selectMonthText+'" ' :' title="'+this.selectMonthText+'" ') : ''),'>' + this.weekName + '</span></em></a></th>');
		}
		
		var dn = this.dayNames;
		for(var i = 0; i < 7; ++i){
		   var d = this.startDay+i;
		   if(d > 6){
			   d = d-7;
		   }
			mpre.push('<th><span>', dn[d].substr(0,1), '</span></th>');
		}
		mpre.push('</tr></thead><tbody><tr>');

		if (this.showWeekNumber) {
			mpre.push('<td class="x-date-weeknumber-cell"><a href="#" hidefocus="on" class="x-date-weeknumber" tabIndex="1"><em><span ',weekNumberQuickTip,'></span></em></a></td>');
		}
		
		for(var k = 0; k < 42; ++k) {
			if(k % 7 === 0 && k > 0){
				if (this.showWeekNumber) {
					mpre.push('</tr><tr><td class="x-date-weeknumber-cell"><a href="#" hidefocus="on" class="x-date-weeknumber" tabIndex="1"><em><span ',weekNumberQuickTip,'></span></em></a></td>');
				} else {
					mpre.push('</tr><tr>');
				}
			}
			mpre.push('<td class="x-date-date-cell"><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span ',(this.weekendDays.indexOf((k+this.startDay)%7)!=-1?weekEndQuickTip:''),'></span></em></a></td>');
		}
		mpre.push('</tr></tbody></table></td></tr></table></td>');
		var prerenderedMonth = mpre.join("");

		if (this.summarizeHeader && this.noOfMonth > 1) {
			m.push('<td align="center" id="',this.id,'-summarize" colspan="',this.noOfMonthPerRow,'" class="x-date-middle x-date-pickerplus-middle"></td></tr>');
			m.push("<tr>");
		}

		for(var x=0,xk=this.noOfMonth; x<xk; ++x) {            
            m.push('<td><table class="x-date-pickerplus',(x%this.noOfMonthPerRow===0?'':' x-date-monthtable'),(!this.prevNextDaysView?" x-date-pickerplus-prevnexthide":""),'" cellspacing="0"><tr>');
			if (x===0) {
				m.push('<td class="x-date-left">');
				if (this.renderPrevNextButtons) {
					m.push('<a class="npm" href="#" ',(this.useQuickTips? ' ext:qtip="'+this.prevText+'" ' :' title="'+this.prevText+'" '),'></a>');
				}
				if (this.renderPrevNextYearButtons) {
					m.push('<a class="npy" href="#" ',(this.useQuickTips? ' ext:qtip="'+this.prevYearText+'" ' :' title="'+this.prevYearText+'" '),'></a>');
				}
				m.push('</td>');
			}			
			else if (x==this.noOfMonthPerRow-1) {
				if (this.renderPrevNextButtons) {				
					m.push('<td class="x-date-dummy x-date-middle">',widfaker,'</td>');
				}
			}			
            m.push("<td class='x-date-middle x-date-pickerplus-middle",(x===0 && !this.disableMonthPicker ?" x-date-firstMonth":""),"' align='center'>");
			if (x>0 || this.disableMonthPicker) {
				m.push('<span id="',this.id,'-monthLabel', x , '"></span>');
			}
			m.push('</td>');
			if (x==this.noOfMonthPerRow-1)	{
				m.push('<td class="x-date-right">');
				if (this.renderPrevNextButtons) {				
					m.push('<a class="npm" href="#" ', (this.useQuickTips? ' ext:qtip="'+this.nextText+'" ' :' title="'+this.nextText+'" ') ,'></a>');
				}
				if (this.renderPrevNextYearButtons) {
					m.push('<a class="npy" href="#" ',(this.useQuickTips? ' ext:qtip="'+this.nextYearText+'" ' :' title="'+this.nextYearText+'" '),'></a>');
				}
				m.push('</td>');				
			}
			else if (x===0) {
				if (this.renderPrevNextButtons) {				
					m.push('<td class="x-date-dummy x-date-middle">',widfaker,'</td>');
				}
			}			
			
            m.push('</tr><tr><td',(x===0 || x==this.noOfMonthPerRow-1?' colspan="3" ':''),'><table class="x-date-inner" id="',this.id,'-inner-date', x ,'" cellspacing="0">');

			m.push(prerenderedMonth);
	
            if( (x+1) % this.noOfMonthPerRow === 0) {
                m.push("</tr><tr>");
            }            
        }
        m.push('</tr>');
		
		m.push('<tr><td',(this.noOfMonthPerRow>1?' colspan="'+this.noOfMonthPerRow+'"':''),' class="x-date-bottom" align="center"><div><table width="100%" cellpadding="0" cellspacing="0"><tr><td align="right" class="x-date-multiokbtn">',widfaker,'</td><td align="center" class="x-date-todaybtn">',widfaker,'</td><td align="left" class="x-date-multiundobtn">',widfaker,'</td></tr></table></div></td></tr>');
		
		m.push('</table><div class="x-date-mp"></div>');
        var el = document.createElement("div");
        el.className = "x-date-picker";
        el.innerHTML = m.join("");  

        container.dom.insertBefore(el, position);

        this.el = Ext.get(el);        
        this.eventEl = Ext.get(el.firstChild);

		if (this.renderPrevNextButtons) {
			var crl = new Ext.util.ClickRepeater(this.el.child("td.x-date-left a.npm"), {
				handler: this.showPrevMonth,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
	
			var crr = new Ext.util.ClickRepeater(this.el.child("td.x-date-right a.npm"), {
				handler: this.showNextMonth,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
		}
		
		if (this.renderPrevNextYearButtons) {
			var cryl = new Ext.util.ClickRepeater(this.el.child("td.x-date-left a.npy"), {
				handler: this.showPrevYear,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
	
			var cryr = new Ext.util.ClickRepeater(this.el.child("td.x-date-right a.npy"), {
				handler: this.showNextYear,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
		}
		if (this.allowMouseWheel) {
			this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
		}

		if (!this.disableMonthPicker) {
	        this.monthPicker = this.el.down('div.x-date-mp');
	        this.monthPicker.enableDisplayMode('block');
		}


        var kn = new Ext.KeyNav(this.eventEl, {
            "left" : function(e){
                (!this.disabled && e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextButtons) ?
                    this.showPrevMonth() :
					this.activeDateKeyNav(-1));
            },

            "right" : function(e){
                (!this.disabled && e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextButtons) ?
                    this.showNextMonth() :
					this.activeDateKeyNav(1));
            },

            "up" : function(e){
                (!this.disabled && e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextYearButtons) ?
                    this.showNextYear() :
					this.activeDateKeyNav(-7));
            },

            "down" : function(e){
                (!this.disabled && e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextYearButtons) ?
                    this.showPrevYear() :
					this.activeDateKeyNav(7));
            },

            "pageUp" : function(e){
				if (!this.disabled) {
			        this.update(this.activeDate.add("mo", this.pageKeyWarp*(-1)));
				}
            },

            "pageDown" : function(e){
				if (!this.disabled) {				
			        this.update(this.activeDate.add("mo", this.pageKeyWarp));
				}
            },

            "enter" : function(e){
                e.stopPropagation();
				if (!this.disabled) {				
					if (this.multiSelection) {
						this.okClicked();
					}
					else {
						this.finishDateSelection(this.activeDate);
					}
				}
                return true;
            }, 
            scope : this 
        });

		if (!this.disableSingleDateSelection) {
			this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
		}
		if (this.multiSelection && this.showWeekNumber) {
			this.eventEl.on("click", this.handleWeekClick,  this, {delegate: "a.x-date-weeknumber"});
		}
        this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.spaceKeyPressed,  this);		
        
        this.cellsArray = [];
        this.textNodesArray = [];
        this.weekNumberCellsArray = [];
        this.weekNumberTextElsArray = [];		
		this.weekNumberHeaderCellsArray = [];
	
		var cells,textNodes,weekNumberCells,weekNumberTextEls,weekNumberHeaderCells;
        for(var xx=0,xxk=this.noOfMonth; xx< xxk; ++xx) {
            cells = Ext.get(this.id+'-inner-date'+xx).select("tbody td.x-date-date-cell");
            textNodes = Ext.get(this.id+'-inner-date'+xx).query("tbody td.x-date-date-cell span");
            this.cellsArray[xx] = cells;
            this.textNodesArray[xx] = textNodes;
			if (this.showWeekNumber) {
				weekNumberCells = Ext.get(this.id+'-inner-date'+xx).select("tbody td.x-date-weeknumber-cell");
				weekNumberTextEls = Ext.get(this.id+'-inner-date'+xx).select("tbody td.x-date-weeknumber-cell span");				
				this.weekNumberCellsArray[xx] = weekNumberCells;
				this.weekNumberTextElsArray[xx] = weekNumberTextEls;				
				weekNumberHeaderCells = Ext.get(this.id+'-inner-date'+xx).select("th.x-date-weeknumber-header");
				this.weekNumberHeaderCellsArray[xx] = weekNumberHeaderCells;
			}
        }

//set the original monthpicker again to the first month only to be able to quickly change the startmonth		
		if (!this.disableMonthPicker) {
			this.mbtn = new Ext.Button({
				text: " ",
				tooltip: this.monthYearText,
				renderTo: this.el.child("td.x-date-firstMonth", true)			
			});
	
			this.mbtn.on('click', this.showMonthPickerPlus, this);
			this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
		}

//showtoday from Ext 2.2
		if (this.renderTodayButton || this.showToday) {
	        var today = new Date().dateFormat(this.format);			
			this.todayBtn = new Ext.Button({
				renderTo: this.el.child("td.x-date-bottom .x-date-todaybtn", true),
                text: String.format(this.todayText, today),
				tooltip: String.format(this.todayTip, today),
				handler: this.selectToday,
				scope: this
			});
		}
		
		if (this.multiSelection && this.renderOkUndoButtons) {
			this.OKBtn = new Ext.Button({
	            renderTo: this.el.child("td.x-date-bottom .x-date-multiokbtn", true),
				text: this.okText,
				handler: this.okClicked,
				scope: this
			});

			this.undoBtn = new Ext.Button({
	            renderTo: this.el.child("td.x-date-bottom .x-date-multiundobtn", true),
				text: this.undoText,
				handler: function() {
					if (!this.disabled) {
						this.fireEvent("undo", this, this.preSelectedDates);
						this.preSelectedDates = [];
						for (var i=0,il=this.selectedDates.length;i<il;++i) {
							this.preSelectedDates.push(this.selectedDates[i].clearTime().getTime());
						}
						this.update(this.activeDate);
					}
				},
				scope: this
			});
		}
		
//In development...


		if(Ext.isIE){
            this.el.repaint();
        }
//preselect dates if given
		this.preSelectedDates = [];
		for(var sdc=0, sdcl=this.selectedDates.length; sdc < sdcl; ++sdc) {
		   this.preSelectedDates.push(this.selectedDates[sdc].clearTime().getTime());
		}

        this.update(this.value);
    },
	
	showMonthPickerPlus: function() {
		if (!this.disabled) {
			this.showMonthPicker();
		}
	},

//converts all custom dates to timestamps numbers for faster calculations and splits their attributes into separate arrays
	convertCSSDatesToNumbers : function(objarr) {
//date,text,class		
		var converted =  [[],[],[]];
		for (var i=0,il=objarr.length;i<il;++i) {
			converted[0][i] = objarr[i].date.clearTime().getTime();
			converted[1][i] = (objarr[i].text ? objarr[i].text : this.defaultEventDatesText);
			converted[2][i] = (objarr[i].cls ? objarr[i].cls : this.defaultEventDatesCls);
		}
		return converted;
	},

	clearSelectedDates : function(update) {
		if (typeof update === "undefined") {
			update=true;
		}
		this.selectedDates = [];
		this.preSelectedDates = [];
		if (this.rendered && update) {
			this.update(this.activeDate);
		}
	},
	
//support json dates
	jsonDate: function(dates) {
		if (!Ext.isArray(dates)) {
			if (typeof dates === "string") {
				return Date.parseDate(dates.replace(/T/," "),'Y-m-d H:i:s');
			}
		}
		else {
			for (var i=0,il=dates.length;i<il;i++) {
				if (typeof dates[i] === "string") {
					dates[i] = Date.parseDate(dates[i].replace(/T/," "),'Y-m-d H:i:s');
				}
			}
		}
		return dates;
	},
	
	setSelectedDates : function(dates,update) {
		if (typeof update === "undefined") {
			update=true;
		}
		dates = this.jsonDate(dates);
		if (!Ext.isArray(dates)) {
			dates = [dates];
		}
		var d, dt;
		for (var i=0,il=dates.length;i<il;++i) {
			d = dates[i];
			dt = d.clearTime().getTime();
			if (this.preSelectedDates.indexOf(dt)==-1) {
				this.preSelectedDates.push(dt);
				this.selectedDates.push(d);				
			}
		}
		if (this.rendered && update) {
			this.update(this.activeDate);
		}
	},

	setAllowedDates : function(dates,update) {
		if (typeof update === "undefined") {
			update=true;
		}
		this.allowedDates = this.jsonDate(dates);
		if (this.rendered && update) {
			this.update(this.activeDate);
		}
	},

	setMinDate: function(minDate) {
		this.minDate = this.jsonDate(minDate);
        this.update(this.value, true);
	},

	setMaxDate: function(maxDate) {
		this.maxDate = this.jsonDate(maxDate);
        this.update(this.value, true);
	},

	setDateLimits: function(minDate,maxDate) {
		this.minDate = this.jsonDate(minDate);
		this.maxDate = this.jsonDate(maxDate);
        this.update(this.value, true);
	},

	
	// private
//forcerefresh option from ext 2.2 just included to be compatible	
    update : function(date, forceRefresh ,masked){
		if (typeof masked==="undefined")  {
			masked = false;
		}
		if (typeof forceRefresh==="undefined")  {
			forceRefresh = false;
		}
		
		if (forceRefresh) {
			var ad = this.activeDate;
			this.activeDate = null;
			date = ad;			
		}				
		
		var dMask = (this.displayMask && (isNaN(this.displayMask) || this.noOfMonth > this.displayMask)? true: false);
		
		if (!masked && dMask) {
			this.el.mask(this.displayMaskText);
//set forcerefresh to false because new date (from old activedate) is already calculated
			this.update.defer(10, this, [date,false,true]);
			return false;
		}
		
		if (this.stayInAllowedRange && (this.minDate||this.maxDate)) {
			if (this.minDate && (this.minDate.getFullYear() > date.getFullYear() || (this.minDate.getMonth() > date.getMonth() && this.minDate.getFullYear() == date.getFullYear()))) {
				date = new Date(this.minDate.getTime());
			}
			else if (this.maxDate && (this.maxDate.getFullYear() < date.getFullYear() || (this.maxDate.getMonth() < date.getMonth() && this.maxDate.getFullYear() == date.getFullYear()))) {
				date = new Date(this.maxDate.getTime());
			}
		}
		
		var newStartMonth = date.getMonth();
		var oldStartMonth = (this.activeDate ? this.activeDate.getMonth() : newStartMonth);
		var newStartYear = date.getFullYear();
		var oldStartYear = (this.activeDate ? this.activeDate.getFullYear() : newStartYear);
		
		if (oldStartMonth!=newStartMonth) {
            this.fireEvent("beforemonthchange", this, oldStartMonth, newStartMonth);			
		}
		if (oldStartYear!=newStartYear) {
            this.fireEvent("beforeyearchange", this, oldStartYear, newStartYear);
		}
		
        this.activeDate = date.clearTime();
		this.preSelectedCells = [];
		this.lastSelectedDateCell = '';
		this.activeDateCell = '';
		var lsd = (this.lastSelectedDate?this.lastSelectedDate:0);
		var today = new Date().clearTime().getTime();
		var min = this.minDate ? this.minDate.clearTime().getTime() : Number.NEGATIVE_INFINITY;
		var max = this.maxDate ? this.maxDate.clearTime().getTime() : Number.POSITIVE_INFINITY;
		var ddMatch = this.disabledDatesRE;
		var ddText = this.disabledDatesText;
		var ddays = this.disabledDays ? this.disabledDays.join("") : false;
		var ddaysText = this.disabledDaysText;
		
		var edMatch = this.eventDatesRE;
		var edCls = this.eventDatesRECls;
		var edText = this.eventDatesREText;		

		var adText = this.allowedDatesText;
		
		var format = this.format;
		var adt = this.activeDate.getTime();
		
		this.todayMonthCell	= false;
		this.todayDayCell = false;
		if (this.allowedDates) {
			this.allowedDatesT = [];
			for (var k=0, kl=this.allowedDates.length;k<kl;++k) {
				this.allowedDatesT.push(this.allowedDates[k].clearTime().getTime());
			}
		}
		var setCellClass = function(cal, cell,textnode,d){
	
			var foundday, eCell = Ext.get(cell), eTextNode = Ext.get(textnode), t = d.getTime(), tiptext=false, fvalue;
			cell.title = "";
			cell.firstChild.dateValue = t;

//check this per day, so holidays between years in the same week will be recognized (newyear in most cases),
//yearly eventdates are also possible then
			var dfY = d.getFullYear();
			if (cal.lastRenderedYear!==dfY) {
				cal.lastRenderedYear=dfY;
				if(cal.markNationalHolidays) {
//calculate new holiday list for current year
					cal.nationalHolidaysNumbered = cal.convertCSSDatesToNumbers(cal.nationalHolidays(dfY));
				}
				cal.eventDatesNumbered = cal.convertCSSDatesToNumbers(cal.eventDates(dfY));
			}
			
			// disabling
			if(t < min) {
				cell.className = " x-date-disabled";
				tiptext = cal.minText;				
			}
			if(t > max) {
				cell.className = " x-date-disabled";
				tiptext = cal.maxText;
			}
			if(ddays){
				if(ddays.indexOf(d.getDay()) != -1){
					tiptext = ddaysText;
					cell.className = " x-date-disabled";
				}
			}
			if(ddMatch && format){
				fvalue = d.dateFormat(format);
				if(ddMatch.test(fvalue)){
					tiptext = ddText.replace("%0", fvalue);					
					cell.className = " x-date-disabled";
				}
			}

			if (cal.allowedDates && cal.allowedDatesT.indexOf(t)==-1){
				cell.className = " x-date-disabled";
				tiptext = adText;
			}

			//mark weekends
			if(cal.markWeekends && cal.weekendDays.indexOf(d.getDay()) != -1 && !eCell.hasClass('x-date-disabled')) {
				eCell.addClass(cal.weekendCls);
			}
			

			if(!eCell.hasClass('x-date-disabled') || cal.styleDisabledDates) {
//mark dates with specific css (still selectable) (higher priority than weekends)
				if (cal.eventDatesNumbered[0].length>0) {
					foundday = cal.eventDatesNumbered[0].indexOf(t);
					if (foundday!=-1) {
						if(cal.eventDatesNumbered[2][foundday]!==""){						
							eCell.addClass(cal.eventDatesNumbered[2][foundday]+(cal.eventDatesSelectable?"":"-disabled"));
							tiptext = (cal.eventDatesNumbered[1][foundday]!=="" ? cal.eventDatesNumbered[1][foundday] : false);
						}
					}
				}

//regular Expression custom CSS Dates
				if(edMatch && format){
					fvalue = d.dateFormat(format);
					if(edMatch.test(fvalue)){
						tiptext = edText.replace("%0", fvalue);					
						cell.className = edCls;
					}
				}
			}
			
			
			if(!eCell.hasClass('x-date-disabled')) {
//mark Holidays				
				if(cal.markNationalHolidays && cal.nationalHolidaysNumbered[0].length>0) {
					foundday = cal.nationalHolidaysNumbered[0].indexOf(t);
					if (foundday!=-1) {
						eCell.addClass(cal.nationalHolidaysCls);
						tiptext = (cal.nationalHolidaysNumbered[1][foundday]!=="" ? cal.nationalHolidaysNumbered[1][foundday] : false);
					}
				}
				
				
//finally mark already selected items as selected
				if (cal.preSelectedDates.indexOf(t)!=-1) {
					eCell.addClass("x-date-selected");
					cal.preSelectedCells.push(cell.firstChild.monthCell+"#"+cell.firstChild.dayCell);
				}
				
				if (t == lsd) {
					cal.lastSelectedDateCell = cell.firstChild.monthCell+"#"+cell.firstChild.dayCell;
				}
				
			}
			else if (cal.disabledLetter){
				textnode.innerHTML = cal.disabledLetter;
			}

//mark today afterwards to ensure today CSS has higher priority
			if(t == today){
				eCell.addClass("x-date-today");
				tiptext = cal.todayText;
			}

//keynavigation?
			if(cal.showActiveDate && t == adt && cal.activeDateCell === ''){
				eCell.addClass("x-datepickerplus-activedate");
				cal.activeDateCell = cell.firstChild.monthCell+"#"+cell.firstChild.dayCell;
			}

//any quicktips necessary?
			if (tiptext) {
				if (cal.useQuickTips) {
					Ext.QuickTips.register({
						target: eTextNode,
						text: tiptext
					});
				}
				else {
					cell.title = tiptext;
				}
			}
			
			
		};

		var cells,textEls,days,firstOfMonth,startingPos,pm,prevStart,d,sel,i,intDay,weekNumbers,weekNumbersTextEls,curWeekStart,weekNumbersHeader,monthLabel,main,w;
		var summarizeHTML = [];
		for(var x=0,xk=this.noOfMonth;x<xk;++x) {
			if (this.summarizeHeader && this.noOfMonth > 1 && (x===0||x==this.noOfMonth-1)) {
				summarizeHTML.push(this.monthNames[date.getMonth()]," ",date.getFullYear());
				if (x===0) {
					summarizeHTML.push(" - ");
				}
			}
			cells = this.cellsArray[x].elements;
			textEls = this.textNodesArray[x];

			if ((this.markNationalHolidays || this.eventDates.length>0) && this.useQuickTips) {
				for (var e=0,el=textEls.length;e<el;++e) {
					Ext.QuickTips.unregister(textEls[e]);
				}
			}
			
			days = date.getDaysInMonth();
			firstOfMonth = date.getFirstDateOfMonth();
			startingPos = firstOfMonth.getDay()-this.startDay;
	
			if(startingPos <= this.startDay){
				startingPos += 7;
			}
	
			pm = date.add("mo", -1);
			prevStart = pm.getDaysInMonth()-startingPos;
	
			days += startingPos;
	
			d = new Date(pm.getFullYear(), pm.getMonth(), prevStart).clearTime();
	
			i = 0;
			if (this.showWeekNumber) {
				weekNumbers = this.weekNumberCellsArray[x].elements;
				weekNumbersTextEls = this.weekNumberTextElsArray[x].elements;				
				curWeekStart = new Date(d);
				curWeekStart.setDate(curWeekStart.getDate() + 7);
				
				weekNumbersHeader = this.weekNumberHeaderCellsArray[x].elements;
				weekNumbersHeader[0].firstChild.monthValue = date.getMonth();
				weekNumbersHeader[0].firstChild.dateValue = curWeekStart.getTime();				
				weekNumbersHeader[0].firstChild.monthCell = x;
				weekNumbersHeader[0].firstChild.dayCell = 0;
				
				while(i < weekNumbers.length) {
					weekNumbersTextEls[i].innerHTML = curWeekStart.getWeekOfYear();
					weekNumbers[i].firstChild.dateValue = curWeekStart.getTime();
					weekNumbers[i].firstChild.monthCell = x;
					weekNumbers[i].firstChild.dayCell = (i*7);
					curWeekStart.setDate(curWeekStart.getDate() + 7);
					i++;
				}
				i = 0;
			}

			for(; i < startingPos; ++i) {
				textEls[i].innerHTML = (++prevStart);
				cells[i].firstChild.monthCell = x;
				cells[i].firstChild.dayCell = i;
				
				d.setDate(d.getDate()+1);
				cells[i].className = "x-date-prevday";
				setCellClass(this, cells[i],textEls[i],d);
			}
			
			for(; i < days; ++i){
				intDay = i - startingPos + 1;
				textEls[i].innerHTML = (intDay);
				cells[i].firstChild.monthCell = x;
				cells[i].firstChild.dayCell = i;
				d.setDate(d.getDate()+1);
				cells[i].className = "x-date-active";
				setCellClass(this, cells[i],textEls[i],d);
				if(d.getTime() == today){
					this.todayMonthCell	= x;
					this.todayDayCell = i;
				}
			}
		
			var extraDays = 0;
			for(; i < 42; ++i) {
				textEls[i].innerHTML = (++extraDays);
				cells[i].firstChild.monthCell = x;
				cells[i].firstChild.dayCell = i;
				d.setDate(d.getDate()+1);
				cells[i].className = "x-date-nextday";
				setCellClass(this, cells[i],textEls[i],d);
			}

			if (x===0 && !this.disableMonthPicker) {
				this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
			}
			else {
				monthLabel = Ext.get(this.id+'-monthLabel' + x);                    
				monthLabel.update(this.monthNames[date.getMonth()] + " " + date.getFullYear());
			}
			date = date.add('mo',1);

			
			if(!this.internalRender){
				main = this.el.dom.firstChild;
				w = main.offsetWidth;
				this.el.setWidth(w + this.el.getBorderWidth("lr"));
				Ext.fly(main).setWidth(w);
				this.internalRender = true;
				// opera does not respect the auto grow header center column
				// then, after it gets a width opera refuses to recalculate
				// without a second pass
//Not needed anymore (tested with opera 9)
							
			}
		}
		if (this.summarizeHeader && this.noOfMonth > 1) {
			var topHeader = Ext.get(this.id+'-summarize');
			topHeader.update(summarizeHTML.join(""));
		}
		this.el.unmask();
		if (oldStartMonth!=newStartMonth) {
            this.fireEvent("aftermonthchange", this, oldStartMonth, newStartMonth);
		}
		if (oldStartYear!=newStartYear) {
            this.fireEvent("afteryearchange", this, oldStartYear, newStartYear);
		}
	
    },

	beforeDestroy : function() {
		if(this.rendered) {		
			if (this.mbtn) {		
				this.mbtn.destroy();
			}
			if (this.todayBtn) {
				this.todayBtn.destroy();
			}
			if (this.OKBtn){
				this.OKBtn.destroy();
			}
			if (this.undoBtn){
				this.undoBtn.destroy();			
			}
		}
	},


    handleWeekClick : function(e, t){
		if (!this.disabled) {
			e.stopEvent();
			var startweekdate = new Date(t.dateValue).getFirstDateOfWeek(this.startDay), amount=0, startmonth, curmonth,enableUnselect;
			var monthcell = t.monthCell;
			var daycell = t.dayCell;
			switch(t.parentNode.tagName.toUpperCase()) {
			case "TH":
				amount=42;
				startmonth = t.monthValue;
				break;
			case "TD":
				amount=7;
				break;
			}
			
			if ((amount==42 && this.fireEvent("beforemonthclick", this, startmonth,this.lastStateWasSelected) !== false)
			||  (amount==7 && this.fireEvent("beforeweekclick", this, startweekdate,this.lastStateWasSelected) !== false)) {
			
				if (!Ext.EventObject.ctrlKey && this.multiSelectByCTRL) {
					this.removeAllPreselectedClasses();
				}
				
				enableUnselect=true;	
				if (this.disablePartialUnselect) {
					var teststartweekdate = startweekdate;
					for (var k=0;k<amount;++k) {
		//check, if the whole set is still selected, then make unselection possible again
						curmonth = teststartweekdate.getMonth();		
						if ((amount == 7 || curmonth === startmonth) && this.preSelectedDates.indexOf(teststartweekdate.clearTime().getTime())==-1) {
							enableUnselect=false;
							break;
						}
						teststartweekdate = teststartweekdate.add(Date.DAY,1);
					}
				}
		
				var reverseAdd =  false;
				var dateAdder = 1;
				if (this.strictRangeSelect &&	(
													(this.preSelectedDates.indexOf(startweekdate.add(Date.DAY,-1).clearTime().getTime())==-1 && !enableUnselect) ||
													(this.preSelectedDates.indexOf(startweekdate.add(Date.DAY,-1).clearTime().getTime())!=-1 && enableUnselect)
												)
					) {
					reverseAdd = true;
					startweekdate = startweekdate.add(Date.DAY,amount-1);
					dateAdder = -1;
				}
				
				this.maxNotified = false;
				for (var i=0,ni;i<amount;++i) {
					curmonth = startweekdate.getMonth();
					ni = (reverseAdd ? amount-1-i : i);
					if (amount == 7 || curmonth === startmonth) {
						this.markDateAsSelected(startweekdate.clearTime().getTime(),true,monthcell,daycell+ni,enableUnselect);
					}
					startweekdate = startweekdate.add(Date.DAY,dateAdder);
				}
				if (amount==42) {
					this.fireEvent("aftermonthclick", this, startmonth,this.lastStateWasSelected);
				}
				else {
					this.fireEvent("afterweekclick", this, new Date(t.dateValue).getFirstDateOfWeek(this.startDay),this.lastStateWasSelected);
				}
			}
		}
	},

	markDateAsSelected : function(t,fakeCTRL,monthcell,daycell,enableUnselect) {
		var currentGetCell = Ext.get(this.cellsArray[monthcell].elements[daycell]);
	
		if ((currentGetCell.hasClass("x-date-prevday") || currentGetCell.hasClass("x-date-nextday") ) && this.prevNextDaysView!=="mark") {		
			return false;
		}

		if (this.multiSelection && (Ext.EventObject.ctrlKey || fakeCTRL)) {
			var beforeDate = new Date(t).add(Date.DAY,-1).clearTime().getTime();
			var afterDate = new Date(t).add(Date.DAY,1).clearTime().getTime();				
			
			if (this.preSelectedDates.indexOf(t)==-1) {
				if (this.maxSelectionDays === this.preSelectedDates.length) {
					if (!this.maxNotified)  {
				        if(this.fireEvent("beforemaxdays", this) !== false){
							Ext.Msg.alert(this.maxSelectionDaysTitle,this.maxSelectionDaysText.replace(/%0/,this.maxSelectionDays));
						}
						this.maxNotified = true;
					}
					return false;
				}
				if (currentGetCell.hasClass("x-date-disabled")) {
					return false;
				}
				
				if (this.strictRangeSelect && this.preSelectedDates.indexOf(afterDate)==-1 && this.preSelectedDates.indexOf(beforeDate)==-1 && this.preSelectedDates.length > 0) {
					return false;
				}
				
				this.preSelectedDates.push(t);
				this.markSingleDays(monthcell,daycell,false);
				this.markGhostDatesAlso(monthcell,daycell,false);
				this.lastStateWasSelected = true;
			}
			else {
				if (enableUnselect &&	(!this.strictRangeSelect ||
											(this.strictRangeSelect && 
											 	(
													(this.preSelectedDates.indexOf(afterDate)==-1 && this.preSelectedDates.indexOf(beforeDate)!=-1 ) ||
													(this.preSelectedDates.indexOf(afterDate)!=-1 && this.preSelectedDates.indexOf(beforeDate)==-1 )
												)
											)
										)
					){
					this.preSelectedDates.remove(t);
					this.markSingleDays(monthcell,daycell,true);
					this.markGhostDatesAlso(monthcell,daycell,true);
					this.lastStateWasSelected = false;
				}
			}
		}
		else {
//calling update in any case would get too slow on huge multiselect calendars, so set the class for the selected cells manually	 (MUCH faster if not calling update() every time!)
			this.removeAllPreselectedClasses();
			this.preSelectedDates = [t];			
			this.preSelectedCells = [];
			this.markSingleDays(monthcell,daycell,false);
			this.markGhostDatesAlso(monthcell,daycell,false);
			this.lastStateWasSelected = true;
		}
		this.lastSelectedDate = t;
		this.lastSelectedDateCell = monthcell+"#"+daycell;
		if (this.multiSelection && !this.renderOkUndoButtons) {
			this.copyPreToSelectedDays();
		}
		return true;
	},

	markSingleDays : function(monthcell,daycell,remove) {
		if(!remove) {
			Ext.get(this.cellsArray[monthcell].elements[daycell]).addClass("x-date-selected");
			this.preSelectedCells.push((monthcell)+"#"+(daycell));
		}
		else {
			Ext.get(this.cellsArray[monthcell].elements[daycell]).removeClass("x-date-selected");
			this.preSelectedCells.remove((monthcell)+"#"+(daycell));
		}
	},

	markGhostDatesAlso : function(monthcell,daycell,remove) {
		if (this.prevNextDaysView=="mark") {
			var currentGetCell = Ext.get(this.cellsArray[monthcell].elements[daycell]), dayCellDiff;
			if(currentGetCell.hasClass("x-date-prevday") && monthcell>0) {
				dayCellDiff = (5-Math.floor(daycell/7))*7;
				if(Ext.get(this.cellsArray[monthcell-1].elements[daycell+dayCellDiff]).hasClass("x-date-nextday")) {
					dayCellDiff-=7;
				}
				this.markSingleDays(monthcell-1,daycell+dayCellDiff,remove);
			}
			else if(currentGetCell.hasClass("x-date-nextday") && monthcell<this.cellsArray.length-1) {
				dayCellDiff = 28;
				if(this.cellsArray[monthcell].elements[daycell].firstChild.firstChild.firstChild.innerHTML != this.cellsArray[monthcell+1].elements[daycell-dayCellDiff].firstChild.firstChild.firstChild.innerHTML) {
					dayCellDiff=35;
				}
				this.markSingleDays(monthcell+1,daycell-dayCellDiff,remove);
			}
			else if(currentGetCell.hasClass("x-date-active") && ((daycell < 14 && monthcell>0) || (daycell > 27 && monthcell<this.cellsArray.length-1))){
				if (daycell<14) {
					dayCellDiff = 28;
					if(!Ext.get(this.cellsArray[monthcell-1].elements[daycell+dayCellDiff]).hasClass("x-date-nextday")) {
						dayCellDiff=35;
					}
					if(daycell+dayCellDiff < 42 && this.cellsArray[monthcell].elements[daycell].firstChild.firstChild.firstChild.innerHTML == this.cellsArray[monthcell-1].elements[daycell+dayCellDiff].firstChild.firstChild.firstChild.innerHTML) {
						this.markSingleDays(monthcell-1,daycell+dayCellDiff,remove);
					}
				}
				else {
					dayCellDiff = 28;
					if(!Ext.get(this.cellsArray[monthcell+1].elements[daycell-dayCellDiff]).hasClass("x-date-prevday")) {
						dayCellDiff=35;
					}
					if(daycell-dayCellDiff >= 0 && this.cellsArray[monthcell].elements[daycell].firstChild.firstChild.firstChild.innerHTML == this.cellsArray[monthcell+1].elements[daycell-dayCellDiff].firstChild.firstChild.firstChild.innerHTML) {
						this.markSingleDays(monthcell+1,daycell-dayCellDiff,remove);
					}
				}
			}
		}
	},
	
	
	removeAllPreselectedClasses : function() {
		for (var e=0,el=this.preSelectedCells.length;e<el;++e) {												
			var position = this.preSelectedCells[e].split("#");
			Ext.get(this.cellsArray[position[0]].elements[position[1]]).removeClass("x-date-selected");
		}
		this.preSelectedDates = [];
		this.preSelectedCells = [];
	},

    handleDateClick : function(e, t){
		
		e.stopEvent();
		var tp = Ext.fly(t.parentNode);

        if(!this.disabled && t.dateValue && !tp.hasClass("x-date-disabled") && !tp.hasClass("x-datepickerplus-eventdates-disabled") && this.fireEvent("beforedateclick", this,e) !== false){
			if (( !tp.hasClass("x-date-prevday") && !tp.hasClass("x-date-nextday") ) || this.prevNextDaysView=="mark") {
				var eO = Ext.EventObject;
				if ((!eO.ctrlKey && this.multiSelectByCTRL) || eO.shiftKey || !this.multiSelection) {
					this.removeAllPreselectedClasses();
				}
				var ctrlfaker = (((!eO.ctrlKey && !this.multiSelectByCTRL) || eO.shiftKey) && this.multiSelection ? true:false);
	
	
				if (eO.shiftKey && this.multiSelection && this.lastSelectedDate) {
					var startdate = this.lastSelectedDate;
					var targetdate = t.dateValue;
					var dayDiff = (startdate<targetdate? 1:-1);
					var lsdCell = this.lastSelectedDateCell.split("#");
					var tmpMonthCell = parseInt(lsdCell[0],10);
					var tmpDayCell = parseInt(lsdCell[1],10);
					var testCell,ghostCounter=0,ghostplus=0;
	
					this.maxNotified = false;
	
	
	
	//startdate lies in nonvisible month ?
					var firstVisibleDate = this.activeDate.getFirstDateOfMonth().clearTime().getTime();
					var lastVisibleDate = this.activeDate.add(Date.MONTH,this.noOfMonth-1).getLastDateOfMonth().clearTime().getTime();
	
					if (startdate<firstVisibleDate ||
						startdate>lastVisibleDate) {
				
	//prepare for disabledCheck
						var min = this.minDate ? this.minDate.clearTime().getTime() : Number.NEGATIVE_INFINITY;
						var max = this.maxDate ? this.maxDate.clearTime().getTime() : Number.POSITIVE_INFINITY;
						var ddays = this.disabledDays ? this.disabledDays.join("") : "";
						var ddMatch = this.disabledDatesRE;
						var format = this.format;
						var allowedDatesT =  this.allowedDates ? this.allowedDatesT : false;
						var d,ddMatchResult,fvalue;
	//check, if the days would be disabled
						while(startdate<firstVisibleDate || startdate>lastVisibleDate) {
							d=new Date(startdate);
	
							ddMatchResult = false;
							if(ddMatch){
								fvalue = d.dateFormat(format);
								ddMatchResult = ddMatch.test(fvalue);
							}
	//don't use >= and <= here for datecomparison, because the dates can differ in timezone
							if(	!(startdate < min) &&
								!(startdate > max) &&
								ddays.indexOf(d.getDay()) == -1 &&
								!ddMatchResult &&
								( !allowedDatesT || allowedDatesT.indexOf(startdate)!=-1 )
							   ) {
	//is not disabled and can be processed
	
								if (this.maxSelectionDays === this.preSelectedDates.length) {
									if(this.fireEvent("beforemaxdays", this) !== false){								
										Ext.Msg.alert(this.maxSelectionDaysTitle,this.maxSelectionDaysText.replace(/%0/,this.maxSelectionDays));
									}
									break;
								}
								this.preSelectedDates.push(startdate);
	
							}
							startdate = new Date(startdate).add(Date.DAY,dayDiff).clearTime().getTime();
						}
					
						tmpMonthCell = (dayDiff>0 ? 0 : this.cellsArray.length-1);
						tmpDayCell = (dayDiff>0 ? 0 : 41);
	
	//mark left ghostdates aswell
						testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
						while (testCell.hasClass("x-date-prevday") || testCell.hasClass("x-date-nextday")) {
							testCell.addClass("x-date-selected");
							this.preSelectedCells.push((tmpMonthCell)+"#"+(tmpDayCell));
							tmpDayCell+=dayDiff;
							testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
						}
					}
					
	//mark range of visible dates
					while ((targetdate-startdate)*dayDiff >0 && tmpMonthCell>=0 && tmpMonthCell<this.cellsArray.length) {									
						this.markDateAsSelected(startdate,ctrlfaker,tmpMonthCell,tmpDayCell,true);
	
	//take care of summertime changing (would return different milliseconds)
						startdate = new Date(startdate).add(Date.DAY,dayDiff).clearTime().getTime();
										
						testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
	
						if (testCell.hasClass("x-date-active")) {
							ghostCounter=0;						
						}
						else {
							ghostCounter++;
						}
						tmpDayCell+=dayDiff;
						if (tmpDayCell==42) {
							tmpMonthCell++;
							tmpDayCell=(ghostCounter>=7?14:7);
						}
						else if (tmpDayCell<0) {
							tmpMonthCell--;
							tmpDayCell=34;
							
							testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
							if (testCell.hasClass("x-date-nextday") || ghostCounter==7) {
								tmpDayCell=27;
							}
						}
					}
	
				}
				
	
				this.markDateAsSelected(t.dateValue,ctrlfaker,t.monthCell,t.dayCell,true);
					
				this.finishDateSelection(new Date(t.dateValue));
			}
		}
    },
	
	copyPreToSelectedDays : function() {
		this.selectedDates = [];
		for (var i=0,il=this.preSelectedDates.length;i<il;++i) {
			this.selectedDates.push(new Date(this.preSelectedDates[i]));
		}
	},
	okClicked : function() {
		this.copyPreToSelectedDays();
		this.selectedDates = this.selectedDates.sortDates();
		this.fireEvent("select", this, this.selectedDates);
	},

	spaceKeyPressed: function(e) {
		var ctrlfaker = (((!Ext.EventObject.ctrlKey && !this.multiSelectByCTRL) || Ext.EventObject.shiftKey) && this.multiSelection ? true:false);
		if (!this.disabled && this.shiftSpaceSelect == Ext.EventObject.shiftKey && this.showActiveDate) {
			var adCell = this.activeDateCell.split("#");
			var tmpMonthCell = parseInt(adCell[0],10);
			var tmpDayCell = parseInt(adCell[1],10);
			this.markDateAsSelected(this.activeDate.getTime(),ctrlfaker,tmpMonthCell,tmpDayCell,true);
			this.finishDateSelection(this.activeDate);
		}
		else {
			this.selectToday();
		}
	},

	finishDateSelection: function(date) {
        this.setValue(date);		
		if (this.multiSelection) {
			this.fireEvent("afterdateclick", this, date,this.lastStateWasSelected);
		}
		else {
			this.fireEvent("afterdateclick", this, date,this.lastStateWasSelected);				
	        this.fireEvent("select", this, this.value);
		}
	},

    selectToday : function(){
        if(!this.disabled && this.todayBtn && !this.todayBtn.disabled){
			var today = new Date().clearTime();
			var todayT = today.getTime();
		//today already visible?
			if (typeof this.todayMonthCell === "number") {
				this.markDateAsSelected(todayT,false,this.todayMonthCell,this.todayDayCell,true);
			}
			else if (this.multiSelection){
				this.update(today);
			}
			this.finishDateSelection(today);
        }		
    },
	
    setValue : function(value){
		if (Ext.isArray(value)) {
			this.selectedDates = [];
			this.preSelectedDates = [];			
			this.setSelectedDates(value,true);
			value = value[0];
		}
        this.value = value.clearTime(true);

        if(this.el && !this.multiSelection && this.noOfMonth==1){
            this.update(this.value);
        }
		
    },
	
	
	setSize: Ext.emptyFn
	
});
Ext.reg('datepickerplus', Ext.ux.DatePickerPlus);  




	
if (parseInt(Ext.version.substr(0,1),10)>2) {
//ext 3.0		
	Ext.menu.DateItem = Ext.ux.DatePickerPlus;
	Ext.override(Ext.menu.DateMenu,{
		initComponent: function(){
			this.on('beforeshow', this.onBeforeShow, this);
			if(this.strict = (Ext.isIE7 && Ext.isStrict)){
				this.on('show', this.onShow, this, {single: true, delay: 20});
			}
			var PickerWidget = (this.initialConfig.usePickerPlus ? Ext.ux.DatePickerPlus : Ext.DatePicker);
			Ext.apply(this, {
				plain: true,
				showSeparator: false,
				items: this.picker = new PickerWidget(Ext.apply({
					internalRender: this.strict || !Ext.isIE,
					ctCls: 'x-menu-date-item'
				}, this.initialConfig))
			});
			Ext.menu.DateMenu.superclass.initComponent.call(this);
			this.relayEvents(this.picker, ["select"]);
			this.on('select', this.menuHide, this); //this.menuHide is not a function
			if(this.handler){
				this.on('select', this.handler, this.scope || this);
			}
		}
	});
	
}
else {
//ext 2.x
	Ext.menu.DateItem = function(config){
		if (config && config.usePickerPlus) {
			Ext.menu.DateItem.superclass.constructor.call(this, new Ext.ux.DatePickerPlus(config), config);	//NEW LINE			
		}
		else {
			Ext.menu.DateItem.superclass.constructor.call(this, new Ext.DatePicker(config), config);
		}
		this.picker = this.component;
		this.addEvents('select');
		
		this.picker.on("render", function(picker){
			picker.getEl().swallowEvent("click");
			picker.container.addClass("x-menu-date-item");
		});

		this.picker.on("select", this.onSelect, this);
	};
//this breaks in ext 3.0 (Ext.menu.Adapter and Ext.menu.DateItem do not exist in ext 3.0 anymore)
	Ext.extend(Ext.menu.DateItem, Ext.menu.Adapter,{
		// private
		onSelect : function(picker, date){
			this.fireEvent("select", this, date, picker);
			Ext.menu.DateItem.superclass.handleClick.call(this);
		}
	});
}


if (Ext.form && Ext.form.DateField) {
	Ext.ux.form.DateFieldPlus = Ext.extend(Ext.form.DateField, {
		usePickerPlus: true,
		showWeekNumber: true,
		noOfMonth : 1,
		noOfMonthPerRow : 3,
		nationalHolidaysCls: 'x-datepickerplus-nationalholidays',
		markNationalHolidays:true,
		eventDates: function(year) {
			return [];
		},
		eventDatesRE : false,
		eventDatesRECls : '',
		eventDatesREText : '',
		multiSelection: false,
		multiSelectionDelimiter: ',',			
		multiSelectByCTRL: true,	
		fillupRows: true,
		markWeekends:true,
		weekendText:'',
		weekendCls: 'x-datepickerplus-weekends',
		weekendDays: [6,0],
		useQuickTips: true,
		pageKeyWarp: 1,
		maxSelectionDays: false,
		resizable: false,
		renderTodayButton: true,
		renderOkUndoButtons: true,
		tooltipType: 'qtip',
		allowedDates : false,
		allowedDatesText : '',
		renderPrevNextButtons: true,
		renderPrevNextYearButtons: false,
		disableMonthPicker:false,
		showActiveDate: false,
		shiftSpaceSelect: true,
		disabledLetter: false,
		allowMouseWheel:  true,
		summarizeHeader: false,
		stayInAllowedRange: true,
		disableSingleDateSelection: false,
		eventDatesSelectable: false,
		styleDisabledDates: false,

		allowOtherMenus: false,

		onBeforeYearChange : function(picker, oldStartYear, newStartYear){
			this.fireEvent("beforeyearchange", this, oldStartYear, newStartYear, picker);
		},
		
		onAfterYearChange : function(picker, oldStartYear, newStartYear){
			this.fireEvent("afteryearchange", this, oldStartYear, newStartYear, picker);
		},
		
		onBeforeMonthChange : function(picker, oldStartMonth, newStartMonth){
			this.fireEvent("beforemonthchange", this, oldStartMonth, newStartMonth, picker);
		},
		
		onAfterMonthChange : function(picker, oldStartMonth, newStartMonth){
			this.fireEvent("aftermonthchange", this, oldStartMonth, newStartMonth, picker);
		},
		
		onAfterMonthClick : function(picker, month, wasSelected){
			this.fireEvent("aftermonthclick", this, month, wasSelected, picker);
		},
		
		onAfterWeekClick : function(picker, startOfWeek, wasSelected){
			this.fireEvent("afterweekclick", this, startOfWeek, wasSelected, picker);
		},

		onAfterDateClick : function(picker, date, wasSelected){
			this.fireEvent("afterdateclick", this, date, wasSelected, picker);
		},
		
		onBeforeMouseWheel : function(picker, event){
			this.fireEvent("beforemousewheel", this, event, picker);
		},
		
		onBeforeMaxDays : function(picker){
			this.fireEvent("beforemaxdays", this, picker);
		},
		
		onUndo : function(picker, preSelectedDates){
			this.fireEvent("undo", this, preSelectedDates, picker);
		},

		onTriggerClick : function(){
			if(this.disabled){
				return;
			}
			if(!this.menu){
				this.menu = new Ext.menu.DateMenu({
					allowOtherMenus: this.allowOtherMenus,
//is needed at initialisation		
					usePickerPlus:this.usePickerPlus,
					noOfMonth:this.noOfMonth,
					noOfMonthPerRow:this.noOfMonthPerRow,
					listeners: {
						'beforeyearchange': {fn:this.onBeforeYearChange,scope:this},
						'afteryearchange': {fn:this.onAfterYearChange,scope:this},
						'beforemonthchange': {fn:this.onBeforeMonthChange,scope:this},
						'aftermonthchange': {fn:this.onAfterMonthChange,scope:this},
						'afterdateclick': {fn:this.onAfterDateClick,scope:this},
						'aftermonthclick': {fn:this.onAfterMonthClick,scope:this},
						'afterweekclick': {fn:this.onAfterWeekClick,scope:this},
						'beforemousewheel': {fn:this.onBeforeMouseWheel,scope:this},
						'beforemaxdays': {fn:this.onBeforeMaxDays,scope:this},
						'undo': {fn:this.onUndo,scope:this}
					}
				});
//do this only once!					
				this.relayEvents(this.menu, ["select"]);
			}

			if (this.menu.isVisible()) {
				this.menu.hide();
				return;
			}
			if (this.disabledDatesRE) {
				this.ddMatch = this.disabledDatesRE;
			}
			if(typeof this.minDate == "string"){
				this.minDate = this.parseDate(this.minDate);
			}
			if(typeof this.maxDate == "string"){
				this.maxDate = this.parseDate(this.maxDate);
			}
			
			Ext.apply(this.menu.picker,  {
				minDate : this.minValue || this.minDate,
				maxDate : this.maxValue || this.maxDate,
				disabledDatesRE : this.ddMatch,
				disabledDatesText : this.disabledDatesText,
				disabledDays : this.disabledDays,
				disabledDaysText : this.disabledDaysText,
				showToday : this.showToday,	//from Ext 2.2
				format : this.format,
				minText : String.format(this.minText, this.formatDate(this.minValue || this.minDate)),
				maxText : String.format(this.maxText, this.formatDate(this.maxValue || this.maxDate)),
				showWeekNumber: this.showWeekNumber,
				nationalHolidaysCls: this.nationalHolidaysCls,
				markNationalHolidays:this.markNationalHolidays,
				multiSelectByCTRL: this.multiSelectByCTRL,	
				fillupRows: this.fillupRows,
				multiSelection: this.multiSelection,
				markWeekends:this.markWeekends,
				weekendText:this.weekendText,
				weekendCls: this.weekendCls,
				weekendDays: this.weekendDays,
				useQuickTips: this.useQuickTips,
				eventDates: this.eventDates,
				eventDatesRE: this.eventDatesRE,
				eventDatesRECls: this.eventDatesRECls,
				eventDatesREText: this.eventDatesREText,
				pageKeyWarp: this.pageKeyWarp,
				maxSelectionDays: this.maxSelectionDays,
				resizable: this.resizable,
				renderTodayButton: this.renderTodayButton,
				renderOkUndoButtons: this.renderOkUndoButtons,
				allowedDates : this.allowedDates,
				allowedDatesText : this.allowedDatesText,
				renderPrevNextButtons: this.renderPrevNextButtons,
				renderPrevNextYearButtons: this.renderPrevNextYearButtons,
				disableMonthPicker:this.disableMonthPicker,
				showActiveDate: this.showActiveDate,
				shiftSpaceSelect: this.shiftSpaceSelect,
				disabledLetter: this.disabledLetter,
				allowMouseWheel: this.allowMouseWheel,
				summarizeHeader: this.summarizeHeader,
				stayInAllowedRange: this.stayInAllowedRange,
				disableSingleDateSelection: this.disableSingleDateSelection,
				eventDatesSelectable: this.eventDatesSelectable,
				styleDisabledDates: this.styleDisabledDates
			});
//Ext 3.0
			if (this.menuEvents) {
				this.menuEvents('on');
			}
			else {
//ext 2.2.x				
				this.menu.on(Ext.apply({}, this.menuListeners, {
					scope:this
				}));
			}
			this.menu.picker.setValue(this.getValue() || new Date());
			this.menu.show(this.el, "tl-bl?");
			this.menu.focus();
		},
		
		setValue : function(date){
			var field = this;     
			if (Ext.isArray(date)) {
				var formatted = [];
				for (var e=0,el=date.length;e<el;++e) {
					formatted.push(field.formatDate(date[e]));
				}
			
				var value = formatted.join(this.multiSelectionDelimiter);
			
//bypass setValue validation on Ext.DateField
				Ext.form.DateField.superclass.setValue.call(this, value);
			}
			else {
				Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));				   
			}
		},

		validateValue : function(value){
			if (this.multiSelection){
				var field = this;
				var values = value.split(this.multiSelectionDelimiter);
				var isValid = true;
				for (var e=0,el=values.length;e<el;++e) {											  
					if (!Ext.ux.form.DateFieldPlus.superclass.validateValue.call(field, values[e])) {
						isValid = false;
					}
				}
				return isValid;
			}
			else {
				return Ext.ux.form.DateFieldPlus.superclass.validateValue.call(this, value);
			}         
		},

		getValue : function() {
			if (this.multiSelection) {
				var value = Ext.form.DateField.superclass.getValue.call(this);
				var field = this;					
				var values = value.split(this.multiSelectionDelimiter);
				var dates = [];
				for (var e=0,el=values.length;e<el;++e) {											  
					var checkDate = field.parseDate(values[e]);
					if (checkDate) {
						dates.push(checkDate);
					}
				}
				return (dates.length>0?dates:"");
			}
			else {
				return Ext.ux.form.DateFieldPlus.superclass.getValue.call(this);
			}
		},			


		beforeBlur : function(){
			if (this.multiSelection) {
				this.setValue(this.getRawValue().split(this.multiSelectionDelimiter));
			}
			else {
				var v = this.parseDate(this.getRawValue());
				if(v){
					this.setValue(v);
				}
			}
		},


		
		submitFormat:'Y-m-d',
		submitFormatAddon: '-format',			
		onRender:function() {
	
			Ext.ux.form.DateFieldPlus.superclass.onRender.apply(this, arguments);
//be sure not to have duplicate formfield names (at least IE moans about it and gets confused)				
//				this.name =  (typeof this.name==="undefined"?this.id+this.submitFormatAddon:(this.name==this.id?this.name+this.submitFormatAddon:this.name));		
			var name =  this.name || this.el.dom.name || (this.id+this.submitFormatAddon);
			if (name==this.id) {
				name+= this.submitFormatAddon;
			}
			this.hiddenField = this.el.insertSibling({
				tag:'input',
				type:'hidden',
				name: name,
				value:this.formatHiddenDate(this.parseDate(this.value))
			});
			this.hiddenName = name;
			this.el.dom.removeAttribute('name');
			this.el.on({
				keyup:{scope:this, fn:this.updateHidden},
				blur:{scope:this, fn:this.updateHidden}
			});
	
			this.setValue = this.setValue.createSequence(this.updateHidden);
			
			if(this.tooltip){
				if(typeof this.tooltip == 'object'){
					Ext.QuickTips.register(Ext.apply({
						  target: this.trigger
					}, this.tooltip));
				} else {
					this.trigger.dom[this.tooltipType] = this.tooltip;
				}
			}
			
	
		},
		onDisable: function(){
			Ext.ux.form.DateFieldPlus.superclass.onDisable.apply(this, arguments);
			if(this.hiddenField) {
				this.hiddenField.dom.setAttribute('disabled','disabled');
			}
		},
		
		onEnable: function(){
			Ext.ux.form.DateFieldPlus.superclass.onEnable.apply(this, arguments);
			if(this.hiddenField) {
				this.hiddenField.dom.removeAttribute('disabled');
			}
		},
		
		formatHiddenDate : function(date){
			return Ext.isDate(date) ? Ext.util.Format.date(date, this.submitFormat) : date;
		},
		
		formatMultiHiddenDate : function(date) {
			var field = this, formatted = [],value;
			for (var e=0,el=date.length;e<el;++e) {
				formatted.push(field.formatHiddenDate(date[e]));
			}
			value = formatted.join(this.multiSelectionDelimiter);
			this.hiddenField.dom.value = value;
		},
		
		updateHidden:function(date) {
			if (Ext.isArray(date)) {
				this.formatMultiHiddenDate(date);
			}
			else {
				var value = this.getValue();
				if (Ext.isArray(value)) {
					this.formatMultiHiddenDate(value);
				} else {
					this.hiddenField.dom.value = this.formatHiddenDate(value);
				}
			}
		}

	});
	Ext.reg('datefieldplus', Ext.ux.form.DateFieldPlus);
}



Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {
    incrementValue: 1,
    alternateIncrementValue: 5,
    triggerClass: 'x-form-spinner-trigger',
    splitterClass: 'x-form-spinner-splitter',
    alternateKey: Ext.EventObject.shiftKey,
    defaultValue: 0,
    accelerate: false,

    constructor: function(config){
        Ext.ux.Spinner.superclass.constructor.call(this, config);
        Ext.apply(this, config);
        this.mimicing = false;
    },

    init: function(field){
        this.field = field;

        field.afterMethod('onRender', this.doRender, this);
        field.afterMethod('onEnable', this.doEnable, this);
        field.afterMethod('onDisable', this.doDisable, this);
        field.afterMethod('afterRender', this.doAfterRender, this);
        field.afterMethod('onResize', this.doResize, this);
        field.afterMethod('onFocus', this.doFocus, this);
        field.beforeMethod('onDestroy', this.doDestroy, this);
    },

    doRender: function(ct, position){
        var el = this.el = this.field.getEl();
        var f = this.field;

        if (!f.wrap) {
            f.wrap = this.wrap = el.wrap({
                cls: "x-form-field-wrap"
            });
        }
        else {
            this.wrap = f.wrap.addClass('x-form-field-wrap');
        }

        this.trigger = this.wrap.createChild({
            tag: "img",
            src: Ext.BLANK_IMAGE_URL,
            cls: "x-form-trigger " + this.triggerClass
        });

        if (!f.width) {
            this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());
        }

        this.splitter = this.wrap.createChild({
            tag: 'div',
            cls: this.splitterClass,
            style: {
                width:'13px',
                height:'2px;'
            }
        });
        this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();

        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.initTrigger();
        this.initSpinner();
    },

    doAfterRender: function(){
        var y;
        if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {
            this.el.position();
            this.el.setY(y);
        }
    },

    doEnable: function(){
        if (this.wrap) {
            this.wrap.removeClass(this.field.disabledClass);
        }
    },

    doDisable: function(){
        if (this.wrap) {
            this.wrap.addClass(this.field.disabledClass);
            this.el.removeClass(this.field.disabledClass);
        }
    },

    doResize: function(w, h){
        if (typeof w == 'number') {
            this.el.setWidth(w - this.trigger.getWidth());
        }
        this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());
    },

    doFocus: function(){
        if (!this.mimicing) {
            this.wrap.addClass('x-trigger-wrap-focus');
            this.mimicing = true;
            Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {
                delay: 10
            });
            this.el.on('keydown', this.checkTab, this);
        }
    },

    // private
    checkTab: function(e){
        if (e.getKey() == e.TAB) {
            this.triggerBlur();
        }
    },

    // private
    mimicBlur: function(e){
        if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {
            this.triggerBlur();
        }
    },

    // private
    triggerBlur: function(){
        this.mimicing = false;
        Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
        this.el.un("keydown", this.checkTab, this);
        this.field.beforeBlur();
        this.wrap.removeClass('x-trigger-wrap-focus');
        this.field.onBlur.call(this.field);
    },

    initTrigger: function(){
        this.trigger.addClassOnOver('x-form-trigger-over');
        this.trigger.addClassOnClick('x-form-trigger-click');
    },

    initSpinner: function(){
        this.field.addEvents({
            'spin': true,
            'spinup': true,
            'spindown': true
        });

        this.keyNav = new Ext.KeyNav(this.el, {
            "up": function(e){
                e.preventDefault();
                this.onSpinUp();
            },

            "down": function(e){
                e.preventDefault();
                this.onSpinDown();
            },

            "pageUp": function(e){
                e.preventDefault();
                this.onSpinUpAlternate();
            },

            "pageDown": function(e){
                e.preventDefault();
                this.onSpinDownAlternate();
            },

            scope: this
        });

        this.repeater = new Ext.util.ClickRepeater(this.trigger, {
            accelerate: this.accelerate
        });
        this.field.mon(this.repeater, "click", this.onTriggerClick, this, {
            preventDefault: true
        });

        this.field.mon(this.trigger, {
            mouseover: this.onMouseOver,
            mouseout: this.onMouseOut,
            mousemove: this.onMouseMove,
            mousedown: this.onMouseDown,
            mouseup: this.onMouseUp,
            scope: this,
            preventDefault: true
        });

        this.field.mon(this.wrap, "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);
    },

    onMouseOver: function(){
        if (this.disabled) {
            return;
        }
        var middle = this.getMiddle();
        this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
        this.trigger.addClass(this.tmpHoverClass);
    },

    //private
    onMouseOut: function(){
        this.trigger.removeClass(this.tmpHoverClass);
    },

    //private
    onMouseMove: function(){
        if (this.disabled) {
            return;
        }
        var middle = this.getMiddle();
        if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||
        ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {
        }
    },

    //private
    onMouseDown: function(){
        if (this.disabled) {
            return;
        }
        var middle = this.getMiddle();
        this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
        this.trigger.addClass(this.tmpClickClass);
    },

    //private
    onMouseUp: function(){
        this.trigger.removeClass(this.tmpClickClass);
    },

    //private
    onTriggerClick: function(){
        if (this.disabled || this.el.dom.readOnly) {
            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;
    },

    //private
    //checks if control is allowed to spin
    isSpinnable: function(){
        if (this.disabled || this.el.dom.readOnly) {
            Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly
            return false;
        }
        return true;
    },

    handleMouseWheel: function(e){
        //disable scrolling when not focused
        if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {
            return;
        }

        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 (this.isSpinnable() == false) {
            return;
        }
        if (Ext.EventObject.shiftKey == true) {
            this.onSpinUpAlternate();
            return;
        }
        else {
            this.spin(false, false);
        }
        this.field.fireEvent("spin", this.field, this);
        this.field.fireEvent("spinup", this.field, this);
    },

    //private
    onSpinDown: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        if (Ext.EventObject.shiftKey == true) {
            this.onSpinDownAlternate();
            return;
        }
        else {
            this.spin(true, false);
        }
        this.field.fireEvent("spin", this.field, this);
        this.field.fireEvent("spindown", this.field, this);
    },

    //private
    onSpinUpAlternate: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        this.spin(false, true);
        this.field.fireEvent("spin", this.field, this);
        this.field.fireEvent("spinup", this.field, this);
    },

    //private
    onSpinDownAlternate: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        this.spin(true, true);
        this.field.fireEvent("spin", this.field, this);
        this.field.fireEvent("spindown", this.field, this);
    },

    spin: function(down, alternate){
        var v = parseFloat(this.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);
        this.field.setRawValue(v);
    },

    fixBoundries: function(value){
        var v = value;

        if (this.field.minValue != undefined && v < this.field.minValue) {
            v = this.field.minValue;
        }
        if (this.field.maxValue != undefined && v > this.field.maxValue) {
            v = this.field.maxValue;
        }

        return this.fixPrecision(v);
    },

    // private
    fixPrecision: function(value){
        var nan = isNaN(value);
        if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {
            return nan ? '' : value;
        }
        return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));
    },

    doDestroy: function(){
        if (this.trigger) {
            this.trigger.remove();
        }
        if (this.wrap) {
            this.wrap.remove();
            delete this.field.wrap;
        }

        if (this.splitter) {
            this.splitter.remove();
        }

        if (this.dd) {
            this.dd.unreg();
            this.dd = null;
        }

        if (this.proxy) {
            this.proxy.remove();
        }

        if (this.repeater) {
            this.repeater.purgeListeners();
        }
    }
});

//backwards compat
Ext.form.Spinner = Ext.ux.Spinner;

Ext.ns('Ext.ux.form');


Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, {
    actionMode: 'wrap',
    deferHeight: true,
    autoSize: Ext.emptyFn,
    //onBlur: Ext.emptyFn,
    adjustSize: Ext.BoxComponent.prototype.adjustSize,

	constructor: function(config) {
		var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass');

		var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig);

		var plugins = config.plugins
			? (Ext.isArray(config.plugins)
				? config.plugins.push(spl)
				: [config.plugins, spl])
			: spl;

		Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins}));
	},
	
    triggerBlur: function(){
        this.mimicing = false;
        Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
        this.el.un("keydown", this.checkTab, this);
        this.field.beforeBlur();
        this.wrap.removeClass('x-trigger-wrap-focus');
        this.field.onBlur.call(this.field);
    },
	
    // private
    getResizeEl: function(){
        return this.wrap;
    },

    // private
    getPositionEl: function(){
        return this.wrap;
    },

    // private
    alignErrorIcon: function(){
        if (this.wrap) {
            this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
        }
    },

    validateBlur: function(){
        return true;
    }
});

Ext.reg('spinnerfield', Ext.ux.form.SpinnerField);

//backwards compat
Ext.form.SpinnerField = Ext.ux.form.SpinnerField;

//Ext.override(Ext.layout.CardLayout, {
//    setActiveItem : function(item){
//        item = this.container.getComponent(item);
//        if(this.activeItem != item){
//            if (item.rendered && this.animate) {
//
//                Ext.Fx.syncFx();
////              Correct top position of card because they have to occupy the same space
////              during animation.
//                var n = item.getEl();
//                n.setStyle({
//                    position: 'absolute',
//                    top: this.container.getLayoutTarget().getPadding('t') + 'px'
//                });
//                if (this.activeItem) {
//                    this.activeItem.getEl().fadeOut({
//                        useDisplay:true,
//                        callback: Ext.Component.prototype.hide,
//                        scope: this.activeItem
//                    });
//                }
//                this.activeItem = item;
//                item.show();
//                this.container.doLayout();
//                n.fadeIn({
//                    callback: function() {
//                        n.setStyle({
//                            position: ''
//                        });
//                    }
//                });
//                Ext.Fx.sequenceFx();
//            } else {
//                if(this.activeItem){
//                    this.activeItem.hide();
//                }
//                this.activeItem = item;
//                item.show();
//                this.container.doLayout();
//            }
//        }
//    }
//});



var o = Ext.Container.prototype.lookupComponent;
Ext.override(Ext.Container, {

    // Override of our container's method to allow raw Elements to be added.
    // This is called in the context of the container.
    lookupComponent: function(comp) {
        if (comp instanceof Ext.Element) {
            return comp;
        } else if (comp.nodeType && (comp.nodeType == 1)) {
            return Ext.get(comp);
        } else {
            return o.call(this, comp);
        }
    }
});

Ext.layout.CarouselLayout = Ext.extend(Ext.layout.ContainerLayout, {
    constructor: function(config) {
        config = config || {};

//      Non-chunked, then animation makes no sense.
        if (!(config.chunkedScroll || config.pagedScroll)) {
            Ext.applyIf(config, {
                scrollIncrement: 10,
                scrollRepeatInterval: 10,
                marginScrollButtons: 12,
                animScroll: false
            });
        }
        Ext.layout.CarouselLayout.superclass.constructor.call(this, config);

//      Set up animation config depending upon animation requirements.
        if (this.chunkedScroll || this.pagedScroll) {
            this.scrollRepeatInterval = this.scrollDuration * 1000;
            this.scrollAnimationConfig = {
                duration: this.chunkedScroll ? this.scrollDuration : this.scrollDuration * 2,
                callback: this.updateScrollButtons,
                scope: this
            }
        } else {
            this.scrollAnimationConfig = this.animScroll ? {
                duration: this.scrollDuration,
                callback: this.updateScrollButtons,
                scope: this
            } : false;
        }
        
//        if (Ext.isIE){
//	        if (!this.defaults) { this.defaults={} }
//	        if (!this.defaults.bodyStyle) { this.defaults.bodyStyle={} }
//	        this.defaults.bodyStyle.position='static';
//	    }

    },

    
    scrollIncrement : 10,
    
    scrollRepeatInterval : 10,
    
    scrollDuration : .35,
    
    animScroll : true,

    chunkedScroll: false,
    

    // private
    monitorResize: true,
    
    scrollButtonPosition: 'right',

    loopCount: 0,
    loopPictureDelay: 5,


    loopImages: function(){
    	    	
	    if (this.loopCount <= 0){ return }
	    
        var s = this.getScrollTo(1);
        if (s) {
            s = Math.min(this.getMaxScrollPos(), s);
            if(s != this.getScrollPos()) {
                this.scrollTo(s);
            }else{
            	return; 
            }
        }else{
            this.scrollTo(0);
    		this.loopCount--;
	    	if (this.loopCount <= 0){ return }
        }
    	    	
    	this.loopImages.defer(this.loopPictureDelay * 1000, this);
    },


    // private
    onLayout : function(ct, target){
        var cs = ct.items.items, len = cs.length, c, i;

        if(!this.scrollWrap){
            this.scrollWrap = target.createChild({
                cls: 'x-carousel-layout',
                cn: [
	            	{
	                    cls: 'x-carousel-left-scrollbutton',
	                    style: {
	                        height: '100%'
	                    }
	                }, 
	            	{
	                    cls: 'x-carousel-right-scrollbutton',
	                    style: {
	                        height: '100%'
	                    }
	                },
	                {
	                    tag: this.scrollElementTag || 'div',
	                    cls: 'x-carousel-scroller',
	                    cn: {
	                        cls: 'x-carousel-body'
	                    }
	                } 
                ]
            });

//          Add the class that defines element positions
            this.scrollWrap.addClass('x-scroll-button-position-' + this.scrollButtonPosition);

            this.scrollLeft = this.scrollWrap.child('.x-carousel-left-scrollbutton');
            this.scrollRight = this.scrollWrap.child('.x-carousel-right-scrollbutton');
            this.scroller = this.scrollWrap.child('.x-carousel-scroller');
            this.strip = this.scroller.child('.x-carousel-body');
            
            if (this.pagedScroll) {
                this.scrollLeft.on('click',this.onScrollLeftClick, this);
                this.scrollRight.on('click',this.onScrollRightClick, this);
            } else {
                this.leftRepeater = new Ext.util.ClickRepeater(this.scrollLeft, {
                    interval : this.pagedScroll ? 10000 : this.scrollRepeatInterval,
                    delay: 0,
                    handler: this.onScrollLeftClick,
                    scope: this
                });
                this.rightRepeater = new Ext.util.ClickRepeater(this.scrollRight, {
                    interval : this.pagedScroll ? 10000 : this.scrollRepeatInterval,
                    delay: 0,
                    handler: this.onScrollRightClick,
                    scope: this
                });
            }
            this.renderAll(ct, this.strip);
        }
//        this.scroller.setWidth(this.container.getLayoutTarget().getWidth() - (this.scrollLeft.getWidth() + this.scrollRight.getWidth() + this.marginScrollButtons));
        this.updateScrollButtons.defer(10, this);
        
        
    	if (this.loopCount > 0 && (this.chunkedScroll || this.pagedScroll) && !this.startedLoop){ 
    		this.startedLoop=true; 
	    	this.loopImages.defer(this.loopPictureDelay * 1000, this);
    	}

    },

    // private
    renderItem : function(c, position, target){
        if(c) {
            if (c.initialConfig) {
                if (c.rendered){
                    if(typeof position == 'number'){
                        position = target.dom.childNodes[position];
                    }
                    target.dom.insertBefore(c.getEl().dom, position || null);
                } else {
                    c.render(target, position);
                }
            } else if (c instanceof Ext.Element) {
                c.el = c;
                if(typeof position == 'number'){
                    position = target.dom.childNodes[position];
                }
                target.dom.insertBefore(c.dom, position || null);
            }
        }
        c.el.addClass('x-carousel-item');
    },

    // private
    onResize : function(c, position, target){
        Ext.layout.CarouselLayout.superclass.onResize.apply(this, arguments);
		
		if (!this.strip) return; //fixes a weird bug in ext 3.02 where resize was being called before onlayout
		
        if (Ext.isIE) {
            this.scrollLeft.setHeight(this.scroller.getHeight());
            this.scrollRight.setHeight(this.scroller.getHeight());
        }

		this.setItemsEdges();

//      If width increase has introduced spare space to the right, close it up.
        var r = this.getMaxScrollPos();
        if (this.getScrollPos() > r) {
            this.scrollTo(r);
        }
        

    },

	setItemsEdges:function(){
//      Register strip-relative left/right edges for easy chunked scrolling
        var stripLeft = this.strip.getLeft();
        var t = this.container.items.items;
        var lt = t.length;
        for (var i = 0; i < lt; i++) {
            var c = t[i];
            var e = c.el;
            var b = e.getBox();
            var l = b.x - stripLeft;
            var r = b.right - stripLeft;

//          "left" is the leftmost visible pixel.
//          "leftAnchor" is the postition to scroll to to bring the
//          item correctly into view with half the inter-item gap visible.
//          Same principle applies to "right"
            c.edges = {
                left: l,
                leftAnchor: l,
                right: r,
                rightAnchor: r
            };

//          Adjust anchors to be halfway between items.
            if (i == 0) {
                e.setStyle({'margin-left': '0px'});
            } else {
                if (i == t.length - 1) {
                    e.setStyle({'margin-right': '0px'});
                }
                var prev = t[i - 1];
                var halfGap = ((l - prev.edges.right) / 2);
                prev.edges.rightAnchor += halfGap;
                c.edges.leftAnchor -= halfGap;
            } 
        }

//      Work out average item width
        this.itemWidth = t[lt - 1].edges.rightAnchor / lt;
		
	},

    getNextOnLeft: function() {
        var t = this.container.items.items;
        if (t.length) {
            for (var i = t.length - 1; i > -1; i--) {
                if (t[i].edges.left < this.getScrollPos()) {
                    return t[i];
                }
            }
        }
    },


    getNextOnRight: function() {
        var t = this.container.items.items;
        if (t.length) {
            var scrollRight = this.scroller.dom.scrollLeft + this.getClientWidth();
            for (var i = 0, l = t.length; i < l; i++) {
                if (t[i].edges.right > scrollRight) {
                    return t[i];
                }
            }
        }
    },


    onScrollRightClick : function(){
        this.loopCount=0;
        var s = this.getScrollTo(1);
        if (s) {
            s = Math.min(this.getMaxScrollPos(), s);
            if(s != this.getScrollPos()) {
                this.scrollTo(s);
            }
        }
    },


    onScrollLeftClick : function(){
        this.loopCount=0;
        var s = Math.max(0, this.getScrollTo(-1));
        if(s != this.getScrollPos()) {
            this.scrollTo(s);
        }
    },

    // private
    scrollTo : function(pos){

//      Calculate scroll duration based on how far we have to scroll.
        if (this.scrollAnimationConfig) {
            var distance = Math.abs(this.getScrollPos() - pos);
            this.scrollAnimationConfig.duration = this.scrollDuration * (distance / this.itemWidth);
        }

        this.scroller.scrollTo('left', pos, this.scrollAnimationConfig);

//      Scroll animation will have called this in its callback.
        if(!this.scrollAnimationConfig){
            this.updateScrollButtons();
        }
    },

    // private
    updateScrollButtons : function(){
        var pos = this.getScrollPos();
        this.scrollLeft[(pos == 0) ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
        this.scrollRight[(pos >= this.getMaxScrollPos()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
    },

    getScrollWidth : function(){
        var t = this.container.items.items;

        if (t.length && ! t[0].edges){
        	this.setItemsEdges();
        }

        return t.length ? t[t.length - 1].edges.rightAnchor : 0;
    },

    // private
    getScrollPos : function(){
        return this.scroller.dom.scrollLeft || 0;
    },

    getMaxScrollPos: function() {
        return this.getScrollWidth() - this.getClientWidth();
    },

    // private
    getClientWidth : function(){
        return this.scroller.dom.clientWidth || 0;
    },

    // private
    getScrollTo : function(dir){
        var pos = this.getScrollPos();

        if (this.chunkedScroll || this.pagedScroll) {
//          -1 for left, 1 for right
            if (dir == -1) {
                var nextLeft = this.getNextOnLeft();
                if (nextLeft) {
                    if (this.pagedScroll) {
                        return nextLeft.edges.rightAnchor - this.getClientWidth();
                    } else {
                        return nextLeft.edges.leftAnchor;
                    }
                }
            } else {
                var nextRight = this.getNextOnRight();
                if (nextRight) {
                    if (this.pagedScroll) {
                        return nextRight.edges.leftAnchor;
                    } else {
                        return (nextRight.edges.rightAnchor - this.getClientWidth());
                    }
                }
            }
        } else {
            return (dir == -1) ? pos - this.scrollIncrement : pos + this.scrollIncrement;
        }
    },

    // private
    isValidParent : function(c, target){
        return true;
    }

});

Ext.Container.LAYOUTS['carousel'] = Ext.layout.CarouselLayout;


Ext.ux.PanelBlind = Ext.extend(Ext.Panel, {

    constructor: function(config) {
        config = Ext.apply({
            autoHeight: true,
            floating: true,
            cls: 'x-blind',
            bodyStyle: {
                'border-width': '0px'
            },
            buttonAlign: 'center',
            buttons: [{
                text: 'hide',
				iconCls: 'hide-btn',
				scale: 'large',
				//scale: 'small',
				iconAlign: 'left',
                handler: this.dismiss,
                scope: this
            }]
        }, config);
        Ext.ux.PanelBlind.superclass.constructor.call(this, config);
    },

    init: function(client) {
        this.client = client;
        if (!this.client.blinds) {
            this.client.blinds = new Ext.util.MixedCollection();
        }
        client.blinds.add(this);
        //this.client.constructor.prototype.showBlind = this.clientShowBlind;
        //this.client.constructor.prototype.dismissBlind = this.clientDismissBlind;
        this.client.on({
            destroy: this.destroy,
            resize: this.syncSizeWithClient,
            scope: this
        });
    },

    syncSizeWithClient: function() {
        this.setWidth(this.client.body.getSize(true).width);
    },

    //clientShowBlind: function(id) {
    //    var b = this.blinds.get(id);
    //    if (b) {
    //        b.show();
    //    }
    //},
    //
    //clientDismissBlind: function(id) {
    //    var b = this.blinds.get(id);
    //    if (b) {
    //        b.dismiss();
    //    }
    //},
    
    show: function(options) {
    	var mask = (this.useMask ? this.client.getEl().mask() : undefined);
        if (options){ 
        	this.removeRange();
        	this.add(options.items);
        	if (this.rendered) {
        		this.doLayout();
        	}
        }
        if (!this.rendered) {
            this.render(this.client.getEl());
            this.doLayout();
        }

//		Synchronize this blind's z-index to be one above the client Element's mask
       	if (mask){ this.el.setZIndex(Number(mask.getStyle('z-index')) + 1) }

        this.el.disableShadow();
        this.syncSizeWithClient();
        this.el.alignTo(this.client.body, 'tl-tl');
        this.el.slideIn('t', {
            callback: function() {
                this.el.visible = true; // Ext bug. Flag not set causes enableShadow to fail.
                this.el.enableShadow(true);
                
		    	this.items.each(function (item) {
		    		if (typeof item.afterShow == 'function') item.afterShow()	
		    	})
                
            },
            scope: this
        });
    },

    dismiss: function() {
    	
    	this.items.each(function (item) {
    		if (typeof item.beforeDismiss == 'function') item.beforeDismiss()	
    	})

    	
		this.el.disableShadow();
		this.el.slideOut('t');
		this.client.getEl().unmask();
		
        if (this.destroyOnDismiss) {
			(function(){
				this.destroy();
				delete this.client.blinds[this.id];
			}).defer(2000,this);
        }
    }

});

Ext.namespace('Ext.ux');
 

Ext.ux.MultiSelectTextField = function(config) {
 
    // init data
    this.values         = [];
    this.displayValues  = [];
    this.hiddenFields   = [];
 
    Ext.ux.MultiSelectTextField.superclass.constructor.call(this, config);
    
};
 
Ext.extend(Ext.ux.MultiSelectTextField, Ext.form.ComboBox, {
 


 
  
  hideTrigger: true,
  
  // private
  values: [],
  
  // private
  displayValues: [],
  
  // private
  hiddenFields: [],

	delimiter: ',',
	fixedWidth: 0,
	rightAlignValue:false,


    initValue : function(){
        if(!this.ranInitValue){
        	this.ranInitValue=true; 
	        if(this.value !== undefined){
	            this.setValue(this.value);
	        }
	        this.originalValue = this.getValue();
        }		
    },

	getValue: function() {
	    var thisValue='';
	    if (!this.fixedWidth){
	        thisValue=this.values.join(this.delimiter);        	
	    }else{
			for ( var i=0;  i<this.values.length; i++){
				thisValue+=(this.rightAlignValue ? this.padLeft(this.values[i],this.fixedWidth) : this.padRight(this.values[i],this.fixedWidth) );
			}
	    }
	    return thisValue;
	},
  
  
  addValue: function(v, defer)
  {
    var r = this.valueField ? this.findRecord(this.valueField, v) : this.store.getById(v);
    if(!r)
    {
      return;
    }

    var value = this.valueField ? r.data[this.valueField] : r.id;
    var text  = r.data[this.displayField];
    
    // only if the value hasn't already been added
    if (this.values.indexOf(value) == -1)
    { 
      var hidden = this.el.insertSibling(
                    { tag:'input', 
                      type:'hidden', 
                      value: value,
                      name: (this.hiddenName || this.name)}, 
                    'before', true);
      
      this.values.push(value);
      this.displayValues.push(text);
      this.hiddenFields.push(hidden);
    }
    
    if (!defer)
    {
      this.updateDisplay();
    }
  },
  
  
  removeValue: function(v, defer)
  {  
    var r = this.valueField ? this.findRecord(this.valueField, v) : this.store.getById(v);
    if(!r)
    {
      return;
    }

    var value = this.valueField ? r.data[this.valueField] : r.id;
    var text  = r.data[this.displayField];

    var idx = this.values.indexOf(value);
    if (idx == -1)
    {
      return;
    }
    
    this.removeItemAtIndex(idx);
    
    if (!defer)
    {
      this.updateDisplay();
    }
  },

  // private
  removeItemAtIndex: function(idx, defer)
  {
    var field = Ext.get(this.hiddenFields[idx]); 
    field.remove();
    
    this.values[idx]        = null;
    this.displayValues[idx] = null;
    this.hiddenFields[idx]  = null;
    
    if (!defer)
    {
      this.cleanData();
    }
  },

  // private
  cleanData: function()
  {
    this.values         = this.cleanArray(this.values);
    this.displayValues  = this.cleanArray(this.displayValues);
    this.hiddenFields   = this.cleanArray(this.hiddenFields); 
  },
  
  // private  
  cleanArray: function(arr)
  {
    var cleaned = [];
    var len = arr.length;
    for (var i=0; i<len; i++)
    {
      if (arr[i])
      {
        cleaned.push(arr[i]);
      }
    }    
    return cleaned;
  },
  
  
  setValue: function(v)
  {
    this.clearValue();
    
    if (!(v instanceof Array)){
		if (!this.fixedWidth){
      		v = v.split(this.delimiter);
	    }else{
			var newV=[];
			for (var i=0; i < v.length; i += this.fixedWidth){
				newV.push (v.substr(i,this.fixedWidth).trim());
			}
			v=newV;
	    }
    }
    
    var len = v.length;
    for (var i=0; i<len; i++)
    {
      this.addValue(v[i], true);
    }

    
    this.updateDisplay();
  },
  
  // private
  onBlur: function()
  {
    this.updateDisplay();
    Ext.form.ComboBox.superclass.onBlur.call(this);
  },
  
  // private
  updateDisplay: function()
  {
    var text = this.displayValues.join(', ');
    if (text.trim() !== '')
    {
      text += ', ';
    }
      
    Ext.form.ComboBox.superclass.setValue.call(this, text);    
  },
  
  
  clearValue : function()
  {
    this.values = [];
    this.displayValues = [];
    
    var len = this.hiddenFields.length;
    for (var i=0; i<len; i++)
    {
      if (this.hiddenFields[i].remove) { 
		Ext.fly(this.hiddenFields[i]).remove();
      }
    }
    this.hiddenFields = [];
    
    //
    this.setRawValue('');
    this.lastSelectionText = '';
    this.applyEmptyText();
  },
  
  // private
  onSelect : function(record, index)
  {
    if(this.fireEvent('beforeselect', this, record, index) !== false){
      this.addValue(record.data[this.valueField || this.displayField]);
      this.collapse();
      this.fireEvent('select', this, record, index);
    }
  },
    
  // private
  onRender : function(ct, position)
  {
    Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
        
    // prevent input submission
    this.el.dom.removeAttribute('name');
    
    if(Ext.isGecko)
    {
      this.el.dom.setAttribute('autocomplete', 'off');
    }

    if(!this.lazyInit)
    {
      this.initList();
    }
    else
    {
      this.on('focus', this.initList, this, {single: true});
    }

    if(!this.editable)
    {
      this.editable = true;
      this.setEditable(false);
    }
  },
  
  // private
  getLastValue: function()
  {
    var parts = this.getRawValue().split(',');
    return parts[parts.length - 1].trim();
  },
  
  // private
  // Implements the default empty TriggerField.onTriggerClick function
  onTriggerClick : function()
  {
    if(this.disabled)
    {
      return;
    }
    
    if(this.isExpanded())
    {
      this.collapse();
      this.el.focus();
    }
    else
    {
      this.onFocus({});
      if(this.triggerAction == 'all') 
      {
        this.doQuery(this.allQuery, true);
      } 
      else
      {
        this.doQuery(this.getLastValue());
      }
      this.el.focus();
    }
  },
  
  //private
  initQuery : function()
  {
    var val = this.getLastValue();
    if (val.trim() !== '')
    {
      this.doQuery(val);
    }
    this.removeOld();
  },
  
  // private
  // clean out the data from ones you've deleted
  removeOld: function()
  {
    var str   = this.getRawValue();  
    var len   = this.displayValues.length;
    // sorted by length descending
    var items = this.displayValues.slice().sort(function(x,y){ return y.length - x.length; });
    var removed = false;
    
    for (var i=0; i<len; i++)
    {
      var val = items[i];
      if (str.indexOf(val) == -1)
      {
        removed = true;
        this.removeItemAtIndex(this.displayValues.indexOf(val), true);
      }
    }

    if (removed)
    {
      this.cleanData();
      this.updateDisplay();
    }
  },
  
  // private
  fieldParts: function()
  {
    var parts = this.getRawValue().split(',');
    var len = parts.length;
    for (var i=0; i<len; i++)
    {
      parts[i] = parts[i].trim();
    }
    return parts;
  },
  
  //private
  onLoad : function()
  {
    if(!this.hasFocus)
    {
      return;
    }
    
    if(this.store.getCount() > 0)
    {
      this.expand();
      this.restrictHeight();
      
      if(this.lastQuery == this.allQuery)
      {  
        
        if(!this.selectByValue(this.value, true))
        {
          this.select(0, true);
        }
      }
      else
      {
        this.selectNext();
        if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE)
        {
          this.taTask.delay(this.typeAheadDelay);
        }
      }
    }
    else
    {
      this.onEmptyResults();
    }
    //this.el.focus();
  },
  
  validateValue:function(value)
  {  
    if (this.values.length === 0 && !this.allowBlank)
    {
      this.markInvalid(this.blankText);
      return false;
    }
    
    if (this.values.length < this.minLength)
    {
      this.markInvalid(String.format(this.minLengthText, this.minLength));
      return false;
    }
    
    if (this.values.length > this.maxLength)
    {
      this.markInvalid(String.format(this.maxLengthText, this.maxLength));
      return false;
    }
    
    return true;
  },

 

	padLeft:  function (s_text, n_chars, s_pad_char){
		s_text=(!s_text ? '' : String(s_text.trim()));
		s_pad_char=(!s_pad_char ? ' ' : String(s_pad_char));

		var len_text=s_text.length;
		var n_steps=n_chars-len_text;
		for ( var n_counter=0; n_counter < n_steps; n_counter++ ){
			s_text=s_pad_char+s_text;
		}
		s_text=s_text.substr(0,n_chars); //make sure the string is not too long
		return s_text;

	},
	padRight:  function (s_text, n_chars, s_pad_char){
		s_text=(!s_text ? '' : String(s_text.trim()));
		if (!s_pad_char || typeof(n_chars)=='undefined' || !(n_chars>1)) {
			s_pad_char=' ';
		}else{
			s_pad_char=String(s_pad_char);
		}

		for ( ; s_text.length < n_chars; s_text+=s_pad_char ){
		}
		s_text=s_text.substr(0,n_chars); //make sure the string is not too long
		return s_text;
	}
  
});

Ext.reg('multitextfield', Ext.ux.MultiSelectTextField);
Ext.ux.MultiSelectPopupField = Ext.extend(Ext.ux.MultiSelectTextField, {
    
    popupNoFrame: false,
	popupHideTitle: false,
    
    hideTrigger: false,

    getValues: function() {
        return this.values;
    },

    hasValue: function(v) {
        return (this.values.indexOf(v) !== -1);
    },

    onTriggerClick: function() {
//      Always hide the typehead dropdown when the trigger is clicked.
        this.collapse();

        if (!this.gridPanel) {
            this.createGrid();
        } else {
            this.store.clearFilter(true);
            this.gridStore.fireEvent('datachanged', this.gridStore);
        }

//      Trigger toggles between hidden and visible.
        if (this.popup.el.isVisible()) {
            this.hidePopup();
        } else {
            this.popup.show();
            this.popup.el.alignTo(this.el, this.popupAlignTo || "bl");
            this.gridPanel.view.scroller.dom.scrollTop = 0;
            Ext.getDoc().on('mousedown', this.onBodyMousedown, this);
        }
    },

    createGrid: function() {

//      Number of fields in raw ComboBox Record
        this.fields = this.store.recordType.prototype.fields;
        this.fieldCount = this.fields.length;

//      Threshold at which to start folding records into rows.
        if (!this.minItemsForSnake) this.minItemsForSnake = 10;

//      Remove any filter added by ComboBox typeahead activity
        this.store.clearFilter();

        if (this.store.getCount() < this.minItemsForSnake) {
            this.columnCount = 1;
        } else if (!this.columnCount) {
            this.columnCount = 3;
        }

//      If we have no ColumnModel, create one.
        if (!this.colModel) {
            this.colModel = [];
            for (var i = 0; i < this.fieldCount; i++) {
                this.colModel.push({
                    width: this.itemsWidth || 100,
                    dataIndex: this.fields.keys[i]
                });
            }
            this.colModel = new Ext.grid.ColumnModel(this.colModel);
        } else if (Ext.isArray(this.colModel)) {
            this.colModel = new Ext.grid.ColumnModel(this.colModel);
        }

//      First cell in each column group is rendered preceded by a checkbox
        this.colModel.config[0].renderer = this.firstCellRenderer.createDelegate(this);

//      The number of displayable data columns
        this.dataColumns = this.colModel.getColumnCount();

//      See if we need to either categorize or fold.
        if (this.categoryField) {
            this.gridStore = this.categorizeStore();
        } else {

//          If we need to fold the Store create a new Folded store, and a ColumnModel to match
            if (this.store.getCount() > this.minItemsForSnake) {
                this.gridStore = this.convertStore();
                this.colModel = this.convertColumnModel();
            } else {
                this.gridStore = this.store;
            }
        }

//      Disallow row selection.
        var sm = new Ext.grid.RowSelectionModel({
            listeners: {
                beforerowselect: function(){ return false; }
            }
        });

        this.gridPanel = new Ext.grid.GridPanel({
            stateful: false,
            stripeRows: false,
            border: false,
            ds: this.gridStore,
            cm: this.colModel,
            sm: sm,
            listeners: {
                cellClick: this.toggleSelection,
                scope: this
            }
        });
        
        var configWindow={
            stateful: false,
            closeAction: 'hide',
            hidden: true,
            width: this.popupWidth || Math.min(Ext.getBody().getViewSize().width,this.colModel.getTotalWidth() + 40),
            height: this.popupHeight || 300,
            renderTo: Ext.getBody(),
            layout: 'fit',
            items: this.gridPanel,
            listeners: {
                hide: this.onPopupHide,
                scope: this
            }
        };
        
        if (!this.popupHideTitle){
        	configWindow.title = this.popupTitle || "Choose one or more";
        }

        this.popup = (!this.popupNoFrame ? new Ext.Window(configWindow) :  new Ext.Panel(configWindow) );
        
//      Only hide headers if we are NOT using categories.
        if (!this.categoryField) {
            this.gridPanel.el.addClass('multiselect-popup');
        }
    },

    convertStore: function() {

//      Number of Records in the old, linear Store
        var c = this.store.getCount();

//      The number of Records in the new, folded Store
        var newCount = Math.floor((c + (this.columnCount - 1)) / this.columnCount);
        var newData = [];

//      Loop though the Records beginning at each row start, creating a new, longer row.
        for (var i = 0; i < newCount; i++) {

//          The row which incompasses several records from the linear Store
            var rowData = [];

//          The original linear Store indices of each Record added to the row.
            var linearIndices = [];

//          Stride through the Records in the linear Store that will comprise
//          one Record in the folded Store.
            for (var k = 0, j = i; k < this.columnCount && j < c; k++) {
                linearIndices.push(j);
                rowData = rowData.concat(this.store.getAt(j).json);
                j += newCount;
            }
            rowData.linearIndices = linearIndices;
            newData.push(rowData);
        }

//      Create a dummy record layout with the correct number of fields.
        var fields = [];
        for (var i = 0; i < this.columnCount; i++) {
            for (var j = 0; j < this.fields.length; j++) {
                fields.push(this.fields.keys[j] + '-col-' + i);
            }
        }

        return new Ext.data.SimpleStore({
            fields: fields,
            data: newData
        });
    },


    convertColumnModel: function() {
        if (Ext.isArray(this.colModel)) {
            var n = [];
            for (var i = 0; i < this.columnCount; i++) {
                for (var j = 0; j < this.colModel.length; j++) {
                    var di = this.colModel[j].dataIndex;
                    if (typeof di == 'undefined') {
                        di = this.fields.keys[j];
                    }
                    n.push({
                        renderer: this.colModel[j].renderer,
                        width: this.colModel[j].width || 100,
                        dataIndex: di + '-col-' + i
                    });
                }
            }
            return new Ext.grid.ColumnModel(n);
        } else {
            var n = [];
            for (var i = 0; i < this.columnCount; i++) {
                for (var j = 0; j < this.colModel.getColumnCount(); j++) {
                    di = this.colModel.config[j].dataIndex;
                    if (typeof di == 'undefined') {
                        di = this.fields.keys[j];
                    }
                    n.push({
                        renderer: this.colModel.config[j].renderer,
                        width: this.colModel.config[j].width || 100,
                        dataIndex: di + '-col-' + i
                    });
                }
            }
            return new Ext.grid.ColumnModel(n);
        }
    },


    categorizeStore: function() {
        this.categories = {};
        this.categoryNames = [];
        this.columnCount = 0;
        var newCount = 0;
        if (!this.displayField){
	        this.valueField=this.displayField
        }
        for (var i = 0, it = this.store.data.items, l = it.length; i < l; i++) {
            var data=it[i].data;
            var j=[data[this.displayField]];
            j.linearIndex = i;
            
            //var d = Ext.apply({}, data);
            var cat = data[this.categoryField];
            if (!this.categories[cat]) {
                this.categoryNames.push(cat);
                this.categories[cat] = [];
                this.columnCount++;
            }
            this.categories[cat].push(j);
            newCount = Math.max(newCount, this.categories[cat].length);
        }

//      We've lost the category column, so one fewer data columns.
        this.dataColumns=1;

        var newData = [];
        for (var i = 0; i < newCount; i++) {
//          The row which incompasses several records from the linear Store
            var rowData = [];

//          The original linear Store indices of each Record added to the row.
            var linearIndices = [];

//          Arrange categories across a single Record
            for (var j = 0; j < this.columnCount; j++) {
                var colArray = this.categories[this.categoryNames[j]];
                if (typeof(colArray[i])=='undefined'){
                	colArray[i]=[''];
                };
                rowData = rowData.concat(colArray[i]);
                linearIndices.push(colArray[i].linearIndex);
            }
            rowData.linearIndices = linearIndices;
            newData.push(rowData);
        }

//      Create a dummy record layout with the correct number of fields.
        var fields = [];
        for (var i = 0; i < this.columnCount; i++) {
        	fields.push(this.fields.keys[0] + '-col-' + i);
        }

//      Create a ColumnModel with the correct number of fields.
        var cm = [];
        for (var i = 0; i < this.columnCount; i++) {
                cmConfig = {
                    header: this.categoryNames[i],
                    dataIndex: this.fields.keys[0] + '-col-' + i
                };
                cmConfig.renderer = this.firstCellRenderer.createDelegate(this);
                cm.push(cmConfig);
        }
        this.colModel = new Ext.grid.ColumnModel(cm);
        
        return new Ext.data.SimpleStore({
            fields: fields,
            data: newData
        });
    },

    firstCellRenderer: function(value, meta, rec, rowIndex, colIndex, store) {
        this.gridStore.clearFilter(true);
        var rec = this.store.getAt(this.getLinearIndex(rowIndex, colIndex));
        if (!rec){
        	return '&#160;';
        }
        var v = this.valueField ? rec.get(this.valueField) : rec.id;
        var checked = this.hasValue(v) ? 'checked' : '';
        var checkbox = '<input type="checkbox" ' + checked + ' id="' + Ext.id() + '"></input>&#160;';
        return checkbox + value;
    },

    getLinearIndex: function(rowIndex, colIndex) {
        var col = Math.floor(colIndex / this.dataColumns);
        var li = this.gridStore.getAt(rowIndex).json.linearIndices; 

//      If we are using the unconverted Store who's json does NOT have an added
//      "linearIndices" array, then just use the grid's rowIndex.
        return li ? li[col] : rowIndex;
    },

    toggleSelection: function(g, rowIndex, colIndex, e) {
//      Find the checkbox associated with ths clicked column
        var col = Math.floor(colIndex / this.dataColumns);
        var cell = this.gridPanel.view.getCell(rowIndex, col * this.dataColumns);
        var checkbox = Ext.fly(cell).child('input[type=checkbox]', true);

//      Find the index into the linear Store associated with this column
        var linearIndex = this.getLinearIndex(rowIndex, colIndex);

//      Clear any filter used by the ComboBox.
        var rec = this.store.getAt(linearIndex);
        if (rec){
	        var v = this.valueField ? rec.get(this.valueField) : rec.id;
	        if (this.hasValue(v)) {
	            this.removeValue(v);
	            checkbox.checked = false;
	        } else {
	            this.addValue(v);
	            checkbox.checked = true;
	        }
	    }
    },

    hidePopup: function() {
        this.popup.hide();
    },

    onPopupHide: function() {
        Ext.getDoc().un('mousedown', this.onBodyMousedown, this);
    },

    onBodyMousedown: function(e) {
        if (!e.within(this.popup.el) && !(e.getTarget() == this.trigger.dom)) {
            this.popup.hide();
            this.focus(false, 1);
        }
    }

});
Ext.reg('multiselectpopup',Ext.ux.MultiSelectPopupField);



Ext.ux.PanPanel = Ext.extend(Ext.Panel, {

	bodyStyle : {
		'overflow-y':'auto',
		'overflow-x':'auto'
	},

    wheelIncrement : 20,
    constructor : function(config) {
        config.autoHeight = false;
        config.autoWidth = false;
        config.autoScroll = false;
        Ext.ux.PanPanel.superclass.constructor.call(this, config);
    },

    onRender : function() {

        if (this.ownerCt && this.ownerCt.getInnerHeight){
        	this.ownerCt.on('resize', this.onParentResize, this );
        }

        Ext.ux.PanPanel.superclass.onRender.apply(this, arguments);
        this.onParentResize.defer(10,this);

        this.el.on('mousedown', this.onMouseDown, this);
        this.el.on('mousewheel', this.onWheel, this);
        this.el.setStyle('cursor', 'move');

    },
    onParentResize: function(){
    	
        if (this.ownerCt && this.ownerCt.getInnerHeight){
        	
        	this.suspendEvents();
        	this.setHeight(this.ownerCt.getInnerHeight());
        	this.setWidth(this.ownerCt.getInnerWidth());
        	this.resumeEvents();
        }
    },
    onMouseDown : function(e) {
        e.stopEvent();
        this.mouseX = e.getPageX();
        this.mouseY = e.getPageY();
        Ext.getBody().on('mousemove', this.onMouseMove, this);
        Ext.getDoc().on('mouseup', this.onMouseUp, this);
    },


    onWheel : function(e){
        var d = e.getWheelDelta()*this.wheelIncrement*-1;
        e.stopEvent();

        var pos = this.body.dom.scrollTop;
        var newpos = pos + d;
        var sh = this.body.dom.scrollHeight-this.body.dom.clientHeight;

        var s = Math.max(0, Math.min(sh, newpos));
        if(s != pos){
            this.body.dom.scrollTop=s;
        }
    },

    onMouseMove : function(e) {
        e.stopEvent();
        var x = e.getPageX();
        var y = e.getPageY();
        if (e.within(this.body)) {
	        var xDelta = x - this.mouseX;
	        var yDelta = y - this.mouseY;
	        this.body.dom.scrollLeft -= xDelta;
	        this.body.dom.scrollTop -= yDelta;
	    }
        this.mouseX = x;
        this.mouseY = y;
    },

    onMouseUp : function(e) {
        Ext.getBody().un('mousemove', this.onMouseMove, this);
        Ext.getDoc().un('mouseup', this.onMouseUp, this);
    }

});

Ext.reg('panpanel',Ext.ux.PanPanel);


Ext.ux.Wizard=Ext.extend(Ext.Panel,{
    
    stepTitleTpl : '<h3>{stepTitle} [{stepIndex}/{stepCount}]</h3><br>',
    
    // private
    constructor : function(config){
        
        Ext.ux.Wizard.superclass.constructor.call(this, config);
        
//        this.addEvents(            
//            'beforestepchange',            
//            'stepchange',
//            'invalid'
//        );
        var navButtons = this.getNavButtons();
        if (navButtons.prev){
            navButtons.prev.disabled = true;
            if (!navButtons.prev.handler){
                navButtons.prev.handler = this.onPrev.createDelegate(this);
            }
        }

        if (navButtons.next){
            if (!navButtons.next.handler){
                navButtons.next.handler = this.onNext.createDelegate(this);
            }
        }

        if (navButtons.finish){
            navButtons.finish.hidden = true;
            if (!navButtons.finish.handler){
                navButtons.finish.handler = this.onFinish.createDelegate(this);
            }
        }
        
        this.stepTitleItem = this.getStepTitleItem();
        this.stepJumpItem = this.getStepJumpItem();
        this.jumpData = [];
	if (navButtons.cancel){
            if (!navButtons.cancel.handler){
                navButtons.cancel.handler = this.onCancel.createDelegate(this);
            }
        }

    },

    disableButtons: function(){
    	this.updateNavButtons(true);
    },
    
    // private
    addStep : function(item){
        var cardLayout = this.getCardLayout();
        if (Ext.isArray(item)){
            Ext.each(item, function(c){
                this.addStep(c);
            });         
        }else{
            cardLayout.add(item);
            var jd = this.jumpData;
            var i = [jd.length, item.stepTitle];
            jd.push(i);
        }
    },
    
    // private
    onRender : function(ct, position){
        var cardLayout = this.getCardLayout(); 
        Ext.ux.Wizard.superclass.onRender.call(this, ct, position);
        
        (function(){
            var stepIndex = (typeof this.stepIndex == 'undefined' ? 0 : this.stepIndex);
            this.setActiveStep(stepIndex);
            if(this.stepJumpItem) {
                this.jumpCombo = new Ext.form.ComboBox({
                    renderTo: this.stepJumpItem.body,
                    width: this.stepJumpItem.el.getWidth(true),
                    editable: false,
                    triggerAction: 'all',
                    mode: 'local',
                    displayField: 'cap',
                    valueField: 'idx',
                    store: new Ext.data.SimpleStore({
                        fields: ['idx', 'cap'],
                        data: this.jumpData
                    }),
                    listeners: {
                        scope: this,
                        select: function(combo, record, index) {
			    	this.fireEvent('onstepchanged');
				this.setActiveStep(index);
			}
                    }
                });
            }
            this.updateStepTitle();
            this.stepIndex = undefined;
        }).defer(1,this)
    },

    // gets the container for the title
    getStepTitleItem : function(){
        return this.stepTitleItem || (this.stepTitleItem = this.findBy(function(item){ return item.stepTitleItem === true }, this)[0]);              
    },
    
    // gets the container for the jump to combo
    getStepJumpItem : function(){
        return this.stepJumpItem || (this.stepJumpItem = this.findBy(function(item){ return item.stepJumpItem === true }, this)[0]);              
    },

    // returns the navigation buttons
    getNavButtons : function(){ 
        if (!this.navButtons){
            function checkButtons(item, buttonName){ 
                if (item.buttons){
                    var button;
                    Ext.each(item.buttons, function(c){
                        if (c.wizardButton === buttonName){
                            button = c;
                            return false;
                        }
                    });
                    return button;
                }
            }
            
            this.navButtons={'prev':false,'next':false,'finish':false,'cancel':false};
            for (buttonName in this.navButtons){
                
                var button = checkButtons(this, buttonName) || (this.findBy(function(item){ 
                    return item.wizardButton === buttonName || (item.buttons && checkButtons(item, buttonName) );
                },this)[0]);
                
                if (button){
                    this.navButtons[buttonName] = button;
                }
                    
            }
        }
        return this.navButtons;
    },
    
    // returns the layout
    getCardLayout : function(){
        return this.cardLayout || (this.cardLayout = this.findBy(function(item){ return item.layout === 'card' }, this)[0]);     
    },
    
    // returns the current step index
    getStepIndex : function(step){
        var cardLayout = this.getCardLayout();
        return this.cardLayout.items.indexOf(step) || 0;
    },
    
    // returns the total steps
    getStepCount : function(){
        var cardLayout = this.getCardLayout();
        return (!cardLayout.items ? 0 : cardLayout.items.length);
    },
    
    // updates the step title
    updateStepTitle : function(){
        var stepTitleItem=this.getStepTitleItem();
        if (!stepTitleItem){
            return false; 
        }
        
        var stepIndex = this.getStepIndex(this.getActiveStep());
        var stepCount = this.getStepCount();
        if (typeof this.stepTitleTpl =='string' && this.stepTitleTpl!=''){
            this.stepTitleTpl = new Ext.XTemplate(this.stepTitleTpl);
        }

        var activeStep = this.getActiveStep();
        var stepTitle = (activeStep && activeStep.stepTitle ? activeStep.stepTitle : '');

		if (!(typeof this.stepTitleTpl =='string' &&  this.stepTitleTpl=='')){
	        this.stepTitleTpl.overwrite(stepTitleItem.el, {stepIndex: stepIndex+1, stepCount: stepCount, stepTitle: stepTitle} );
	    }
        
        if (this.jumpCombo) {
            this.jumpCombo.setValue(stepIndex);
        }
    },
    
    // updates the navigation buttons
    updateNavButtons : function (disableAll){
        var navButtons = this.getNavButtons();
        var stepIndex = this.getStepIndex(this.getActiveStep());
        var stepCount = this.getStepCount();
		var buttonStatus;
        
		buttonStatus={prev: true, next: true, finish:'hide', cancel:true};
        
        if (stepIndex <=0){
            buttonStatus.prev = false;
        }
        
        if (stepIndex >= stepCount-1){
            buttonStatus.next = 'hide';
            buttonStatus.finish = true;           
        }

		if(this.buttonStatus){
			buttonStatus=this.buttonStatus;
		}

        for (buttonName in navButtons){
            var navButton = navButtons[buttonName];
            var status = buttonStatus[buttonName];
            if (!navButton){
			}else if (navButton && status=='hide'){
                navButton.hide();
            }else{
                navButton.show();
	        	if (disableAll){
	                navButton.setDisabled(true);
	        	}else{
                navButton.setDisabled(!status);
            }           
        }
        }
    },
    setActiveStep: function (step, finishing, initalLoad){
        var cardLayout = this.getCardLayout();
        var stepCount = this.getStepCount();
        if (stepCount == 0){
            this.updateNavButtons();
            return false;

        }else if (!isNaN(step) && ( step<0 || step >= stepCount) ){
            return false;
        }
        var activeStep = this.getActiveStep();

        var newActiveStep=(finishing ? activeStep : this.getCardLayout().layout.container.getComponent(step) );
        
        if(activeStep){
            if (!finishing && activeStep === newActiveStep){ 
                if (!initalLoad){
                	return false;
                }
            }else if (!this.noValidate && !activeStep.noValidate && activeStep.validate && !activeStep.validate()){
                var stepIndex = this.getStepIndex(activeStep);        
                var newStepIndex = this.getStepIndex(newActiveStep);
                if (newStepIndex>=stepIndex){ 
					activeStep.fireEvent("invalid", activeStep, this)
                    return false;
                }
            }else if(activeStep.fireEvent("beforestepchange", activeStep, this) === false){
                return false;
            }
        }                
        if (!finishing){
        	cardLayout.layout.setActiveItem(newActiveStep);        
        	newActiveStep.fireEvent("stepchange", newActiveStep, this);
		}
        this.updateNavButtons();
        this.updateStepTitle();
        return true;
    },
    
    // moves to the next available step
    onNext : function(){
	var activeStep=this.getActiveStep();
	activeStep.fireEvent("onnextclick", activeStep, this);
        var stepIndex = this.getStepIndex(this.getActiveStep());      
        this.setActiveStep(stepIndex + 1);
    },
    
    // moves to the previous available step
    onPrev : function(){
	var activeStep=this.getActiveStep();
	activeStep.fireEvent("onprevclick", activeStep, this);
        var stepIndex = this.getStepIndex(this.getActiveStep());      
        this.setActiveStep(stepIndex - 1);
    },
    
    // commits the wizard select
    onFinish : function(){
	var activeStep=this.getActiveStep();
	activeStep.fireEvent("onfinishclick", activeStep, this);

		if (this.setActiveStep(undefined, true)){
			if (this.afterFinish){
				this.addStep(this.afterFinish);
				var stepCount=this.getStepCount();
				this.setActiveStep(stepCount-1);
				this.disableButtons();
			}
			this.fireEvent("onfinishclick", this.getActiveStep(), this);
		}
    },

    onCancel:function(){
	var activeStep=this.getActiveStep();
	activeStep.fireEvent("oncancelclick", activeStep, this);
    },
    
    // returns the active step
    getActiveStep : function(){
        var cardLayout = this.getCardLayout();
        return cardLayout.layout.activeItem;        
    }

});

Ext.reg('wizard',Ext.ux.Wizard);
Ext.namespace('Ext.ux');

Ext.ux.Image = Ext.extend(Ext.BoxComponent, {
	
	src : Ext.BLANK_IMAGE_URL,
	
	
	autoEl : {
		tag : 'img',
		cls : 'tng-managed-image',
		src : Ext.BLANK_IMAGE_URL		
	},
	
	
	initComponent: function () {		
		Ext.ux.Image.superclass.initComponent.apply(this, arguments);
	},
	
	
	onRender: function(){
		Ext.ux.Image.superclass.onRender.apply(this, arguments);
		
		if(!Ext.isEmpty(this.src) && (this.src !== Ext.BLANK_IMAGE_URL)){
			this.setSrc(this.src);
		}
		this.relayEvents(this.el, 
			[
				"click", "dblclick", "mousedown", "mouseup", "mouseover",
				"mousemove", "mouseout", "keypress", "keydown", "keyup"
			]
		);				
	},
	
	
	setSrc: function (src) {
		this.el.dom.src = src;
	}
});

Ext.reg('image', Ext.ux.Image);





Ext.ux.RowActionSingle = function(config) {
	Ext.apply(this, config);

	this.addEvents({
		 beforeaction:true
		,action:true
	});

	if(!this.tpl) {
		// create template
		this.tpl = new Ext.Template(this.tplCt.replace(/\{items\}/, this.getTpl() ));
	} 

	if(this.autoWidth) { 
		this.width =  this.widthSlope + this.widthIntercept;
		this.fixed = true;
	}

	// setup renderer
	if(!this.renderer) {
		this.renderer = function(value, cell, record, row, col, store, container) {
			cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell';
            var result = this.tpl.apply(this.getData(value, cell, record, row, col, store));
            if (container) {
                Ext.fly(container.parentNode).addClass('ux-row-action-cell');
                container.innerHTML = result;
            }
			return result;
		}.createDelegate(this);
	}

	Ext.ux.RowActionSingle.superclass.constructor.call(this);
}

Ext.extend(Ext.ux.RowActionSingle, Ext.util.Observable, {

	autoWidth:true,
	
	header:'',

	
	menuDisabled:true,

	
	sortable:false,

	name:'', 
	index:undefined, 
	iconCls:'',
	text:'', 
	style:'',
	tooltip:'',
	
	tplItem:'<div class="ux-row-action-item {cls}" {qtip} actionIndex={actionIndex} {style}>{text}</div>',
	tplItemInline:'<span class="ux-row-action-item" {qtip} actionIndex={actionIndex} {style}><img src={blankImage} class="ux-row-action-blank-icon {cls}"/>{text}</span>',
	inlineTpl:undefined,
	tplCt:'<div class="ux-row-action">{items}</div>',

	
	widthIntercept:4,

	
	widthSlope:21,


	
	getTpl: function( inlineTpl ){
		var tpl = (inlineTpl && !this.noBlankImg ? this.tplItemInline : this.tplItem);

		tpl = tpl.replace(/\{actionIndex\}/, this.index);		
		tpl = tpl.replace(/\{blankImage\}/, Ext.BLANK_IMAGE_URL);


		var rText= '&#160;';
		if(this.textIndex) {
			rText= '<span>{' + this.textIndex + '}</span>'; 
		}else if(this.text) {
			rText= '<span>' + this.text + '</span>';
		}
		tpl = tpl.replace(/\{text\}/,rText);
		
		var rCls='';	
		if(this.iconIndex) {
			rCls = ' {' + this.iconIndex + '}';
		}else if(this.iconCls) {
			rCls = ' ' + this.iconCls;
		}
		if(this.cls) {
			rCls += ' ' + this.cls;
		}
		tpl = tpl.replace(/\{cls\}/,rCls);
		
		var rQtip='';
		if(this.qtipIndex) {
			rQtip = ' qtip="{' + this.qtipIndex + '}"';
		}else if(this.tooltip || this.qtip) {
			rQtip = ' qtip="' + (this.tooltip || this.qtip) + '"';
		}
		tpl = tpl.replace(/\{qtip\}/,rQtip);
		
		// setup style		
		var rStyle = (this.style ? ' style="' +this.style + '"' : '' );
		tpl = tpl.replace(/\{style\}/,rStyle);
		return tpl;
			
	},
	
	getInline: function(value, cell, record, row, col, store){		
		
		if (!this.inlineTpl) { 
			this.inlineTpl=new Ext.Template(this.getTpl(true)); 
		} 		
		return this.inlineTpl.apply(this.getData(value, cell, record, row, col, store));
		
	},

	getData:function(value, cell, record, row, col, store) {
		return (record ? record.data : {});
	},
	
	getColumn:function(){
		
	}
	
	
});

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


Ext.ux.RowActions.prototype = {

	 autoWidth:true,

	
	header:'',

	
	menuDisabled:true,

	
	sortable:false,

	
	tplCt:'<div class="ux-row-action">{items}</div>',

	
	
	widthIntercept:4,

	
	widthSlope:21,
	
	processedActions:false,

	// methods
	

	getData:function(value, cell, record, row, col, store) {
		return (record ? record.data : {});
	},
	 
	get: function (key){
		if (!this.processedActions){ this.initActions() } 

		var action=this.actions.get(key);		
		//action.index=this.actions.indexOfKey(key);
		return action;
	},

	 //private
	getInline: function(action, value, cell, record, row, col, store){
		if (!this.processedActions){ this.initActions() } 

		var actionTemp=this.actions.get(action);
		if (!actionTemp){
			return alert('Did not load the button:'+action);
		}		
		return actionTemp.getInline(value, cell, record, row, col, store);
		
	},
	initActions: function(){
		this.processedActions=true;
		this.initialActions=this.actions;
		this.actions = new Ext.util.MixedCollection(false, function(o){
        	return o.name || o.iconCls || o.text || Ext.id() ;
	    });
	    
	    Ext.each(this.initialActions, function(actionConfig){
	    	actionConfig.index=this.actions.getCount();
	    	var action=new Ext.ux.RowActionSingle(actionConfig);
	    	this.actions.add(action);
	    },this);
	    

		
	},
	
	init:function(obj) { 
		
//		this.mode=obj.getXType(); //fails for any subclass of grid )
				
		if (!this.processedActions){ 
			this.initActions() 
		} 
		
		if(!this.tpl) {
			var ts=[];
			this.actions.each(function(action){
				ts.push(action.getTpl());			
			});
		
			// create template
			this.tpl = new Ext.Template(this.tplCt.replace(/\{items\}/, ts.join('')));
		}
		
		
		// calculate width
		if(this.autoWidth) { 
			this.width =  this.widthSlope * this.actions.getCount() + this.widthIntercept;
			this.fixed = true;
		}
		
		
		if (obj instanceof Ext.DataView){
			
			this.dataview=obj;
			obj.on({
				click:{scope:this, fn: this.onClickDataView}
			});
			
		}else if (obj instanceof Ext.grid.GridPanel){
			// setup renderer
			if(!this.renderer) {
				this.renderer = function(value, cell, record, row, col, store, container) {
					cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell'; 
					var result = this.tpl.apply(this.getData(value, cell, record, row, col, store));
                    if (container) {
                        Ext.fly(container.parentNode).addClass('ux-row-action-cell');
                        container.innerHTML = result;
                    }
                    return result;
				}.createDelegate(this);
			}
			
			this.grid = obj;
			var view = obj.getView();
			view.cellSelectorDepth=5;
			
			obj.on({
				render:{scope:this, fn:function() {
					view.mainBody.on({
						click:{scope:this, fn:this.onClickGrid}
					});
				}}
			});
						
			// this keeps group from toggling when you click on a button
			if(view.groupTextTpl) {
				view.interceptMouse = view.interceptMouse.createInterceptor(function(e) {
					if(e.getTarget('.ux-row-action-item') ) {
						return false;
					}
				});
			}
		}


	},
	
	onClickDataView: function(view2, rowIndex, node, e) {

		var t = e.getTarget('.ux-row-action-item');
		if(!t ) {
			return
		}

		var actionIndex=Number(t.getAttribute('actionIndex'));

		var action=this.actions.get(actionIndex);
		if(true !== this.eventsSuspended && false === action.fireEvent('beforeaction', this, this.dataview, record, action, rowIndex, undefined, iconCls) ) {
			return;
		}

		var record = this.dataview.store.getAt(rowIndex);
		var iconCls=t.className.replace(/ux-row-action-item /, '');
		
		action.fireEvent('action', this, this.dataview, record, action, rowIndex, undefined, iconCls);


	},

	
	
	
	onClickGrid:function(e, target) {
		
		var t = e.getTarget('.ux-row-action-item');
		if(!t ) {
			return
		}

		var row = e.getTarget('.x-grid3-row');
		var col, rowIndex=false, view=this.grid.getView(), store=this.grid.store;
	
		if (false !== row && null !=row ) {				
			col = view.findCellIndex(e.getTarget());
			rowIndex=row.rowIndex;
		}else{
            var groupingRow=e.getTarget('.x-grid-group');
            if (groupingRow){
				var groupId=groupingRow.id.replace(/\&/,'&amp;');
                rowIndex=store.data.findIndex('_groupId',groupId);
				if (rowIndex==-1) {
					Ext.jx.debug('can not match to groupid');
				}				
            }				
		}
		if (rowIndex !==false && rowIndex!=-1){
			var record = store.getAt(rowIndex);
			var iconCls=t.className.replace(/ux-row-action-item /, '')
			var actionIndex=Number(t.getAttribute('actionIndex'));

			var action=this.actions.get(actionIndex);
			if(true !== this.eventsSuspended && false === action.fireEvent('beforeaction', this, this.grid, record, action, rowIndex, col, iconCls) ) {
				return;
			}
			
			action.fireEvent('action', this, this.grid, record, action, rowIndex, col, iconCls);
		}
		
	} 

};



//Ext.grid.ColumnModel.prototype.findColumnIndex = function(dataIndex){
//	var c = this.config;
//	for(var i = 0, len = c.length; i < len; i++){
//		if(c[i].dataIndex == dataIndex){
//			return typeof c[i].id != 'undefined' ? c[i].id : i;
//		}
//	}
//	return -1;
//}


Ext.ux.MultiGroupingStore = Ext.extend(Ext.data.GroupingStore, {
	constructor: function(config) {
		Ext.ux.MultiGroupingStore.superclass.constructor.apply(this, arguments);
	},

	sortInfo: [],

	sort: function(field, dir) {
		var f = [];
		if (Ext.isArray(field)) {
			for (var i = 0, len = field.length; i < len; ++i) {
				if (field[i]) f.push(this.fields.get(field[i])); //jb: added empty field check
			}
		} else {
			f.push(this.fields.get(field));
		}

		if (f.length < 1) {
			return false;
		}

		if (!dir) {
			if (this.sortInfo && this.sortInfo.length > 0 && this.sortInfo[0].field == f[0].name) { // toggle sort dir
				dir = (this.sortToggle[f[0].name] || "ASC").toggle("ASC", "DESC");
			} else {
				dir = f[0].sortDir;
			}
		}

		var st = (this.sortToggle) ? this.sortToggle[f[0].name] : null;
		var si = (this.sortInfo) ? this.sortInfo: null;

		this.sortToggle[f[0].name] = dir;
		this.sortInfo = [];
		for (var i = 0, len = f.length; i < len; ++i) {
			this.sortInfo.push({
				field: f[i].name,
				direction: dir
			});
		}

		if (!this.remoteSort) {
			this.applySort();
			this.fireEvent("datachanged", this);
		} else {
			if (!this.load(this.lastOptions)) {
				if (st) {
					this.sortToggle[f[0].name] = st;
				}
				if (si) {
					this.sortInfo = si;
				}
			}
		}

	},

	setDefaultSort: function(field, dir) {
		dir = dir ? dir.toUpperCase() : "ASC";
		this.sortInfo = [];

		if (!Ext.isArray(field)) {
			this.sortInfo.push({
				field: field,
				direction: dir
			});
		} else {
			for (var i = 0, len = field.length; i < len; ++i) {
				this.sortInfo.push({
					field: field[i].field,
					direction: dir
				});
				this.sortToggle[field[i]] = dir;
			}
		}
	},

	groupBy: function(field, forceRegroup) {
		if (!forceRegroup && this.groupField == field) {
			return; // already grouped by this field
		}

		if (this.groupField) {
			for (var z = 0; z < this.groupField.length; z++) if (field == this.groupField[z]) return;
			this.groupField.push(field);
		} else {
			this.groupField = [field];
		}

		if (this.remoteGroup) {
			if (!this.baseParams) {
				this.baseParams = {};
			}
			this.baseParams['groupBy'] = field;
		}
		if (this.groupOnSort) {
			this.sort(field);
			return;
		}
		if (this.remoteGroup) {
			this.reload();
		} else {
			var si = this.sortInfo || [];
			if (si.field != field) {
				this.applySort();
			} else {
				this.sortData(field);
			}
			this.fireEvent('datachanged', this);
		}
	},

	applySort: function() {

		var si = this.sortInfo;

		if (si && si.length > 0 && !this.remoteSort) {
			this.sortData(si, si[0].direction);
		} else
			if (!this.groupOnSort && !this.remoteGroup) {
				var gs = this.getGroupState();
				if (gs && gs != this.sortInfo) {

					this.sortData(this.groupField);
				}
			}
	},

	getGroupState: function() {
		return this.groupOnSort && this.groupField !== false ? (this.sortInfo ? this.sortInfo: undefined) : this.groupField;
	},

	sortData: function(flist, direction) {
		//alert('sortData '+ direction);
		direction = direction || 'ASC';

		var st = [];

		var o;
		for (var i = 0, len = flist.length; i < len; ++i) {
			o = flist[i];
			st.push(this.fields.get(o.field ? o.field: o).sortType);
		}

		var fn = function(r1, r2) {

			var v1 = [];
			var v2 = [];
			var len = flist.length;
			var o;
			var name;

			for (var i = 0; i < len; ++i) {
				o = flist[i];
				name = o.field ? o.field: o;

				v1.push(st[i](r1.data[name]));
				v2.push(st[i](r2.data[name]));
			}

			var result;
			for (var i = 0; i < len; ++i) {
				result = v1[i] > v2[i] ? 1 : (v1[i] < v2[i] ? -1 : 0);
				if (result != 0) return result;
			}

			return result; //if it gets here, that means all fields are equal
		};

		this.data.sort(direction, fn);
		if (this.snapshot && this.snapshot != this.data) {
			this.snapshot.sort(direction, fn);
		}
	}

});

Ext.ux.MultiGroupingView = Ext.extend(Ext.grid.GroupingView, {
	constructor: function(config) {
		Ext.ux.MultiGroupingView.superclass.constructor.apply(this, arguments);
		// Added so we can clear cached rows each time the view is refreshed
		this.on("beforerefresh",
		function() {
			if (this.rowsCache) {
				delete rowsCache;
			}
		},this);

//      A place in which to place the documentFragment generated by the Ext.ux.DomGridView
        this.offscreenRowCache = Ext.getBody().createChild({
            cls: 'x-hide-offsets'
        }, null, true);

//      We need the cell data to be rendered *immediately* in the MultiGroupingView
        this.renderCellDefer = 0;

	},


	displayEmptyFields: false,


	displayFieldSeparator: ', ',

	getMultiGroupId : function(groupField, value){
	   var gidPrefix = this.grid.getGridEl().id;
	   var colIndex = this.cm.findColumnIndex(groupField);
	   var cfg = this.cm.config[colIndex];
	   var groupRenderer = cfg.groupRenderer || cfg.renderer;
	   var gtext = this.getGroup(value, {data:{}}, groupRenderer, 0, colIndex, this.ds);
	   return gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(value);
	},


    processRows : function(startRow, skipStripe){
//        if(!this.ds || this.ds.getCount() < 1){
//            return;
//        }
        var rows = this.getRows();

        if (!rows.length) return

        skipStripe = skipStripe || !this.grid.stripeRows;
        startRow = startRow || 0;
        Ext.each(rows, function(row, idx){
            row.rowIndex = idx;
            row.className = row.className.replace(this.rowClsRe, ' ');
            if (!skipStripe && (idx + 1) % 2 === 0) {
                row.className += ' x-grid3-row-alt';
            }
        });
        // add first/last-row classes
        if(startRow === 0){
            Ext.fly(rows[0]).addClass(this.firstRowCls);
        }
        Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
    },
	
	
	renderRows: function() {
		var groupField = this.getGroupField();
		var eg = !!groupField;
		// if they turned off grouping and the last grouped field is hidden
		if (this.hideGroupedColumn) {
			var colIndexes = [];
			for (var i = 0, len = groupField.length; i < len; ++i) {
				var cidx = this.cm.findColumnIndex(groupField[i]);
				if (cidx >= 0) {
					colIndexes.push(cidx);
				}
			}
			if (!eg && this.lastGroupField !== undefined) {
				this.mainBody.update('');
				for (var i = 0, len = this.lastGroupField.length; i < len; ++i) {
					var cidx = this.cm.findColumnIndex(this.lastGroupField[i]);
					if (cidx >= 0) {
						this.cm.setHidden(cidx, false);
					} else {
						alert("Unhide Col: " + cidx);
					}
				}
				delete this.lastGroupField;
				delete this.lgflen;
			} else if (eg && colIndexes.length > 0 && this.lastGroupField === undefined) {
				this.lastGroupField = groupField;
				this.lgflen = groupField.length;
				for (var i = 0, len = colIndexes.length; i < len; ++i) {
					this.cm.setHidden(colIndexes[i], true);
				}
			} else if (eg && this.lastGroupField !== undefined && (groupField !== this.lastGroupField || this.lgflen != this.lastGroupField.length)) {
				this.mainBody.update('');
				for (var i = 0,len = this.lastGroupField.length; i < len; ++i) {
					var cidx = this.cm.findColumnIndex(this.lastGroupField[i]);
					if (cidx >= 0) {
						this.cm.setHidden(cidx, false);
					}
				}
				this.lastGroupField = groupField;
				this.lgflen = groupField.length;
				for (var i = 0, len = colIndexes.length; i < len; ++i) {
					this.cm.setHidden(colIndexes[i], true);
				}
			}
		}
		return Ext.ux.MultiGroupingView.superclass.renderRows.apply(this, arguments);
	},

	

	doRender: function(cs, rs, ds, startRow, colCount, stripe) {
		if (rs.length < 1) {
			return '';
		}

		var groupField = this.getGroupField();
		var gfLen = groupField.length;

		var ss = this.grid.getTopToolbar();


		if (ss){
			// Remove all entries alreay in the toolbar
			for (var hh = 0; hh < ss.items.length; hh++) {
				Ext.removeNode(Ext.getDom(ss.items.itemAt(hh).id));
			}

			if (gfLen == 0) {
				ss.addItem(new Ext.Toolbar.TextItem("Drop Columns Here To Group"));
			} else {
				// Add back all entries to toolbar from GroupField[]
				ss.addItem(new Ext.Toolbar.TextItem("Grouped By:"));
				for (var gfi = 0; gfi < gfLen; gfi++) {
					var t = groupField[gfi];
					if (gfi > 0) {
						ss.addItem(new Ext.Toolbar.Separator());
					}
					var b = new Ext.Toolbar.Button({
						text: this.cm.lookup[this.cm.findColumnIndex(t)].header
					});
					b.fieldName = t;
					ss.addItem(b);
				}
			}
		}

		this.enableGrouping = !!groupField;

		if (!this.enableGrouping || this.isUpdating) {
			return Ext.grid.GroupingView.superclass.doRender.apply(this, arguments);
		}

		var gstyle = 'width:' + this.getTotalWidth() + ';';
		var gidPrefix = this.grid.getGridEl().id;
		var groups = [], curGroup, i, len, gid;
		var lastvalues = [];
		var added = 0;
		var currGroups = [];

		// Create a grid-specific stylesheet.
        // Always best to use Jack's classes where possible!
        if (this.stylesheet) {
            Ext.util.CSS.removeStyleSheet(gidPrefix + "-style");
        }
        this.stylesheet = Ext.util.CSS.createStyleSheet(
            "div#" + gidPrefix + " div.x-grid3-row {padding-left:" + (gfLen * 12) + "px}" +
            "div#" + gidPrefix + " div.x-grid3-header {padding-left:" + (gfLen * 12) + "px}", gidPrefix + "-style");


		for (var i = 0, len = rs.length; i < len; i++) {
			added = 0;
			var rowIndex = startRow + i;
			var r = rs[i];
			var differ = 0;
			var gvalue = [];
			var fieldName;
			var fieldLabel;
			var grpFieldNames = [];
			var grpFieldLabels = [];
			var v;
			var changed = 0;
			var addGroup = [];

			for (var j = 0; j < gfLen; j++) {
				fieldName = groupField[j];
				var colIndex=this.cm.findColumnIndex(fieldName);
				var col=this.cm.lookup[colIndex];
				if (!col){
					colIndex=this.cm.findColumnIndex(fieldName);
					break;
				}
				fieldLabel = col.header;
				v = r.data[fieldName];

				if (v !== undefined) {
					if (i == 0) {
						// First record always starts a new group
						addGroup.push({
							idx: j,
							dataIndex: fieldName,
							header: fieldLabel,
							value: v
						});
						lastvalues[j] = v;

						gvalue.push(v);
						grpFieldNames.push(fieldName);
						grpFieldLabels.push(fieldLabel + ': ' + v);
						//gvalue.push(v); ????
					} else {
						if (lastvalues[j] != v) {
							// This record is not in same group as previous one
							//console.debug("Row ",i," added group. Values differ: prev=",lastvalues[j]," curr=",v);
							addGroup.push({
								idx: j,
								dataIndex: fieldName,
								header: fieldLabel,
								value: v
							});
							lastvalues[j] = v;
							changed = 1;

							gvalue.push(v);
							grpFieldNames.push(fieldName);
							grpFieldLabels.push(fieldLabel + ': ' + v);
						} else {
							if (gfLen - 1 == j && changed != 1) {
								// This row is in all the same groups to the previous group
								curGroup.rs.push(r);
							} else if (changed == 1) {
								// This group has changed because an earlier group changed.
								addGroup.push({
									idx: j,
									dataIndex: fieldName,
									header: fieldLabel,
									value: v
								});

								gvalue.push(v);
								grpFieldNames.push(fieldName);
								grpFieldLabels.push(fieldLabel + ': ' + v);
							} else if (j < gfLen - 1) {
								// This is a parent group, and this record is part of this parent so add it
								if (currGroups[fieldName]) {
									currGroups[fieldName].rs.push(r);
								}

							}
						}
					}
				} else {
					if (this.displayEmptyFields) {
						addGroup.push({
							idx: j,
							dataIndex: fieldName,
							header: fieldLabel,
							value: this.emptyGroupText || '(none)'
						});
						grpFieldNames.push(fieldName);
						grpFieldLabels.push(fieldLabel + ': ');
					}
				}
			} //for j


			for (var k = 0; k < addGroup.length; k++) {
				var gp = addGroup[k];
				g = gp.dataIndex;
				var glbl = addGroup[k].header;
				// There is no current group, or its not for the right field, so create one
				gid = gidPrefix + '-gp-' + gp.dataIndex + '-' + Ext.util.Format.htmlEncode(gp.value);

				// if state is defined use it, however state is in terms of expanded
				// so negate it, otherwise use the default.
				var isCollapsed = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;
				var gcls = isCollapsed ? 'x-grid-group-collapsed': '';
				var groupFieldStyle=(!ds.groupFieldStyles ? '' : ds.groupFieldStyles[gp.dataIndex] || '');

				curGroup = {
					group: gp.dataIndex,
					gvalue: gp.value,
					text: gp.header,
					groupId: gid,
					startRow: rowIndex,
					rs: [r],
					cls: gcls,
					style: gstyle + 'padding-left:' + (gp.idx * 12) + 'px;'+groupFieldStyle
				};
				currGroups[gp.dataIndex] = curGroup;
				groups.push(curGroup);
				r._groupId = gid; // Associate this row to a group
			} //for k
		} //for i

		var buf = [];
		var toEnd = 0;
		for (var ilen = 0, len = groups.length; ilen < len; ilen++) {
			toEnd++;
			var g = groups[ilen];
			var leaf = g.group == groupField[gfLen - 1];
			this.doMultiGroupStart(buf, g, cs, ds, colCount);

			if (g.rs.length != 0 && leaf) {
                var rows = Ext.grid.GroupingView.superclass.doRender.call(this, cs, g.rs, ds, g.startRow, colCount, stripe);

//              Process accordingly depending on whether we are using Ext's GridView which produces a String,
//              or Ext.ux.DomGridView which produces a DocumentFragment.
                if (typeof rows == 'string') {
    				buf[buf.length] = rows;
                } else {
                    this.offscreenRowCache.innerHTML = '';
                    this.offscreenRowCache.appendChild(rows);
                    buf[buf.length] = this.offscreenRowCache.innerHTML;
                }
			}

			if (leaf) {
				var jj;
				var gg = groups[ilen + 1];
				if (gg != null) {
					for (var jj = 0; jj < groupField.length; jj++) {
						if (gg.group == groupField[jj]) {
							break;
						}
					}
					toEnd = groupField.length - jj;
				}
				for (var k = 0; k < toEnd; k++) {
					this.doMultiGroupEnd(buf, g, cs, ds, colCount);
				}
				toEnd = jj;
			}

		}

		return buf.join('');
	},

	initTemplates: function() {
		Ext.grid.GroupingView.superclass.initTemplates.call(this);
		this.state = {};

		var sm = this.grid.getSelectionModel();
		sm.on(sm.selectRow ? 'beforerowselect': 'beforecellselect', this.onBeforeRowSelect, this);


        if(!this.startMultiGroup){
            this.startMultiGroup = new Ext.XTemplate(
                '<div id="{groupId}" class="x-grid-group {cls}">',
                    '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
                    '<div id="{groupId}-bd" class="x-grid-group-body">'
            );
        }
		this.startMultiGroup.compile();
		this.endMultiGroup = '</div></div>';
	},

	doMultiGroupStart: function(buf, g, cs, ds, colCount) {
		ds.groupFieldClasses=ds.groupFieldClasses || {};
		ds.groupFieldTemplates=ds.groupFieldTemplates || {};
		ds.groupFieldClasses[g.group]=ds.groupFieldClasses[g.group] || 'x-grid-group-hd';
		ds.groupFieldTemplates[g.group]=ds.groupFieldTemplates[g.group] || this.groupTextTpl;

		if (typeof(ds.groupFieldTemplates[g.group]) == 'string') {
			ds.groupFieldTemplates[g.group] = new Ext.XTemplate(
				'<div id="{groupId}" class="x-grid-group {cls}">',
					'<div id="{groupId}-hd" class="',ds.groupFieldClasses[g.group],'" style="{style}"><div class="x-grid-group-title">',
						ds.groupFieldTemplates[g.group],
					'</div></div>',
					'<div id="{groupId}-bd" class="x-grid-group-body">');
			ds.groupFieldTemplates[g.group].compile();
		}
		buf[buf.length] = ds.groupFieldTemplates[g.group].apply(g);

	},


	doMultiGroupEnd: function(buf, g, cs, ds, colCount) {
		buf[buf.length] = this.endMultiGroup;
	},

	
	getRows: function() {

		// This function is called may times, so use a cache if it is available
		if (this.rowsCache) {
			r = this.rowsCache.slice(0);
		} else {
			if (!this.enableGrouping) {
				return Ext.grid.GroupingView.superclass.getRows.call(this);
			}
			var groupField = this.getGroupField();
			var r = [];
			var g, gs = this.getGroups();
			// this.getGroups() contains an array of DIVS for the top level groups
			r = this.getRowsFromGroup(r, gs, groupField[groupField.length - 1]);

			// Clone the array, but not the objects in it
		}
		return r;
	},

	
	getRowsFromGroup: function(r, gs, lsField) {
		var rx = new RegExp(".*-gp-" + lsField + "-.*");

		// Loop over each section
		for (var i = 0, len = gs.length; i < len; i++) {

			// Get group name for this section
			var groupName = gs[i].id;
			if (rx.test(groupName)) {
				g = gs[i].childNodes[1].childNodes;
				for (var j = 0, jlen = g.length; j < jlen; j++) {
					r[r.length] = g[j];
				}
			} else {
				if (!gs[i].childNodes[1]) {
				} else {
					r = this.getRowsFromGroup(r, gs[i].childNodes[1].childNodes, lsField);
				}
			}
		}
		return r;
	}
});

Ext.ux.MultiGroupingPanel = function(config) {
	config = config || {};
	config.tbar = new Ext.Toolbar({
		id: 'grid-tbr'
	});
	Ext.ux.MultiGroupingPanel.superclass.constructor.call(this, config);
};
Ext.extend(Ext.ux.MultiGroupingPanel, Ext.grid.GridPanel, {

	initComponent: function() {
		Ext.ux.MultiGroupingPanel.superclass.initComponent.call(this);

		// Initialise DragZone
		this.on("render", this.setUpDragging, this);
	},

	setUpDragging: function() {
		this.dragZone = new Ext.dd.DragZone(this.getTopToolbar().getEl(), {
			ddGroup: "grid-body",
			panel: this,
			scroll: false,
			onInitDrag: function(e) {
				// alert('init');
				var clone = this.dragData.ddel;
				clone.id = Ext.id('ven');
				// clone.class='x-btn button';
				this.proxy.update(clone);
				return true;
			},
			getDragData: function(e) {
				var target = Ext.get(e.getTarget().id);
				if (target.hasClass('x-toolbar x-small-editor')) {
					return false;
				}

				d = e.getTarget().cloneNode(true);
				d.id = Ext.id();
				//console.debug("getDragData",this, target);

				this.dragData = {
					repairXY: Ext.fly(target).getXY(),
					ddel: d,
					btn: e.getTarget()
				};
				return this.dragData;
			}

			//Provide coordinates for the proxy to slide back to on failed drag.
			//This is the original XY coordinates of the draggable element.
			,
			getRepairXY: function() {
				return this.dragData.repairXY;
			}

		});

		// This is the target when columns are dropped onto the toolbar (ie added to the group)
		this.dropTarget2s = new Ext.dd.DropTarget('grid-tbr', {
			ddGroup: "gridHeader" + this.getGridEl().id,
			panel: this,
			notifyDrop: function(dd, e, data) {
				var btname = this.panel.getColumnModel().getDataIndex(this.panel.getView().getCellIndex(data.header));
				this.panel.store.groupBy(btname);
				return true;
			}
		});

		// This is the target when columns are dropped onto the grid (ie removed from the group)
		this.dropTarget22s = new Ext.dd.DropTarget(this.getView().el.dom.childNodes[0].childNodes[1], {
			ddGroup: "grid-body",
			panel: this,
			notifyDrop: function(dd, e, data) {
				var txt = Ext.get(data.btn).dom.innerHTML;
				var tb = this.panel.getTopToolbar();
				var bidx = tb.items.findIndexBy(function(b) {
					return b.text == txt;
				},this);
				if (bidx < 0) return; // Error!
				var fld = tb.items.get(bidx).fieldName;

				// Remove from toolbar
				Ext.removeNode(Ext.getDom(tb.items.get(bidx).id));
				if (bidx > 0) {
					Ext.removeNode(Ext.getDom(tb.items.get(bidx - 1).id));;
				}

				var cidx = this.panel.view.cm.findColumnIndex(fld);

				this.panel.view.cm.setHidden(cidx, false);

				var temp = [];

				for (var i = this.panel.store.groupField.length - 1; i >= 0; i--) {
					if (this.panel.store.groupField[i] == fld) {
						this.panel.store.groupField.pop();
						break;
					}
					temp.push(this.panel.store.groupField[i]);
					this.panel.store.groupField.pop();
				}

				for (var i = temp.length - 1; i >= 0; i--) {
					this.panel.store.groupField.push(temp[i]);
				}

				if (this.panel.store.groupField.length == 0) {
					this.panel.store.groupField = false;
				}

				this.panel.store.fireEvent('datachanged', this);
				return true;
			}
		});

	}
});



Ext.override(Array, {
    findBy: function(fn) {
        var result = [];
        for (var i = 0, len = this.length; i < len; i++){
            if(fn.call(this || scope, this[i])) {
                result.push(this[i]);
            }
        }
        if (result.length != 0) return result;
    }
});

Ext.override(String, {
    endsWith: function(s) {
        return this.substring(this.length - s.length) == s;
    }
});



Ext.ux.Sound = (function(){

    var hasFlash = (navigator.plugins && Array.prototype.findBy.call(navigator.plugins, function(p){return p.name.indexOf('Flash')!==-1;}));

//  Disabled if Windows Gecko without the QuickTime plugin
    var  FFWin = (Ext.isGecko && Ext.isWindows), enabled = !FFWin || (navigator.plugins && Array.prototype.findBy.call(navigator.plugins, function(p){return p.name.indexOf('QuickTime')!==-1;}));
    if (!enabled) {
        return {
            enable: Ext.emptyFn,
            disable: Ext.emptyFn,
            play: Ext.emptyFn
        };
    }

    var tracks = {};

    return {

    

        enable: function(){
            enabled = true;
        },

    

        disable: function(){
            enabled = false;
        },

        
        play: function(url, options){
            if(!enabled) return;

            var options = Ext.apply({
              track: 'global',
              url: url,
              replace: false
            }, options);

            if(options.replace && tracks[options.track]) {
                for (var i = 0; i <= tracks[options.track].id; i++) {
                    var sound = Ext.get('sound_' + options.track + '_' + i);
                    sound.dom.Stop && sound.dom.Stop();
                    sound.remove();
                }
                tracks[options.track] = null;
            }

            if(tracks[options.track]) {
                tracks[options.track].id++;
            } else {
                tracks[options.track] = { id: 0 }
            }

            options.id = tracks[options.track].id;
            var sound;

//          Flash not working yet.
            if (options.url.endsWith('.swf') && hasFlash) {
                var objectId = 'sound_' + options.track + '_' + options.id;
                var SWFconfig = {
                  tag: 'object',
                  cls: 'x-hide-offsets',
                  cn: [
                      {tag: 'embed',
                          src: options.url,
                          type: 'application/x-shockwave-flash',
                          quality: 'high'
                      },
                      {tag: 'param', name: 'quality', value: 'high'},
                      {tag: 'param', name: 'movie', value: options.url}
                    ]
                };

                if (Ext.isIE) {
                    SWFconfig.classid = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
                    SWFconfig.codebase = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"
                    SWFconfig.id = objectId;
                } else {
                    SWFconfig.cn[0].id = objectId;
                }

                Ext.DomHelper.useDom = true;
                Ext.getBody().createChild(SWFconfig, null, true);
                Ext.getDom(objectId).Play();
                Ext.DomHelper.useDom = false;
                return;
            } else if (Ext.isIE) {
                sound = document.createElement('bgsound');
                sound.setAttribute('src',options.url);
                sound.setAttribute('loop','1');
                sound.setAttribute('autostart','true');
            } else if (FFWin && !options.url.endsWith('.wav')) {
                sound = document.createElement('object');
                sound.setAttribute('type','audio/mpeg');
                sound.setAttribute('data',options.url);
            } else {
                sound = document.createElement('embed');
                sound.setAttribute('src',options.url);
                sound.setAttribute('hidden','true');
                sound.setAttribute('loop','false');
                sound.setAttribute('autostart','true');
            }
            sound.className = 'x-hide-offsets';
            sound.setAttribute('id','sound_'+options.track+'_'+options.id);
            document.body.appendChild(sound);
        }
    };
})();


Ext.ns('Ext.jx');


Ext.jx.Server = {

	servers:(function(){
		var hash={};
		var currentServer=window.location.href.substr(0,window.location.href.length-window.location.search.length);

        if (/\#/.test(currentServer)) currentServer = currentServer.split("#")[0]; // strip pound sign
        if (/\?/.test(currentServer)) currentServer = currentServer.split("?")[0]; // strip question mark

		hash[currentServer]=undefined; 
		return hash;
	})(),

	getPrimary:function(){
		var lowServer;
		for (var serverName in Ext.jx.Server.servers){
			lowServer=Ext.jx.Server.servers[serverName] || (Ext.jx.Server.servers[serverName]={serverName:serverName, loadCount:0});
			break;
		}
		lowServer.loadCount++;
		lowServer.lastAccess=Date();
		return lowServer.serverName;
	},

	get:function(){
		var lowServer;
		for (var serverName in Ext.jx.Server.servers){
			var thisServer=Ext.jx.Server.servers[serverName];
			if (!thisServer){
				lowServer=Ext.jx.Server.servers[serverName]={serverName:serverName, loadCount:0};
				break;
			}else if(!thisServer.loadCount){
				lowServer=thisServer;
				break;
			}else if (!lowServer || thisServer.loadCount < lowServer.loadCount || (thisServer.loadCount == lowServer.loadCount && thisServer.lastAccess < lowServer.lastAccess)){
				lowServer=thisServer;
			}
		}

		lowServer.loadCount++;
		lowServer.lastAccess=Date();
		return lowServer.serverName;
	},

	release:function(serverName){
		var thisServer=Ext.jx.Server.servers[serverName];
		thisServer.loadCount--;
	}
}

Ext.ns('RL.Vendor');


RL.Vendor.Map = Ext.extend(Ext.ux.GoogleMapPanel, {

	//vendor record
	vendorRecord : null,

	constructor : function (config) {

		var vendorRecord = config.vendorRecord;

		Ext.apply(config, {
			title:'Map',

			mapType:'terrain',
			renderedSafe:true,

			controls:['zoom','menumaptype','scale'],
			zoom:14,

			border: false,

			markers: [{
				address: vendorRecord.getAddress(),
				icon:'building',
				title:vendorRecord.get('VENDOR#VENDOR'),
				html:vendorRecord.template('longdescription'),
				maxWidth:300
			}]
		});
		//eof apply

		RL.Vendor.Map.superclass.constructor.call(this, config);
	}

});
//eof extend

Ext.reg('vendor_map', RL.Vendor.Map);


//Ext.ns('RL.Vendor');
//
//
//RL.Vendor.Map = Ext.extend(Ext.Panel, {
//
//	//vendor record
//	vendorRecord 			: null,
//
//	
//	constructor : function (config) {
//
//		var vendorRecord = config.vendorRecord;
//
//		Ext.apply(config, {
//			title		: 'Map',
//			
//			//when rendered into hidden DOM element this prevents the map from actual rendering
//			layout 		: 'fit',
//			
//			hideBorders : true,
//			
//			items : [
//				{
//					xtype 		: 'gmappanel',
//					minGeoAccuracy	: 1,
//					zoomLevel 	: 14,
//					
////					gmapType	: 'G_PHYSICAL_MAP',
//		
//					mapControls	: [ 'GSmallZoomControl', 'GMenuMapTypeControl', 'GScaleControl'],
//					
//					setCenter	: {
//						geoCodeAddr : vendorRecord.getAddress()
//					}
//				}
//			]
//
//			
////			,
////
////			markers: [{
////				address: vendorRecord.getAddress(),
////				icon:'building',
////				title:vendorRecord.get('VENDOR#VENDOR'),
////				html:vendorRecord.template('longdescription'),
////				maxWidth:300
////			}]
//		});
//		//eof apply
//
//		RL.Vendor.Map.superclass.constructor.call(this, config);
//	}
//
//});
////eof extend
//
//Ext.reg('vendor_map', RL.Vendor.Map);

Ext.ns('Ext.jx');

Ext.jx.External = {

	includeJs : function (s_filename, f_callback) {
		var o_js = document.createElement('script');
		o_js.setAttribute('language', 'javascript');
		o_js.setAttribute('type', 'text/javascript');
		o_js.setAttribute('src', s_filename);

		if (f_callback){

			o_js.onreadystatechange = function () {
				if (o_js.readyState == 'complete' || o_js.readyState == 'loaded') {
					if (o_js.getAttribute('b_fired_onload')!=1){
						o_js.setAttribute('b_fired_onload', 1);
						f_callback();
					}
				}
			}

			o_js.onload = function () {
				if (o_js.getAttribute('b_fired_onload')!=1){
					o_js.setAttribute('b_fired_onload', 1);
					f_callback();
				}
			}
		}

		var o_doc = document.getElementsByTagName('head').item(0);
		o_doc.appendChild(o_js);

		return false;
	},
	includeCss : function (s_filename, f_callback) {
		//<link rel="stylesheet" href=wrm_client/old.css type="text/css">
		var o_css = document.createElement('link');
		o_css.setAttribute('rel', 'stylesheet');
		o_css.setAttribute('type', 'text/css');
		o_css.setAttribute('href', s_filename);

		if (f_callback){

			o_css.onreadystatechange = function () {
				if (o_css.readyState == 'complete' || o_css.readyState == 'loaded') {
					if (o_css.getAttribute('b_fired_onload')!=1){
						o_css.setAttribute('b_fired_onload', 1);
						f_callback();
					}
				}
			}

			o_css.onload = function () {
				if (o_css.getAttribute('b_fired_onload')!=1){
					o_css.setAttribute('b_fired_onload', 1);
					f_callback();
				}
			}
		}

		var o_doc = document.getElementsByTagName('head').item(0);
		o_doc.appendChild(o_css);

		return false;
	}
}
Ext.ns('Ext.jx');


Ext.jx.Template = {
	searchLists:{},
	clearSearchItems: function (searchList, recordValues){
		var Template=Ext.jx.Template;
		if (typeof(searchList)==='string'){
			searchList=Template.searchLists[searchList] || (Template.searchLists[searchList]=[]);
		}
		while (searchList.length){
			searchList.pop();
		}
		return ''; 

	},
	setSearchItem: function (searchList, recordValues){
		var Template=Ext.jx.Template;
		if (typeof(searchList)==='string'){
			searchList=Template.searchLists[searchList] || (Template.searchLists[searchList]=[]);
		}
		searchList.push(recordValues);
		return ''; 
	},
	findSearchItem: function (searchList, index){
		var Template=Ext.jx.Template;
		if (typeof(searchList)==='string'){
			searchList=Template.searchLists[searchList];
		}
		return searchList[index];
	}

}
Ext.ns('Ext.jx');

Ext.jx.Ajax = {

	urlEncode:function(o){
		if(!o){return""}
		var buf=[];
		for(var key in o){
			var ov=o[key],k=encodeURIComponent(key);
			var type=typeof ov;
			if(type=="undefined"){
				buf.push(k,"=&")
			}else{
				if(type!="function" && type!="object" ){
					buf.push(k,"=",encodeURIComponent(ov),"&");
				}else{
					if(Ext.isArray(ov)){
						if(ov.length){
							for(var i=0,len=ov.length;i<len;i++){
								buf.push(k,"=",encodeURIComponent(ov[i]===undefined?"":ov[i]),"&");
							}
						}else{
							buf.push(k,"=&");
						}
					}else if(Ext.isDate(ov)){
						buf.push(k,"=",encodeURIComponent(ov.dateFormat("m/d/Y")),"&");
					}else{
						buf.push(k,"=",encodeURIComponent(Ext.jx.Ajax.urlEncode(ov)),"&");
					}
				}
			}
		}
		buf.pop();
		return buf.join("")
	},

	cleanFieldNamesForPost: function(values, dest, scopePrefix){
		if (typeof(dest)=='undefined'){
			dest={}
		}

		if (!scopePrefix){
			scopePrefix='';
		}else{
			scopePrefix+='#';
		}

		Ext.jx.each(values, function(value, key){
			if (/ext-comp-\d+/.test(key)) return
			
			var varType=Ext.type(value);
			if (varType=='string'){
				if (value==""){ return }

			}else if (varType=='date'){
				value=value.dateFormat("m/d/Y");				
			}else if (varType=='object'){
				if ( Ext.isDate(value) ){
					value=value.dateFormat("m/d/Y");
				}else if ( value.format0 ) {
					value=value.format0();
				}
			}
			var newKey=scopePrefix+key.replace(/\#/,'.');
			dest[newKey]=value;
		});
		return dest;
	}
}
Ext.ns('Ext.jx');

Ext.jx.Table = function(config) {

	this.getForm=function(formName){
		var table=this;
		if (!table.forms){ table.forms={} }
		var form=table.forms[formName];
		if (!form){ alert('Can not find form '+table.tableName+'::'+formName) }

		return form;
	};

	this.getData=function(dataName, options){
		var table=this;
		if (!table.dataStorage){ table.dataStorage={} }
		if (!dataName){ dataName='ALL' }

		var data=table.dataStorage[dataName];
		if (!data && (dataName=='ALL' || (options && options.remote))){ data=table.setData(dataName,{}) }
		if (!data ){ alert('Can not find data '+table.tableName+'::'+dataName) }
		if (!data.getStore ){
			table.setData(dataName, data);
		}
		return data;
	};

	this.setMetaDataDefaults= function(dataName, data){
		var table=this;
		Ext.apply(data, Ext.jx.TableData);
		data.getTable=function(){ return table };

		//set some defaults
		if (!data.dataName){ data.dataName=dataName; }
		var meta=data.meta;
		if (!meta){ meta=data.meta={} }
		if (!meta.root){meta.root='rows'}
		if (!data[meta.root]){data[meta.root]={}};
		if (!meta.totalProperty ){meta.totalProperty='results'}
		if (!meta.id){ meta.id='#LINK_TO_'+table.tableName}
		if (!meta.tableName){ meta.tableName=table.tableName }

		if (!meta.fieldsIndex){ meta.fieldsIndex={}; }

		//make sure fields are loaded
		if (!meta.fields){
			meta.fields=[];
			if (table.fields){
				Ext.jx.each(table.fields, function(defaultField, key){
					var field={};
					Ext.apply(field, defaultField);
					field.name=key
					if (meta.fieldsIndex[key]){ return; }
					meta.fieldsIndex[key]=field;
					meta.fields.push(field);
				})
			}else if(data && data.rows && data.rows[0] ){
				Ext.jx.each(data.rows[0], function(value, key){
					var field={};
					var defaultField=table.fields[key];
					if (defaultField) { Ext.apply(field, defaultField) }
					field.name=key;
					if (meta.fieldsIndex[key]){ return; }
					meta.fieldsIndex[key]=field;
					meta.fields.push(field);
				})
			}else{
				Ext.jx.debug('Did not specify fields');
				return;
			}
		}else{
			Ext.jx.each(meta.fields, function(field, key){
				meta.fieldsIndex[key]=field;
				var defaultField=table.fields[key];
				if (defaultField) { Ext.applyIf(field, defaultField) }
			})
		}
	};

	this.setData= function(dataName, data){
		var table=this;

		if (!table.dataStorage){ table.dataStorage={} }

		if (!data && typeof dataName != 'string' ){
			data=dataName;
			dataName='ALL'
		}
		if (!dataName){ dataName='ALL' }

		if (!data){
			Ext.jx.debug('Did not specify data '+table.tableName+'::'+dataName);
		}else{
			table.dataStorage[dataName]=data;
			table.setMetaDataDefaults(dataName, data);
		}
		return data;
	};

	this.FuncsRow= function(){ 
		return {
//			popupChild : function (tableName){
//
//			},
			getRecord: function (tableName){
				var record=this;
				if ( record.parentTables && record.parentTables[tableName] ){
					return record.parentTables[tableName];
				}
				return '';
			},
			getValue: function (columnName){
				var record=this;
				if (typeof(record.data[columnName])!='undefined'){
					return record.data[columnName];
				}
				if ( record.parentTables ){
					for ( var parentTableName in record.parentTables ){

						var parentTable=record.parentTables[parentTableName];
						if ( typeof(parentTable.record.data[columnName])!='undefined' ){
							return parentTable.record.data[columnName];
						}
					}
				}
				return '';
			},

			template : function (templateName){
				var record=this;
				var table=record.getTable(), tpl;
				if (!table.templates || (!(tpl=table.templates[templateName]))) { return '' }
		        if(typeof tpl == "string"){
		            tpl = new Ext.XTemplate(tpl);
		        }else if(Ext.isArray(tpl)){
		            tpl = new Ext.XTemplate(tpl.join(''));
		        }
				return tpl.applyTemplate( Ext.DataView.prototype.prepareData(record.data,null, record) );
			}
		};
	}();

	this.Funcs=function(){ 
		return {
			updateServer: function (){

			}
		};
	}();

	this.applyDefinition=function(config){
		if (!config){ return }
		for(var s_key in config ){
			var o_item=config[s_key];
			if (o_item=== null || o_item === undefined){ continue }
			if (this[s_key] && (s_key == 'Funcs' || s_key == 'FuncsRow' || s_key == 'forms' || s_key=='templates' )){
				Ext.apply(this[s_key],config[s_key]);
			}else{
				this[s_key]=config[s_key];
			}
		}
	}

	this.applyDefinition(config);

}
Ext.ns('Ext.jx');

Ext.jx.String = {
	reverse: function (s){ return s.split("").reverse().join("") },
	addStripTags: /\/?\/?-->/gi,
	stripTags:function (s, len){
		s=Ext.util.Format.stripTags(Ext.jx.ignoreNull(s)).replace(Ext.jx.String.stripTags,"")
		if (len && s.length > len){
			s=s.substr(0,len)+'...'
		}
		return s;
	},

	replaceSubstr : function (valueOrig, value, substrStart, substrLen){
		var valueFull;
		valueOrig=String(valueOrig);  value=String(value);
		if (!isNaN(substrLen)){
			var before=valueOrig.substr(0,substrStart);
			var after=valueOrig.substr(substrStart+substrLen);
			value=value.substr(0,substrLen);
			valueFull=before+value+after;

		}else{
			var before=valueOrig.substr(0,substrStart);
			valueFull=before+value;
		}
		return valueFull;
	}
}

Ext.jx.stripTags=Ext.jx.String.stripTags;

Ext.ns('Ext.jx');

Ext.jx.math = {

	num : function(v, defaultValue){
		if (typeof(defaultValue)=='undefined') { defaultValue=0 }
        if (typeof v != 'number'){
            v=Number(v);
        	if(typeof(v)  != 'number'){
            	return defaultValue;
            }
        }
        return v;
	},

	safe_divide : function (n_number, n_divisor ) {
		return (Ext.jx.math.num(n_divisor) ==0 ? 0 : Ext.jx.math.num(n_number)/ Ext.jx.math.num(n_divisor));
	},

	safe_divide_round : function (n_number, n_divisor, n_rounding_digit){
		if (typeof(n_rounding_digit)=='undefined') { n_rounding_digit=2 }
		var n_return=Ext.jx.math.round(Ext.jx.math.safe_divide(n_number, n_divisor),n_rounding_digit);
		return n_return;
	},

	generate_random : function (n_max, n_min){
		if (typeof(n_max)=='undefined') { return 0 }
		if (typeof(n_min)=='undefined') { n_min=0 }

		var n_random=Math.floor(Math.random() * (n_max - n_min + 1) + n_min)
		return n_random;
	},

	round: function (n_number, n_digits){
		if (typeof(n_digits)=='undefined') { n_digits=1 }
		var n_rounder=Math.pow(10,n_digits);
		var n_return=Math.round(n_number * n_rounder)/n_rounder;
		return n_return;
	},

	regExPosNeg: /(\-?)(\d*)\.?(\d*)/,
	formatCurrency: function (value, currency){
		var htmlFormat;//todo: get the format from the db dump
		var analyze=Ext.jx.math.analyzeCurrency(htmlFormat), match=Ext.jx.math.regExPosNeg.exec(value), trailingI=0, i;
		var returns=analyze.currencyPrefix+(match[1] || ''), numReverse='';
		var work=Ext.jx.String.reverse(match[2]), workLen=work.length;

		for (i=analyze.digitsGroupingLast; i< workLen; i+=analyze.digitsGroupingLast){
			if (trailingI) numReverse+=analyze.groupingSeperator;
			numReverse+=work.substr(trailingI, i-trailingI);
			trailingI=i;
		}

		if ( trailingI < workLen ) {
			if (trailingI) numReverse+=analyze.groupingSeperator;
			numReverse+=work.substr(trailingI);
		}

		returns+=Ext.jx.String.reverse(numReverse);

		if (analyze.decimalPlaces){
			returns+=analyze.decimalSeperator;
			returns+=String(Math.round(Math.pow(10,analyze.decimalPlaces)*Number('1.'+match[3]))).substr(1);
		}

		returns+=analyze.currencyPostfix
		return returns;
	},

	htmlFormat: '$123,456,789.00',
	//htmlFormat: "123'456'789,00p",
	//htmlFormat: "&#129; 123'456'789,00 &#123;",
	analyzeCache:{},
	regexCurrencyAnalyze:/(.*)(1.?2.?3.?4.?5.?6.?7.?8.?9)(\D*)(0*)(.*)/,
	analyzeCurrency: function (htmlFormat){
		if (!htmlFormat || htmlFormat == '') htmlFormat=Ext.jx.math.htmlFormat;
		if (Ext.jx.math.analyzeCache[htmlFormat]) return Ext.jx.math.analyzeCache[htmlFormat];
		var matchHtmlFormat=Ext.jx.math.regexCurrencyAnalyze.exec(htmlFormat), posNumFormat=Ext.jx.String.reverse(matchHtmlFormat[2]), reference=[], trailingIndex =0;
		var matchNumFormat= /\D+/g.exec(posNumFormat);
		var segment=matchNumFormat ? posNumFormat.substr(0,matchNumFormat.index) : posNumFormat;

		return Ext.jx.math.analyzeCache[htmlFormat]={
			currencyPrefix: matchHtmlFormat[1] || '',
			currencyPostfix: matchHtmlFormat[5] || '',
			decimalSeperator: matchHtmlFormat[3] || '.',
			decimalPlaces: typeof matchHtmlFormat[4]  == 'string' ? matchHtmlFormat[4].length : 0,
			groupingSeperator: matchNumFormat ? posNumFormat.substr(segment.length, matchNumFormat[0].length) : '',
			digitsGroupingLast: segment.length
		};
	}


}

Ext.ns('Ext.jx');

Ext.jx.Object= {
	deepCopy : function (o_source, h_params ) { 

		if (!h_params){h_params={} };
		if (!h_params.h_exclusions){h_params.h_exclusions={} };
		var vars=Ext.jx.vars;

		var o_new,a_object_loop=[];
		if (!vars.n_deep_copy_count){vars.n_deep_copy_count=0};
		var s_rep_field="-REP-ID-DC-"+(++vars.n_deep_copy_count);

		Ext.jx.Prototypes.Pause();
		var o_override_key, o_override_value;


		if (typeof(o_source) === "object" && o_source!=null && typeof(o_source.length) != "undefined"){ 
			o_new=[];
			for (var n_index=0; n_index < o_source.length; n_index++ ){
				var s_key=n_index;
				var s_value=o_source[n_index];

				if (typeof(s_value) === "object" && s_value!=null){
					a_object_loop.push({s_key:s_key, s_value: s_value, o_source:o_source, o_dest: o_new, s_path:''});
				}else{
					o_new[s_key]=s_value;
				}
			}
		}else if (typeof(o_source) === "object"){
			o_new={};
			for (var s_key in o_source){
				var s_value=o_source[s_key];

				if (typeof(s_value) === "object" && s_value!=null){
					a_object_loop.push({s_key:s_key, s_value: s_value, o_source:o_source, o_dest: o_new, s_path:''});
				}else{
					o_new[s_key]=s_value;
				}
			}
		}else{
			Ext.jx.Prototypes.Resume();
			return o_source;
		}

		var s_key, s_value_source, o_dest, s_path;

		for (var n_index=0; n_index < a_object_loop.length; n_index++ ){
			var o_item=a_object_loop[n_index];
			s_key=o_item.s_key;
			s_value_source=o_item.o_source[s_key];
			s_path=o_item.s_path+'['+s_key+']';

			

			if (o_item.s_value.nodeType  || o_item.s_value.constructor == RegExp) { o_item.o_dest[s_key]=s_value_source;  continue }; 

			if (h_params.b_dupe_checker){
				if (s_value_source[s_rep_field] ){
					var o_item_saved=a_object_loop[s_value_source[s_rep_field]-1];
					o_item.o_dest[s_key]=o_item_saved.o_source[s_key];
					continue;
				}
				try { s_value_source[s_rep_field]=n_index+1; }catch(e){}
			}


			var n_rule=h_params.h_exclusions[o_item.s_key] || (o_item.o_source.h_deep_copy_info ? o_item.o_source.h_deep_copy_info[o_item.s_key] : 0 );

			if (typeof(n_rule)==='function'){
				var o_value=n_rule(o_item.s_value, o_item);
				for (var s_key_child in o_value){
					var s_value_child=o_value[s_key_child];
					o_item.o_dest[s_key_child]=s_value_child;
				}

			}else if (n_rule==1){ 

				var o_new_child;
				if (typeof(o_item.s_value) === "object" && o_item.s_value!=null && typeof(o_item.s_value.length) != "undefined"){ 
					o_new_child=[];
					for (var n_index_child=0; n_index_child < o_item.s_value.length; n_index_child++ ){
						var s_key_child=n_index_child;
						var s_value_child=o_item.s_value[n_index_child];
						o_new_child[s_key_child]=s_value_child;
					}
					o_item.o_dest[o_item.s_key] = o_new_child;
				}else if (typeof(o_item.s_value) === "object"){
					o_new_child={};
					for (var s_key_child in o_item.s_value){
						var s_value_child=o_item.s_value[s_key_child];
						o_new_child[s_key_child]=s_value_child;
					}
					o_item.o_dest[o_item.s_key] = o_new_child;
				}else{
					o_item.o_dest[o_item.s_key] = o_item.s_value;
				}

			}else if (n_rule==2){ 
				o_item.o_dest[o_item.s_key] = o_item.s_value;
			}else if (n_rule==3){ 
			}else{ 
				if (h_params.o_existing) { h_params.o_existing=h_params.o_existing[s_key] }

				if (typeof(o_item.s_value.length) != "undefined"){ 
					var o_new_child=o_item.o_dest[o_item.s_key]=[];
					for (var n_index_children=0; n_index_children < o_item.s_value.length; n_index_children++ ){
						var s_key_child=n_index_children;
						var s_value_child=o_item.s_value[s_key_child];
						var o_source_child=o_item.s_value;

						if (typeof(s_value_child) === "object" && s_value_child!=null){
							a_object_loop.push({s_key:s_key_child, s_value: s_value_child, o_source:o_source_child, o_dest: o_new_child, s_path: s_path});
						}else{
							o_new_child[s_key_child]=s_value_child;
						}
					}

				}else {
					var o_new_child=o_item.o_dest[o_item.s_key]={};
					var h_overrides_used=false;
					for (var s_key_child in o_item.s_value){

						var s_value_child=o_item.s_value[s_key_child];
						var o_source_child=o_item.s_value;
						if (h_params.h_overrides && (o_override_key=h_params.h_overrides[s_key_child]) &&  (o_override_value=o_override_key[s_value_child])){
							if (typeof(o_override_value)==='object'){
								if (!h_overrides_used){ h_overrides_used={} }
								for (var s_key_override_child in o_override_value){
									var s_value_override_child=o_override_value[s_key_override_child];
									o_new_child[s_key_override_child]=s_value_override_child;
									h_overrides_used[s_key_override_child]=true;
								}
							}else{
								s_value_child=o_override_value;
							}
						}

						if (typeof(s_value_child) === "object" && s_value_child!=null){
							a_object_loop.push({s_key:s_key_child, s_value: s_value_child, o_source:o_source_child, o_dest: o_new_child, s_path: s_path });
						}else{
							if (!h_overrides_used || !h_overrides_used[s_key_child] ){
								o_new_child[s_key_child]=s_value_child;
							}
						}
					}
				}
			}
		}

		if (h_params.b_dupe_checker && !vars.b_debug_leave_replication_vars){
			for (var n_index=0; n_index < a_object_loop.length; n_index++ ){ 
				var o_item=a_object_loop[n_index];
				s_key=o_item.s_key;
				s_value_source=o_item.o_source[s_key];
				s_value_dest=o_item.o_dest[s_key];

				if (s_value_source && s_value_source[s_rep_field]) { delete s_value_source[s_rep_field]; }
				if (s_value_dest && s_value_dest[s_rep_field]) { delete s_value_dest[s_rep_field]; }

			}
		}

		Ext.jx.Prototypes.Resume();
		h_params.n_objects_copied=a_object_loop.length;
		return o_new;
	}
}
Ext.ns('Ext.jx');

Ext.jx.Prototypes = {
	Pause: function (){

		if (!Ext.jx.vars.h_prototypes){ Ext.jx.vars.h_prototypes = {h_functions:{}, h_arrays:{} } }

		Ext.jx.vars.h_prototypes.Function={};
		for(var s_key in Function.prototype ){
			Ext.jx.vars.h_prototypes.h_functions[s_key]=Function.prototype[s_key];
			delete Function.prototype[s_key];
		}

		Ext.jx.vars.h_prototypes.Array={};
		for(var s_key in Array.prototype ){
			Ext.jx.vars.h_prototypes.h_arrays[s_key]=Array.prototype[s_key];
			delete Array.prototype[s_key];
		}
	},

	Resume: function (){

		if (!Ext.jx.vars.h_prototypes){ Ext.jx.vars.h_prototypes = {h_functions:{}, h_arrays:{} } }


		for(var s_key in Ext.jx.vars.h_prototypes.h_functions){
			if (!Function.prototype[s_key]){  Function.prototype[s_key]=Ext.jx.vars.h_prototypes.h_functions[s_key]; }
		}

		for(var s_key in Ext.jx.vars.h_prototypes.h_arrays ){
			if (!Array.prototype[s_key]){  Array.prototype[s_key]=Ext.jx.vars.h_prototypes.h_arrays[s_key]; }
		}
	}
}
Ext.ns('Ext.jx');

Ext.jx.plugins = {

	onConfig:{
		init:function(o){
			if(typeof(o.onConfig)==='object' && !(o.onConfig instanceof Array)){
				if (o.onConfig.overrideAdv){
					for (var overrideName in o.onConfig.overrideAdv) {
						var sMethod=o.onConfig.overrideAdv[overrideName].method;
						var aArguments=o.onConfig.overrideAdv[overrideName].arguments;
						if (!(aArguments instanceof Array)) { aArguments=[aArguments] };
						o[overrideName]=o[overrideName][sMethod].apply(o[overrideName],aArguments);
					}
				}
			};
		}
	},

	onExt:{
		init:function(o){
			if(typeof(o.onBrowser)==='object' && !(o.onBrowser instanceof Array)){
				var found=false, foundDefaults=false ;
				for (var variable in o.onBrowser) {
					if (Ext[variable]){
						Ext.apply(o,o.onBrowser[variable]);
						foundDefaults=o.onBrowser[variable].defaults;
						found=true; break;
					}
				}

				if (!found && o.onBrowser.isElse ){
					foundDefaults=o.onBrowser.isElse.defaults;
					Ext.apply(o,o.onBrowser.isElse);
				}

				if (foundDefaults){
		            o.items.each(function(f){
		            	o.applyDefaults(f);
		            });
				}
			}
		}
	},

	Tab_Fix:{
		init:function(o){

			o.on('tabchange',function(TabPanel, tab){

				if (!tab.renderedSafe){
					if (!tab.alwaysDoLayout){
						tab.renderedSafe=true;
					}
	                this.el.mask('Preparing your screen...', 'x-mask-loading');
	                this.on('afterlayout',function(){
	                	this.el.unmask.defer(1,this.el);
	                });
					this.doLayout.defer(1,this);
				}

			});
		}
	}
}
Ext.namespace('Ext.jx.Misc');

Ext.jx.Misc.updateItemLabel=function (form, itemName, record, fieldName ){
	var fieldLabel;
	if ((fieldLabel = record.get(fieldName))) {
		var field=form.findBy(function(item){ return item.name===itemName },form)[0];
		if (field) field.fieldLabel=fieldLabel;
	}
}

Ext.jx.Misc.WebKitLateTriggerRender=function(){
	return undefined; //this may already be fixed in ext 3.01+
	//heuristic fix

	if (!this.trigger) return undefined;
	
	if (this.debugTemp){
		Ext.jx.debug();
	}

	//if ( this.el.getWidth() && this.el.getWidth() == this.wrap.getWidth()) {
	//	this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());
	//} else {
		//var elWidth =this.el.getWidth() || this.width || 110;
		//var trigWidth = this.trigger.getWidth()||this.triggerWidth||17;
		
		var elWidth =this.width || this.el.getWidth() || 110;
		var trigWidth = this.triggerWidth || this.trigger.getWidth() ||17;
		
		var adjWidth = this.adjustWidth('input', elWidth - trigWidth );		
		this.el.setWidth(adjWidth);
		
		//this.el.setWidth(elWidth - trigWidth);
		
		this.wrap.setWidth(adjWidth + trigWidth);
	//}
	
    var y;
    if(Ext.isIE && !this.hideTrigger && this.el.getY() != (y = this.trigger.getY())){
        this.el.position();
        this.el.setY(y);
    }
	
}

Ext.jx.Misc.cleanDate=function(dateRaw){
	if (!dateRaw || Ext.isDate(dateRaw)){
		
	}else{
		dateRaw=Date.parseDate(dateRaw,'m/d/Y');
	}
	return dateRaw;
}
Ext.apply(Ext.jx, {
	vars:{language:'',debug:0},
	ignoreNull: function(value){
		if (typeof(value)=='undefined' || value === null ){ value= '' }
		return value;
	},
	debug:function(){ 
		Ext.jx.Prototypes.Pause();
		debugger;
		Ext.jx.Prototypes.Resume();
	},

	getQueryString: function(){
		if (!Ext.jx.qs) Ext.jx.qs= window.location.search ? Ext.urlDecode(window.location.search.substring(1)) : [];
		return Ext.jx.qs;
	},

	getStore: function (tableName, dataName){ 
		return Ext.jx.getTable(tableName).getData(dataName).getStore();
	},

	getStoreValue: function (options){ 
		if (typeof options=='string'){
			options={fieldName:options};
		}

		var row=this.getRecord(options);
		return (row ? row.data[options.fieldName] : undefined );
	},
	setStoreValue: function (options, newValue){ 
		if (typeof options=='string'){
			options={fieldName:options};
		}

		var row=this.getRecord(options);
		return (row ? row.data[options.fieldName]=newValue : undefined );
	},

	getRecord: function (options){
		var store;

		if (options.storeId){
            store = Ext.StoreMgr.lookup(options.storeId);
		}else if (options.tableName){
			if (!options.dataName){ options.dataName = 'ALL' }
			store=Ext.jx.getTable(options.tableName).getData(options.dataName).getStore();
		}else if (options.fieldName.indexOf('#')){
			options.tableName=options.fieldName.split('#')[0];
			if (!options.dataName){ options.dataName = 'ALL' }
			store=Ext.jx.getTable(options.tableName).getData(options.dataName).getStore();
		}

		if (!store){ return undefined }

		var row;
		if (options.recordId){
			row=store.getById(options.recordId);
		}else if (options.recordNo){
			row=store.data.items[options.recordNo];
		}else if (store.data.items.length==1){
			row=store.data.items[0];
		}
		return row;
	},

	getTable: function (tableName){
		if (!tableName){
			Ext.jx.debug('Did not specify tableName');
			return '';
		}
		if (!Ext.jx.tables){ Ext.jx.tables={} }
		tableName=tableName.toUpperCase();
		var table=Ext.jx.tables[tableName];
		if (!table){
			Ext.jx.debug('Can not load table: '+tableName);
		}
		return table;
	},

	setTable: function (tableName, definitions){
		if (!Ext.jx.tables){ Ext.jx.tables={} }
		if (!tableName){
			Ext.jx.debug('Did not specify tableName');
			return '';
		}else if (typeof(tableName)== 'object' ){
			var tables=tableName;
			for(var tableNameTemp in tables ){
				var definitionsTemp=tables[tableNameTemp];
				if (definitionsTemp=== null || definitionsTemp === undefined){ continue }
				Ext.jx.setTable(tableNameTemp, definitionsTemp);
			}
			return;
		}
		var jxTable=Ext.jx.Table;
		tableName=tableName.toUpperCase();
		var table=Ext.jx.tables[tableName];
		if (!table){
			if (!definitions) { definitions={} }
			definitions.tableName=tableName;
			table=Ext.jx.tables[tableName]=new Ext.jx.Table(definitions);
		}else if (definitions){
			table.applyDefinition(definitions);
		}
	    return table;
	},

	copyForm: function(form){
		var paramsCopy={h_exclusions:{}};

		Ext.jx.each(Ext.jx.TableFormCopyOverrides, function(func, funcName){
			paramsCopy.h_exclusions[funcName]=function(){ return func.apply(form,arguments) }
		});

		if (Ext.jx.vars.language){
			var languageOverrides;
			if (!formContainer.languages || !(languageOverrides=formContainer.languages[Ext.jx.vars.language]()) ){
				//alert('The form ('+params.table+'.'+params.form+') does not have that language loaded:'+Ext.jx.vars.language);
			}else{
				paramsCopy.h_overrides=languageOverrides;
			}
		}

		var formCopy=Ext.jx.Object.deepCopy(form, paramsCopy);
		return formCopy;

	},

	TableFormCopyOverrides:{

		CONDITION: function(o, item){
			var form=this;
			var o_source=item.o_source[item.s_key];
			var o_return={};
			Ext.jx.each(o_source, function(s_value, s_key){
				o_return[s_key]=s_value;
			})
			return o_return;
		},

		NO_COPY: function(o, item){
			var o_source=item.o_source[item.s_key];
			var o_return={};
			Ext.jx.each(o_source, function(s_value, s_key){
				o_return[s_key]=s_value;
			})
			return o_return;
		},

		INSERT_DB: function(o, item){
			var form=this;
			var o_source_value=item.s_value;
			var o_source=item.o_source;

			var tableLink=Ext.jx.getTable(o_source_value.tableName);
			var dataLink;

			if (o_source_value.dataName){
				dataLink=tableLink.getData(o_source_value.dataName);
			}else{
				alert('todo');
				//return;
			}

			Ext.jx.Prototypes.Resume();
			var store=dataLink.getStore();
			Ext.jx.Prototypes.Pause();

			var xtype=o_source.xtype;
			if (/^combo/.test(xtype)){
				return {
					store: store
				}
			}else{
				alert('Can not find handler for xtype: '+o_source.xtype);
			}
		}
	},

	TableData:{

		getStore : function(options, config, metaclass){
			var data=this;
			if (!data.storeStorage){
				var table=data.getTable();

				var recordType=Ext.data.Record.create(data.meta.fields, data.meta);

				var reader=new Ext.data.JsonReader(data.meta, recordType);

				if (!data.rows.length) data.rows=[];//this fixes a bug when the recordset is empty


				var store=data.storeStorage=new (metaclass || Ext.data.Store)({
					data:   data,
					reader: reader
					//,storeId: table.tableName+'::'+data.dataName,
					//tableName: table.tableName,
					//dataName: data.dataName
				});

				if (options && options.remote){
					var server=Ext.jx.Server.get();

					if (Ext.lib.Ajax.isCrossDomain(server)){
						store.proxy=new Ext.data.ScriptTagProxy({
							url: server+'?'+(options.remoteUrlAppend || data.dataName)+'&pl='+Ext.jx.getStoreValue('DEPT#DEPTNO')
						});
					}else{
						store.proxy=new Ext.data.HttpProxy({
							url: server+'?'+(options.remoteUrlAppend || data.dataName)+'&pl='+Ext.jx.getStoreValue('DEPT#DEPTNO')
						});
					}

					if (options.loadNow){

						store.on({
							load: function(){
								Ext.jx.Server.release(server);
								//if (!responseObject.responseObject){
								//	eval('responseObject.responseObject='+responseObject.responseText);
								//}
								//alert('store loaded');
							},
							loadException:function(){
								Ext.jx.Server.release(server);
								alert('could not load store');
							}
						});

						store.load();
					}


				}

			}

			return data.storeStorage;

		}

	},


	each: function (s_var, f_callback, o_scope, h_params)	 {
		var b_scope=typeof (o_scope) == "object";
		if (!h_params){h_params={}}

		if (typeof (s_var.length) != "undefined"  ){
			if (h_params.b_reverse_order){
				for(var n_index = s_var.length -1 ; n_index >=0 ; n_index--){
					var o_item=s_var[n_index];
					if (o_item=== null || o_item === undefined){ continue }
					if (false === f_callback.call(o_scope || (typeof(o_item) == "object" ? o_item : this), o_item, n_index, h_params)){ break; }
				}
			}else{
				for(var n_index = 0; n_index < s_var.length; n_index++){
					var o_item=s_var[n_index];
					if (o_item=== null || o_item === undefined){ continue }
					if (false === f_callback.call(o_scope || (typeof(o_item) == "object" ? o_item : this), o_item, n_index, h_params)){ break; }
				}
			}

		}else if (typeof (s_var) == "object") {
			for(var s_key in s_var ){
				var o_item=s_var[s_key];
				if (o_item=== null || o_item === undefined){ continue }
				if (false === f_callback.call(o_scope || (typeof(o_item) == "object" ? o_item : this), o_item, s_key, h_params)){ break; }
			}
		}
		return;
	}

});


Ext.ButtonGroup.prototype.onAfterLayout=Ext.ButtonGroup.prototype.onAfterLayout.trapFunction();
Ext.Element.prototype.getStyle=Ext.Element.prototype.getStyle.trapFunction();


Ext.override(Ext.grid.GridView, {
    hasRows : function(){
		var fc = this.mainBody.dom && this.mainBody.dom.firstChild; //if grid is empty this will keep it from erroring out
        return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
    }
});


if (Ext.isIE){
    Ext.Shadow.prototype.realign=Ext.Shadow.prototype.realign.trapFunction();
    Ext.form.Field.prototype.markInvalid=Ext.form.Field.prototype.markInvalid.trapFunction();
    Ext.lib.Scroll.prototype.setAttr=Ext.lib.Scroll.prototype.setAttr.trapFunction();
    Ext.lib.AnimMgr.stop=Ext.lib.AnimMgr.stop.trapFunction();
    Ext.Element.prototype.setStyle=Ext.Element.prototype.setStyle.trapFunction();
    Ext.Element.prototype.getXY=Ext.Element.prototype.getXY.trapFunction(function(){
        return [0,0]
    });

}else if (Ext.isWebKit){
    Ext.form.Field.prototype.markInvalid=Ext.form.Field.prototype.markInvalid.trapFunction();

}else if (Ext.isGecko3){

}


if (Ext.isIE){ //another hack required for the hopeless browser... http://www.extjs.com/forum/showthread.php?t=81087

    var tempTableEl = null,
        emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
        tableRe = /^table|tbody|tr|td$/i,
        pub,

        afterbegin = 'afterbegin',
        afterend = 'afterend',
        beforebegin = 'beforebegin',
        beforeend = 'beforeend',
        ts = '<table>',
        te = '</table>',
        tbs = ts+'<tbody>',
        tbe = '</tbody>'+te,
        trs = tbs + '<tr>',
        tre = '</tr>'+tbe;

    function ieTable(depth, s, h, e){
        tempTableEl.innerHTML = [s, h, e].join('');
        var i = -1,
            el = tempTableEl,
            ns;
        while(++i < depth){
            el = el.firstChild;
        }

        if(ns = el.nextSibling){
            var df = document.createDocumentFragment();
            while(el){
                ns = el.nextSibling;
                df.appendChild(el);
                el = ns;
            }
            el = df;
        }
        return el;
    }


    function insertIntoTable(tag, where, el, html) {
        var node,
            before;

        tempTableEl = tempTableEl || document.createElement('div');

        if(tag == 'td' && (where == afterbegin || where == beforeend) ||
           !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
            return;
        }
        before = where == beforebegin ? el :
                 where == afterend ? el.nextSibling :
                 where == afterbegin ? el.firstChild : null;

        if (where == beforebegin || where == afterend) {
            el = el.parentNode;
        }

        if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
            node = ieTable(4, trs, html, tre);
        } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
                   (tag == 'tr' && (where == beforebegin || where == afterend))) {
            node = ieTable(3, tbs, html, tbe);
        } else {
            node = ieTable(2, ts, html, te);
        }
        el.insertBefore(node, before);
        return node;
    }


	Ext.DomHelper.insertHtml= function(where, el, html){
	    var hash = {},
	        hashVal,
	        setStart,
	        range,
	        frag,
	        rangeEl,
	        rs,
	        result,
	        needsWrapping = el.nodeType != 1,
	        wrapSpan,
	        unwrap = Ext.emptyFn;

	    if (needsWrapping) {
	        wrapSpan = el.parentNode.appendChild(document.createElement('span'), el);
	        wrapSpan.appendChild(el);
	        el = wrapSpan;
	        unwrap = function() {
	            wrapSpan.parentNode.appendChild(wrapSpan.firstChild, wrapSpan);
	            wrapSpan.parentNode.removeChild(wrapSpan);
	        }
	    }

	    where = where.toLowerCase();
	    // add these here because they are used in both branches of the condition.
	    hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
	    hash[afterend] = ['AfterEnd', 'nextSibling'];

	    if (el.insertAdjacentHTML) {
	        if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
	            return rs;
	        }
	        // add these two to the hash.
	        hash[afterbegin] = ['AfterBegin', 'firstChild'];
	        hash[beforeend] = ['BeforeEnd', 'lastChild'];
	        if ((hashVal = hash[where])) {
	            el.insertAdjacentHTML(hashVal[0], html);
	            result = el[hashVal[1]];
	            unwrap();
	            return result;
	        }
	    } else {
	        range = (el.nodeType == 1 ? el : el.parentNode).ownerDocument.createRange();
	        setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');
	        if (hash[where]) {
	            range[setStart](el);
	            frag = range.createContextualFragment(html);
	            el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
	            result =  el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
	            unwrap();
	            return result;
	        } else {
	            rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
	            if (el.firstChild) {
	                range[setStart](el[rangeEl]);
	                frag = range.createContextualFragment(html);
	                if(where == afterbegin){
	                    el.insertBefore(frag, el.firstChild);
	                }else{
	                    el.appendChild(frag);
	                }
	            } else {
	                el.innerHTML = html;
	            }
	            result = el[rangeEl];
	            unwrap();
	            return result;
	        }
	    }
	    throw 'Illegal insertion point -> "' + where + '"';
	}



	Ext.getCmpWaitFor=function(id, funcRun, scope, taskConfig){
	    var oTask;
	    var task={
	        run : function(){
	            var component=Ext.getCmp(id);
	            if(component){
	                funcRun.call(scope || this || window, component);
	                Ext.TaskMgr.stop(task)
	                return false;
	            }
	        },
	        interval: 10
	    };

	    if (taskConfig) { Ext.apply(task, taskConfig) }

	    if (!(task.run()===false)){
	        if (!task.duration && !task.repeat ){task.repeat=3}
	        Ext.TaskMgr.start(task);
	    }

	}

}

Ext.customAlert = function (config){
	var labels=['ok', 'cancel', 'yes', 'no'], savedLabels=Ext.apply({},Ext.Msg.buttonText);

	Ext.applyIf(config,{buttons:{}, method: 'show', fn: function(buttonId){

		for (i=0; i < config.btns.length; i++){
			var btn=config.btns[i], label=labels[i];
			if (buttonId == label){
				if (btn.fn) return btn.fn.apply(btn, arguments);
			}
		}
	}});

	for (i=0; i < config.btns.length && i < 4; i++){
		var btn=config.btns[i], label=labels[i];
		Ext.Msg.buttonText[label]=btn.buttonText;
		config.buttons[label]=true;
	}

	Ext.Msg[config.method](config);

	Ext.apply(Ext.Msg.buttonText, savedLabels);
}


Ext.override(Ext.BoxComponent, {

    afterRender : function(){
        Ext.BoxComponent.superclass.afterRender.call(this);
        if(this.resizeEl){
            this.resizeEl = Ext.get(this.resizeEl);
        }
        if(this.positionEl){
            this.positionEl = Ext.get(this.positionEl);
        }
        this.boxReady = true;

        //do not overwrite custom 'overflow' styles
        if (this.autoScroll != null) this.setAutoScroll(this.autoScroll);

        this.setSize(this.width, this.height);
        if(this.x || this.y){
            this.setPosition(this.x, this.y);
        }else if(this.pageX || this.pageY){
            this.setPagePosition(this.pageX, this.pageY);
        }
    }
});

Ext.ux.SimpleIframePanel = Ext.extend(Ext.Panel, {
    onRender: function() {
        this.bodyCfg = {
            tag: 'iframe',
            src: this.src,
            cls: this.bodyCls,
            style: {
                border: '0px none'
            }
        };

		Ext.ux.SimpleIframePanel.superclass.onRender.apply(this, arguments);

		//these strategies weren't working well
		////this.body.dom.addEventListener('onLoad',this.resizeIframe.createDelegate(this), false)

		////this.relayEvents(this.body, ['onLoad']);
		////this.body.on('onLoad',this.resizeIframe.createDelegate(this));

		//this.resizeIframe.defer(1000,this); //so go simple

    },


	resizeIframe : function () {


		//more work needed so shelving it for now

		//try {

		var iframe=this.body.dom;


		var innerDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
		if (innerDoc.body.offsetHeight){ //ns6 syntax
			iframe.height = innerDoc.body.offsetHeight + 32; //Extra height FireFox
		}else if (this.body.Document && this.body.Document.body.scrollHeight){ //ie5+ syntax
			iframe.height = iframe.Document.body.scrollHeight;
		}

        //}catch(err){
        //  alert(err.message);
        //}
      }


});

Ext.reg('simple_iframe_panel',Ext.ux.SimpleIframePanel);



//Ext.Template.prototype.re=/\{([\w\#-]+)(?:\:([\w\.\#]*)(?:\((.*?)?\))?)?\}/g;
//Ext.XTemplate.prototype.re=/\{([\w-\.\#]+)(?:\:([\w\.\#]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g;
//Ext.XTemplate.prototype.re=/\{(.*?)\}/g;

Ext.apply(Ext.Component.prototype,{

	getBasicForm : function (){
		if (typeof(this.basicForm) != 'undefined'){ return this.basicForm }
		var forms=this.findParentByType('form');

		this.basicForm=forms.length && forms[0] ? forms[0].form : false;
		//this.basicForm=forms.length  ? forms[0] : false;
		return this.basicForm;
	},

	getStore : function (){
		var basicForm=this.getBasicForm();
		var store=(basicForm===false ? false : basicForm.store);
	},

	getRecord : function (){
		var basicForm=this.getBasicForm();
		var record=(basicForm===false ? false : basicForm.record);
		return record;
	}

});
Ext.form.ComboBoxQuickChange=Ext.extend(Ext.form.ComboBox,{

	initComponent: function(){
        Ext.form.ComboBoxQuickChange.superclass.initComponent.call(this);

		this.on('select',function(){
	        var v = this.getValue();
	        if(String(v) !== String(this.startValue)){
			    this.fireEvent('change', this, v, this.startValue);
				this.startValue=v;
			}
		})
	}
});
Ext.reg('combo_quick_change', Ext.form.ComboBoxQuickChange);
Ext.ns('Ext.form.ComboBox');

Ext.form.ComboBox.Container = Ext.extend(Ext.form.ComboBoxQuickChange, {
	
	tpl : ' ',
	embedd : null,

	height : 300,
	minHeight : 200,

	
	//private
	expandFromOnLoad : false,

 	
 	onViewClick : function(){
    },
    
    
    onViewMove : function(){
    },

    
    onViewOver : function(){
    },
    
    
    initList : function(){
    	Ext.form.ComboBox.Container.superclass.initList.call(this);
    	this.view.setStore(null);
    	
    	if (!(this.embedd instanceof Ext.Component)) this.embedd = Ext.ComponentMgr.create(this.embedd, Ext.Panel);
    	
    	this.embedd.applyToMarkup(this.innerList);
    },
    
    
    onLoad : function(){
    	this.expandFromOnLoad = true;
    	
    	Ext.form.ComboBox.Container.superclass.onLoad.call(this);
    	
    	this.expandFromOnLoad = false;
    },
    
    
    
	onDestroy : function(){
		Ext.destroy(this.embedd);
        
        Ext.form.ComboBox.Container.superclass.onDestroy.call(this);
    },
    
    
    onTypeAhead : function(){
        if(this.store.getCount() > 0){
            var r = this.store.getAt(0);
            var newValue = r.data[this.displayField];
            var len = newValue.length;
            var selStart = this.getRawValue().length;
            if(selStart != len){
                //Ext bug
                //this.setRawValue(newValue);
            	
            	//fix
                this.setValue(r.data[this.valueField]);
                
                this.selectText(selStart, newValue.length);
            }
        }
    },
    
    
    expand : function(){
    	if (this.expandFromOnLoad) return;
    	
        if(this.isExpanded() || !this.hasFocus){
            return;
        }
		
		var topMostParent = this.findParentBy(function(item){ return !item.ownerCt });
		var bottomBelow=topMostParent.el.getBox().bottom;
		var thisBelow=this.el.getBox().bottom;
		var heightBottom=bottomBelow-thisBelow-10;
		
		if (heightBottom >= this.minHeight){
			if (heightBottom <= this.height) this.height = heightBottom;
			this.alignTo='tl-bl';
		}else{
			var topAbove=topMostParent.el.getBox().y;
			var thisAbove=this.el.getBox().y;		
			var heightAbove=thisAbove-topAbove-10;			
			if (heightAbove <= this.height) this.height = heightAbove;
			this.alignTo='bl-tl';
		}
		
		this.list.setHeight(this.height);
		this.embedd.setHeight(this.height);
        this.list.alignTo(this.wrap, this.alignTo);

        this.list.show();
        this.embedd.doLayout();
        
        this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
        Ext.getDoc().on('mousewheel', this.collapseIf, this);
        Ext.getDoc().on('mousedown', this.collapseIf, this);
        this.fireEvent('expand', this);
    },
    
    
    onTriggerClick : function(){
        if(this.disabled){
            return;
        }
        if(this.isExpanded()){
            this.collapse();
            this.el.focus();
        }else {
            this.onFocus({});
            this.expand();
            this.el.focus();
        }
    }
	
});

Ext.reg('container_combo', Ext.form.ComboBox.Container);
//Ext.data.control = function(config){
//	Ext.apply(this, config)
//
//	this.addEvents('beforechange', 'beforeadd', 'beforecreate', 'beforeremove', 'change', 'update', 'add', 'remove');
//
//	//if (this.rowObj){ this.connectRowObj(this.rowObj) }
//}
//
//Ext.extend(Ext.data.control,Ext.util.Observable, {
//
//	firstPage:function(){
//	},
//	previousPage:function(){
//	},
//	nextPage:function(){
//	},
//	lastPage:function(){
//	},
//	refreshPage:function(){
//	},
//
//	first:function(){
//	},
//	previous:function(){
//	},
//	next:function(){
//	},
//	last:function(){
//	},
//	refresh:function(){
//	},
//
//	recordChangedValue : function (record, fieldName, value){
//		//debugger;
//	},
//
//	changeRecord : function(record, mode){
//
//    	if (this.fireEvent('beforechangerecord', this, record)===false){ return false }
//
//
//    	if (this.record){
//    		this.record.un('change',this.recordChangedValue);
//    	}
//
//        this.record=record;
//
//    	if (this.record){
//    		this.record.on('change',this.recordChangedValue);
//    	}
//
//    	this.fireEvent('changerecord', this, record);
//	},
//
//	getValue : function(fieldName){
//		//if (!this.record || !this.record.data){ return ''}
//		return this.record.get(fieldName);
//	},
//
//	
//	changeValue : function(fieldName, value){
//    	if (this.fireEvent('beforechangevalue', this, fieldName, value)===false){ return false }
//    	
////    	var oldValue = this.record.get(fieldName);
//        this.record.set(fieldName, value);
//    
//        //this event firing was moved to Record.set
////    	this.fireEvent('changevalue', this, fieldName, value, oldValue);
//	},
//
//
////	dataControl.on('change', function(record, fieldNameChanged, value){
////		if (!paused && fieldNameChanged == fieldName ){
////			paused=true;
////			f.setValue(value);
////			paused=false;
////		}
////	});
//
//
//	add : function(){
//		var meta=this.store.reader.meta;
//		var recordType = Ext.data.Record.create(meta.fields, meta);
//		var values={};
//        var record = new recordType(values);
//        if (!record.data) { record.data={} }
//        
//        record.dataControl = this;
//        
//    	if (this.fireEvent('beforeadd', this, record)===false){ return false }
//        this.store.add([record]);
//    	this.fireEvent('add', this, record);
//    	this.changeRecord(record,'add')
//		return record;
//	},
//	
//	
//	remove : function(record){
//		if (!record) record = this.record;
//		
//    	if (this.fireEvent('beforeremove', this, record)===false){ return false }
//    	
//    	var store = this.store;
//    	
//    	var recordIndx = store.indexOf(record);
//        store.remove(record);
//    	this.fireEvent('remove', this, record);
//    	
//    	var afterRemove = store.getCount();
//    	//if some records still remain, then setup as current either the record with the same index as removed, or the last one
//    	if (afterRemove) this.changeRecord( store.getAt(recordIndx <= afterRemove - 1 ? recordIndx : afterRemove - 1), 'afterremove')
//    	
//		return record;
//	},
//
//
//
//	onLoad : function(store, r, o){
//
//    },
//
////    getPageData : function(){
////        var total = this.store.getTotalCount();
////        return {
////            total : total,
////            activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
////            pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
////        };
////    },
//
//    onLoadError : function(){
//
//    },
//
////    readPage : function(d){
////        var v = this.field.dom.value, pageNum;
////        if (!v || isNaN(pageNum = parseInt(v, 10))) {
////            this.field.dom.value = d.activePage;
////            return false;
////        }
////        return pageNum;
////    },
//
//
//    beforeLoad : function(){
////        if(this.rendered && this.loading){
////            this.loading.disable();
////        }
//    },
//
//    doLoad : function(start){
////        var o = {}, pn = this.paramNames;
////        o[pn.start] = start;
////        o[pn.limit] = this.pageSize;
////        this.store.load({params:o});
//    },
//
//
//
//
//    unbindStore : function(store){
//        store = Ext.StoreMgr.lookup(store);
//        store.un("beforeload", this.beforeLoad, this);
//        store.un("load", this.onLoad, this);
//        store.un("loadexception", this.onLoadError, this);
//        this.store = undefined;
//    },
//
//
//    bindStore : function(store){
//        store = Ext.StoreMgr.lookup(store);
//        store.on("beforeload", this.beforeLoad, this);
//        store.on("load", this.onLoad, this);
//        store.on("loadexception", this.onLoadError, this);
//        this.store = store;
//    }
//});
Ext.Element.prototype.withinBox=function(coords, padding){
	var box=this.getBox(true);
	var x=coords.x || coords[0];
	var y=coords.y || coords[1];
	if (!padding){ padding=0  }
	return (box.x-padding <= x && box.right+padding >= x && box.y-padding <= y && box.bottom+padding >= y);
}
//Ext.apply(Ext.data.Record.prototype, Ext.util.Observable.prototype);

//Ext.apply(Ext.data.Record.prototype, {
//	
//	//reference to Ext.data.control to which this record belong
//	dataControl : null
//	
//});

Ext.data.Record.create=Ext.data.Record.create.interceptResult(function(recordtype, fields, meta){

	var tableDef;
	if (meta && meta.tableName && (tableDef=Ext.jx.getTable(meta.tableName))){
		if (!tableDef.FuncsRow) { tableDef.FuncsRow={} }
		if (!recordtype.prototype.getTable){
			recordtype.prototype.getTable=function(){
				return tableDef;
			}
			Ext.apply(recordtype.prototype, tableDef.FuncsRow);
		}
	}else{
	}

	if (meta && meta.rowObj && meta.rowObj.prototype ){
		Ext.apply(recordtype.prototype, meta.rowObj.prototype);
	}

	var returns=Ext.extend(recordtype, {
        constructor: function(data, id) {

        	//this.addEvents( "update", "remove" );
        	recordtype.superclass.constructor.apply(this, arguments);

		    if (!this.id){ this.id = (id || id === 0) ? id : ++Ext.data.Record.AUTO_ID };
		    if (!this.data){ this.data = data; }

        }
    });

	return returns;

});


////XXX subclass Record and call superclass method
//(function(){
//	var intercepted = Ext.data.Record.prototype.set;
//	
//	
//	Ext.data.Record.prototype.set = function(name, value){
//		var oldValue = this.get(name);
//		
//		intercepted.apply(this, arguments);
//	
//	    this.fireEvent('update', this, name, value, oldValue);
//		if (this.dataControl) this.dataControl.fireEvent('changevalue', this.dataControl, name, value, oldValue, this);
//	};
//})()




//Ext.data.Record.prototype.set = function(name, value){
//    if(String(this.data[name]) == String(value)){
//        return;
//    }
//    this.dirty = true;
//    if(!this.modified){
//        this.modified = {};
//    }
//    if(typeof this.modified[name] == 'undefined'){
//        this.modified[name] = this.data[name];
//    }
//    this.data[name] = value;
//    if(!this.editing && this.store){
//    	
//    	//fire record's 'update' event before the 'update' event of Store - to process the 'onUpdate' event
//    	//of the data control a bit earlier 
//    	this.fireEvent('update', this, name, value, this.modified[name]);
//    	
//        this.store.afterEdit(this);
//    }
//}

Ext.DataView.prototype.prepareData = function(data, index, record){

	//if (record.events){
		var dataCopy={'_record':record};
		for(var key in data){
			dataCopy[key]=data[key];
		}
		return dataCopy;
	//}
	//return data;
};

Ext.data.Store.prototype.getStoreId=function (){
	if (!this.storeId){ this.setStoreId( Ext.id() ) }
	return this.storeId;
}

Ext.data.Store.prototype.setStoreId=function (storeId){
    if (this.id || this.storeId){
        Ext.StoreMgr.unregister(this);
    }
    this.storeId=this.id=storeId;
    Ext.StoreMgr.register(this);
}

//Ext.form.Field.prototype.getDataValueStoreName=function(){
//  var DataFieldNameRe  = /\#/;
//  var fieldName;
//  if (DataFieldNameRe.test(this.id)){
//      fieldName=this.id;
//  }else if (DataFieldNameRe.test( this.getName() )){
//      fieldName=this.getName();
//  }else if (DataFieldNameRe.test(this.name)){
//      fieldName=this.name;
//  }
//  return fieldName;
//}

Ext.override(Ext.form.DateField, {
    onTriggerClick : function(){		
        if(this.disabled){
            return;
        }
        if(this.menu == null){
            this.menu = new Ext.menu.DateMenu(Ext.apply({
                hideOnClick: false
            }, this.configMenu));
        }
        this.onFocus();
        Ext.apply(this.menu.picker,  {
            minDate : this.minValue,
            maxDate : this.maxValue,
            disabledDatesRE : this.disabledDatesRE,
            disabledDatesText : this.disabledDatesText,
            disabledDays : this.disabledDays,
            disabledDaysText : this.disabledDaysText,
            format : this.format,
            showToday : this.showToday,
            minText : String.format(this.minText, this.formatDate(this.minValue)),
            maxText : String.format(this.maxText, this.formatDate(this.maxValue))
        }, this.configPicker);
		
		if (!this.getValue()){
	        this.menu.picker.setValue(this.parseDate(this.defaultDate || this.minValue  || new Date()));
		}else{
	        this.menu.picker.setValue(this.getValue());			
		}
        this.menu.show(this.el, "tl-bl?");
        this.menuEvents('on');
    }
});


//Ext.form.DateField.prototype.onTriggerClick=Ext.form.DateField.prototype.onTriggerClick.createInterceptor(function(){
//	if (!this.getValue() && this.minValue){
//		this.setValue(this.minValue);
//		(function(){
//			this.fireEvent('change', this, this.minValue, '');
//		}).defer(100, this)
//	}
//});

//Ext.form.DateField.prototype.onTriggerClick = Ext.form.DateField.prototype.onTriggerClick.createInterceptor(function(name){	
//	if(this.disabled){
//		return;
//	}
//	if(this.menu == null){
//		this.menu = new Ext.menu.DateMenu({
//			hideOnClick: false,
//			datePickerMsg: this.datePickerMsg
//		});
//	}	
//});

Ext.form.DateField.prototype.onRender=Ext.form.DateField.prototype.onRender.createSequence(function(){
	if (this.vtype=='daterange' && this.endDateField){
		var field=this;
		(function(){
			Ext.form.VTypes['daterange'](field.getValue(),field)
		}).defer(100, this);
	}
});

Ext.DatePicker.prototype.onRender=Ext.DatePicker.prototype.onRender.createSequence(function(){
	if (this.datePickerMsg){
		this.el.insertHtml ('afterBegin', '<div align=center class="x-date-middle x-btn x-btn-text" style="width:auto">'+this.datePickerMsg+'</div>');
	}
});


Date.prototype.getMaxDate=function(date){
	return (this.subtractDt(date) > 0 ? this : date );	
}

Date.prototype.getMinDate=function(date){
	return (this.subtractDt(date) < 0 ? this : date );	
}

Date.prototype.subtractDt=function(date){
	var days=(this.getTime()-date.getTime())/86400000;
	return days;
}
Ext.apply(Ext.form.VTypes, {
	daterange: function(val, field) {
		//var date = field.parseDate(val);
		//alert(date);
		// We need to force the picker to update values to recaluate the disabled dates display
		var sd, ed, changingStartDate=false;
		if (!field.startDateField && !field.endDateField){
			alert('needs to have the startDateField or the endDateField');
			return;
		}else if (field.endDateField){
			changingStartDate=true;
			sd=field;
			ed=field.ownerCt.findField(field.endDateField) || field.ownerCt.ownerCt.findField(field.endDateField) || field.ownerCt.ownerCt.ownerCt.findField(field.endDateField);
			
			
			if (!ed){
				alert('Could not find the end date field');
				return;
			}

		}else if (field.startDateField){
			sd=field.ownerCt.findField(field.startDateField) || field.ownerCt.ownerCt.findField(field.startDateField) || field.ownerCt.ownerCt.ownerCt.findField(field.startDateField);
			ed=field;
			if (!sd){
				alert('Could not find the start date field');
				return;
			}
		}

		var dispUpd = function(picker, activeDate) {
			var ad = picker.activeDate;
			picker.activeDate = activeDate;
			picker.update(ad);
		}
		var sdValue=sd.getValue();
		var edValue=ed.getValue();

        var minDaysOut=(typeof(sd.minDaysOut)=='undefined' ? 5 : field.minDaysOut);
        var defaultDays=(typeof(sd.defaultDays)=='undefined' ? 5 : field.defaultDays);
        var minDays=(typeof(sd.minDays)=='undefined' ? 2 : field.minDays);
        var maxDays=(typeof(sd.maxDays)=='undefined' ? 2 : field.maxDays);
        var minDate=(typeof(sd.minDate)=='undefined' ? (new Date()).clearTime() : Date.parseDate(sd.minDate,'m/d/Y') );
        var maxDate=(typeof(sd.maxDate)=='undefined' ? '' : Date.parseDate(sd.maxDate,'m/d/Y') );


		//debugger;
//		if (!sd.minValue){
//			var now=new Date().clearTime();
//			var nowNew=now.add('d', minDaysOut);
//			sd.minValue=(minDate.subtractDt(nowNew) < 0 ? nowNew : minDate );
//			if (sd.menu && sd.menu.picker) {
//				sd.menu.picker.minDate = sd.minValue;
//				dispUpd(sd.menu.picker, sd.minValue);
//			}
//
//		}
//		if (!sd.maxValue && maxDate){
//			sd.maxValue=maxDate;
//			if (sd.menu && sd.menu.picker) {
//				sd.menu.picker.maxDate = sd.maxValue;
//				dispUpd(sd.menu.picker,sd.minValue);
//			}
//
//		}

		if (changingStartDate) {


			if (sdValue){
				var minEndDate=sdValue.add('d', minDays);
				ed.minValue = minEndDate;
				if (ed.menu && ed.menu.picker) {
					ed.menu.picker.minDate = minEndDate;
					dispUpd(ed.menu.picker, ed.minValue);
				}

				var minEndDate=sdValue.add('d', minDays);
				if (maxDays){
					var maxEndDate=sdValue.add('d', maxDays);
					ed.maxValue = maxEndDate;
					if (ed.menu && ed.menu.picker) {
						ed.menu.picker.maxDate = maxEndDate;
						dispUpd(ed.menu.picker, ed.minValue);
					}
				}

				if (edValue){
					var days=edValue.subtractDt(sdValue);
					if (days<minDays || (maxDays && days>maxDays)){
						var edValueNew=sdValue.add('d', defaultDays);
						ed.setValue(edValueNew);
						ed.fireEvent('change', ed, edValueNew, edValue);
					}
				}else{
					var edValueNew=sdValue.add('d', defaultDays);
					ed.setValue(edValueNew);
					ed.fireEvent('change', ed, edValueNew, edValue);
				}
			}else{
				ed.minValue=sd.minValue;
				ed.maxValue=sd.maxValue;
				if (ed.menu && ed.menu.picker) {
					ed.menu.picker.minDate = ed.minValue;
					ed.menu.picker.maxDate = ed.maxValue;
					dispUpd(ed.menu.picker, ed.minValue);
				}
			}


//			//alert(days);
//			//debugger;
//			ed.minValue = date;
//			if (ed.menu && ed.menu.picker) {
//				ed.menu.picker.minDate = date;
//				dispUpd(ed.menu.picker);
//			}

		} else {

			


		}
		
		return true;
	},

	password: function(val, field) {
		if (field.initialPassField) {
			var pwd = Ext.getCmp(field.initialPassField);
			return (val == pwd.getValue());
		}
		return true;
	},

	passwordText: 'Passwords do not match'
});
//        onShow : function(){
//        if(this.hideParent){
//            this.container.removeClass('x-hide-' + this.hideMode);
//        }else{
//            this.getActionEl().removeClass('x-hide-' + this.hideMode);
//        }
//
//    },
//        onHide : function(){
//        if(this.hideParent){
//            this.container.addClass('x-hide-' + this.hideMode);
//        }else{
//            this.getActionEl().addClass('x-hide-' + this.hideMode);
//        }



Ext.override(Ext.layout.FormLayout, { 
    renderItem : function(c, position, target){
        if(c && !c.rendered && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
            var args = this.getTemplateArgs(c);
            if(typeof position == 'number'){
                position = target.dom.childNodes[position] || null;
            }
            if(position){
                c.formItem = Ext.get(this.fieldTpl.insertBefore(position, args));
            }else{
                c.formItem = Ext.get(this.fieldTpl.append(target, args));
            }
            c.render('x-form-el-'+c.id);
        }else {
            Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
        }
    }
});


Ext.override(Ext.Component, {

	hideItem: function(){
	    if (this.formItem){
	    	this.formItem.addClass('x-hide-' + this.hideMode);
	    }else{
	    	this.hide();
	    }
	},
	showItem: function(){
	    if (this.formItem){
		    this.formItem.removeClass('x-hide-' + this.hideMode);
	    }else{
	    	this.hide();
	    }
	}
});
Ext.StoreMgr.lookup = function(id){

	if(Ext.isArray(id)){
		var fields = ['field1'], expand = !Ext.isArray(id[0]);
		if(!expand){
			for(var i = 2, len = id[0].length; i <= len; ++i){
				fields.push('field' + i);
			}
		}
		return new Ext.data.ArrayStore({
			fields: fields,
			data: id,
			expandData: expand,
			autoDestroy: true,
			autoCreated: true

		});
	}
	var store=Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
	if (!store) store=eval(id);
	return store;
}
Ext.Panel.prototype.createClasses=Ext.Panel.prototype.createClasses.createSequence(function(){
	if (this.tools){
		var toolsGood=false;
		for (var tool in this.tools){
			toolsGood=true;
			break;
		}
		if (!toolsGood){
			this.tools=undefined;
		}
	}
});
Ext.ns('Ext.ux.TabPanel');


Ext.ux.TabPanel.Slideable = Ext.extend(Ext.TabPanel, {
	
	constructor : function (config){
		Ext.ux.TabPanel.Slideable.superclass.constructor.call(this,config);
	},
	
	
	initComponent : function () {
		var layout = this.layout;
		delete this.layout;
		
		Ext.ux.TabPanel.Slideable.superclass.initComponent.call(this);
		
		if (layout) {
			this.setLayout(
				new Ext.Container.LAYOUTS[layout](this.layoutConfig)
			);
		}
	}
	
}); //eof extend


Ext.reg('tabpanel_slideable', Ext.ux.TabPanel.Slideable);
Ext.Container.prototype.removeRange=Ext.util.MixedCollection.prototype.removeRange=function(starting, ending){
	if (!this.items){ return this }
	for (var i=ending || (this.items.length-1); i>=(starting || 0); i--){
		this.remove(i);
	}
	return this;
};


Ext.override(Ext.Container, {

	//can be set to 'true' to skip component from validation
	isOptional : null,

	loadRecord: Ext.form.BasicForm.prototype.loadRecord,

	//very nasty fix..
	fixTriggerFields: function(){
		this.cascade(function(f){
			//heuristic fix
	        if (f instanceof Ext.form.TriggerField && f.wrap && (!f.wrap.getWidth() || f.wrap.getWidth() == f.el.getWidth() || Ext.isIE)) Ext.jx.Misc.WebKitLateTriggerRender.call(f);
		});
	},


	findField : function(id){

	    var field = (this.items ? this.items.get(id) : null);
	    if(!field){

			this.cascade(function(f){

		        if( f.dataControlField == id || f.dataIndex == id || f.id == id || (f.getName && f.getName() == id) || f.name==id ){
		            field = f;
		            return false;
		        }
			});
	    }
	    return field || null;
	},


	getValues: function(options){

		var values={};
		this.cascade(function(f){

	        if(f.isFormField || f.getValue){
	        	var name=(f.dataControlField || f.dataIndex || f.getName() || f.name || f.id);
	        	var v=f.getValue ? f.getValue() : f.value;
		        if(v === f.emptyText || v === undefined) v = '';
				values[name]=v;
	        }
		});

		return values;
	},


	setValues : function (values) {

		this.cascade(function (field) {
	        if (field.isFormField) {
	        	var name = field.dataControlField || field.dataIndex || field.getName() || field.name || field.id

	        	if (!values.hasOwnProperty(name)) return
	        	try{
					field.setValue(values[name])
				}catch(e){
					debugger;
				}
	        }
		})

		return values
	},


	setValuesIf: function(values, options){
		var valuesOrig=this.getValues();
		this.cascade(function(f){
	        if(f.isFormField){
	        	var name=(f.dataControlField || f.dataIndex || f.getName() || f.name || f.id);
				if (typeof values[name] != 'undefined' && f.rendered){
					var v=f.getValue();
					if(typeof valuesOrig[name] == 'undefined' || valuesOrig[name] == ''){
						f.setValue(values[name]);
					}
				}
	        }
		});

		return values;
	},

    validate: function(id){
        var valid=true; 
        if(this.items){
            var cs = this.items.items;
            var childContainers=[];

            for(var i = 0, len = cs.length; i < len; i++){
            	if (cs[i].items){
            		childContainers.push(cs[i]);
            	} else
            		//add isOptional option to container
            		if (!cs[i].isOptional && cs[i].validate && cs[i].rendered && !cs[i].validate()){ 
	            		valid=false;
	            	}
            }
            if (valid){
	            for(var i = 0, len = childContainers.length; i < len; i++){
	            	if (childContainers[i].validate && !childContainers[i].validate()){ valid=false; break;}
	            }
            }
        }
        return valid;
    }

});


//this allows the visible checkbox option to show up on getValues
Ext.override(Ext.form.FieldSet,{
	getName: function(){
		return this.checkboxName || this.id+'-checkbox';
	},
	getValue: function(){
		return !this.collapsed;
	}
});

Ext.ns('RL');


RL.Frame = Ext.extend(Ext.Viewport, {
	//autoEl : 'div',
	
	//slots : true,
	baseCls: 'x-frameless',
	
	constructor : function(config){
				
		config = config || {};
		
		var frame={
			
			layout : "border",
			hideBorders:true,
			baseCls: 'x-frameless',
			
			items : [{
				region : "north",
				autoHeight : true,
				html: config.northHtml
				//html:'north'
				
			},{
				region : "east",
				autoWidth : true,
				html: config.eastHtml
				
			},
			Ext.apply(config.centerObj,{
				//height:500,				
				region : 'center',				
				baseCls: 'x-frameless',
				//layout:	'fit',
				//autoWidth:true,
				hideBorders: true
			})
			,{
				region : "west",
				baseCls: 'x-frameless',
				hideBorders: true,				
				autoWidth : true,
				html: config.westHtml
				
			},{
				region : "south",
				baseCls: 'x-frameless',
				hideBorders: true,
				autoHeight : true,
				html: config.southHtml
			}]
		};
		//); //eof apply
		
		RL.Frame.superclass.constructor.call(this, frame);
		//centerObj
		
	}

	,afterRender : function(){
		
		RL.Frame.superclass.afterRender.call(this);
	},
	//
	//
	initComponent : function(){
		//after this line all slots will be filled
		RL.Frame.superclass.initComponent.apply(this, arguments);
		
		//		var destBanner = this.slots.banner;
		////var gtawRecord=this.dataControl.record;
		//
		////resort is not chosen or banner is not rendered yet 
		//if (!destBanner.rendered || !gtawRecord.get('GTAW#RESORT')) return;
		//
		//var resortRecord = gtawRecord.getResortRecord();
		//
		//destBanner.removeRange();
		//destBanner.add({
		//	html:resortRecord.data['RESORT#TOPBANNER']
		//});
		//destBanner.doLayout();

	}
	
});
Ext.ns('Ext.jx');

Ext.jx.Map = {
	Google:{
		urlKeys:{},
		loaded:true,
		apiCallBack:function(){
			google.load("maps", "2", {"callback" : function(){
				Ext.jx.Map.Google.loaded=true;
			}});
			Ext.lib.Event.on(window, 'unload', function(){
				GUnload();
			});
		},
		include: function(){  
			if (Ext.jx.Map.included) return false;
			Ext.jx.Map.included=true;
			
			var urlKeys=Ext.jx.Map.Google.urlKeys;
			var base = location.href;
			base = base.replace(/\?.*$/, "");
			base = base.replace(/\#.*$/, "");
			var baseSearch = /^.*\//i.exec(base);
			var mapKey=urlKeys[baseSearch[0]] || urlKeys[base];

			if (!mapKey){
				if ( window.location.protocol == "file:" || window.location.hostname == "localhost"){
					mapKey='ABQIAAAAnLkzKP-cb43Pdlxz6bbznxT2yXp_ZAY8_ufC3CFXhHIE1NvwkxS_OCZ-cUPoy-mwM5UrsmT3Ne5QqQ';
				}else{
					return Ext.jx.debug('Can not load google maps. Obtain a google map key at http://code.google.com/apis/maps/signup.html '+baseSearch[0]);
				}
			}
			Ext.jx.External.includeJs('http://www.google.com/jsapi?key='+mapKey+'&callback=Ext.jx.Map.Google.apiCallBack');
		}
	}
};
Ext.ns('RL.form');

RL.form.SimpleButton = Ext.extend(Ext.Button, {
	template : new Ext.Template( '<em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em>' )
});

Ext.reg('simple_button', RL.form.SimpleButton);
Ext.namespace('Ext.ux');






Ext.ux.MonthPicker = Ext.extend(Ext.Component, {

    format : "M, Y",

    okText : " OK ",

    cancelText : "Cancel",

    constrainToViewport : true,

    monthNames : Date.monthNames,

    startDay : 0,

    value : 0,

    noPastYears: true, // only use the current year and future years

    initComponent: function(){
        Ext.ux.MonthPicker.superclass.initComponent.call(this);

        this.value = this.value ?
                 this.value.clearTime() : new Date().clearTime();

        this.addEvents(

            'select'
        );

        if(this.handler){
            this.on("select", this.handler,  this.scope || this);
        }
    },

    focus : function(){
        if(this.el){
            this.update(this.activeDate);
        }
    },

    onRender : function(container, position){
        var m = [ '<div style="width: 175px; height:175px;"></div>' ]
        m[m.length] = '<div class="x-date-mp"></div>';

        var el = document.createElement("div");
        el.className = "x-date-picker";
        el.innerHTML = m.join("");

        container.dom.insertBefore(el, position);

        this.el = Ext.get(el);
        this.monthPicker = this.el.down('div.x-date-mp');
        this.monthPicker.enableDisplayMode('block');

        this.el.unselectable();

        this.showMonthPicker();

        if(Ext.isIE){
            this.el.repaint();
        }

        this.update(this.value);

    },

    createMonthPicker : function(){
        if(!this.monthPicker.dom.firstChild){
            var buf = ['<table border="0" cellspacing="0">'];
            for(var i = 0; i < 6; i++){
                buf.push(
                    '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
                    '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
                    i == 0 ?
                    '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
                    '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
                );
            }
            buf.push(
                '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
                    this.okText,
                    '</button><button type="button" class="x-date-mp-cancel">',
                    this.cancelText,
                    '</button></td></tr>',
                '</table>'
            );
            this.monthPicker.update(buf.join(''));
            this.monthPicker.on('click', this.onMonthClick, this);
            this.monthPicker.on('dblclick', this.onMonthDblClick, this);

            this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
            this.mpYears = this.monthPicker.select('td.x-date-mp-year');

            this.mpMonths.each(function(m, a, i){
                i += 1;
                if((i%2) == 0){
                    m.dom.xmonth = 5 + Math.round(i * .5);
                }else{
                    m.dom.xmonth = Math.round((i-1) * .5);
                }
            });
        }
    },

    showMonthPicker : function(){
        this.createMonthPicker();
        var size = this.el.getSize();
        this.monthPicker.setSize(size);
        this.monthPicker.child('table').setSize(size);

        this.mpSelMonth = (this.activeDate || this.value).getMonth();
        this.updateMPMonth(this.mpSelMonth);
        this.mpSelYear = (this.activeDate || this.value).getFullYear();
        this.updateMPYear(this.mpSelYear);

        this.monthPicker.show();
        //this.monthPicker.slideIn('t', {duration:.2});
    },

    updateMPYear : function(y){

        if ( this.noPastYears ) {
            var minYear = new Date().getFullYear();
            if ( y < (minYear+4) ) {
                y = minYear+4;
            }
        }

        this.mpyear = y;
        var ys = this.mpYears.elements;
        for(var i = 1; i <= 10; i++){
            var td = ys[i-1], y2;
            if((i%2) == 0){
                y2 = y + Math.round(i * .5);
                td.firstChild.innerHTML = y2;
                td.xyear = y2;
            }else{
                y2 = y - (5-Math.round(i * .5));
                td.firstChild.innerHTML = y2;
                td.xyear = y2;
            }
            this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
        }
    },

    updateMPMonth : function(sm){
        this.mpMonths.each(function(m, a, i){
            m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
        });
    },

    selectMPMonth: function(m){

    },

    onMonthClick : function(e, t){
        e.stopEvent();
        var el = new Ext.Element(t), pn;
        if(el.is('button.x-date-mp-cancel')){
            this.hideMonthPicker();
            //this.fireEvent("select", this, this.value);
        }
        else if(el.is('button.x-date-mp-ok')){
            this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
            //this.hideMonthPicker();
            this.fireEvent("select", this, this.value);
        }
        else if(pn = el.up('td.x-date-mp-month', 2)){
            this.mpMonths.removeClass('x-date-mp-sel');
            pn.addClass('x-date-mp-sel');
            this.mpSelMonth = pn.dom.xmonth;
        }
        else if(pn = el.up('td.x-date-mp-year', 2)){
            this.mpYears.removeClass('x-date-mp-sel');
            pn.addClass('x-date-mp-sel');
            this.mpSelYear = pn.dom.xyear;
        }
        else if(el.is('a.x-date-mp-prev')){
            this.updateMPYear(this.mpyear-10);
        }
        else if(el.is('a.x-date-mp-next')){
            this.updateMPYear(this.mpyear+10);
        }
    },

    onMonthDblClick : function(e, t){
        e.stopEvent();
        var el = new Ext.Element(t), pn;
        if(pn = el.up('td.x-date-mp-month', 2)){
            this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
            //this.hideMonthPicker();
            this.fireEvent("select", this, this.value);
        }
        else if(pn = el.up('td.x-date-mp-year', 2)){
            this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
            //this.hideMonthPicker();
            this.fireEvent("select", this, this.value);
        }
    },

    hideMonthPicker : function(disableAnim){
        Ext.menu.MenuMgr.hideAll();
    },


    showPrevMonth : function(e){
       this.update(this.activeDate.add("mo", -1));
    },


    showNextMonth : function(e){
        this.update(this.activeDate.add("mo", 1));
    },


    showPrevYear : function(){
        this.update(this.activeDate.add("y", -1));
    },


    showNextYear : function(){
        this.update(this.activeDate.add("y", 1));
    },

    update : function( date ) {
        this.activeDate = date;
        this.value = date;

        if(!this.internalRender){
            var main = this.el.dom.firstChild;
            var w = main.offsetWidth;
            this.el.setWidth(w + this.el.getBorderWidth("lr"));
            Ext.fly(main).setWidth(w);
            this.internalRender = true;

            if(Ext.isOpera && !this.secondPass){
                main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
                this.secondPass = true;
                this.update.defer(10, this, [date]);
            }
        }
    }

});

Ext.reg('monthpicker', Ext.ux.MonthPicker);

if (Ext.version.substr(0, 1) == "2") {
	Ext.ux.MonthItem = function(config){
		Ext.ux.MonthItem.superclass.constructor.call(this, new Ext.ux.MonthPicker(config), config);
		this.picker = this.component;
		this.addEvents('select');
		this.picker.on("render", function(picker){
			picker.getEl().swallowEvent("click");
			picker.container.addClass("x-menu-date-item");
		});
		this.picker.on("select", this.onSelect, this);
	};
	Ext.extend(Ext.ux.MonthItem, Ext.menu.Adapter, {
		onSelect: function(picker, date){
			this.fireEvent("select", this, date, picker);
			Ext.ux.MonthItem.superclass.handleClick.call(this);
		}
	});
	Ext.ux.MonthMenu = function(config){
		Ext.ux.MonthMenu.superclass.constructor.call(this, config);
		this.plain = true;
		var mi = new Ext.ux.MonthItem(config);
		this.add(mi);
		this.picker = mi.picker;
		this.relayEvents(mi, ["select"]);
	};
	Ext.extend(Ext.ux.MonthMenu, Ext.menu.Menu, {
		cls: 'x-date-menu'
	});
} else {
	Ext.ux.MonthMenu = Ext.extend(Ext.menu.Menu, {
		enableScrolling: false,
		hideOnClick: true,
		cls: 'x-date-menu',
		initComponent: function(){
			Ext.apply(this, {
				plain: true,
				showSeparator: false,
				items: this.picker = new Ext.ux.MonthPicker(Ext.apply({
					//internalRender: this.strict || !Ext.isIE,
					ctCls: 'x-menu-date-item'
				}, this.initialConfig))
			});
			this.picker.purgeListeners();
			Ext.ux.MonthMenu.superclass.initComponent.call(this);
			this.relayEvents(this.picker, ['select']);
			this.on('select', this.menuHide, this);
			if (this.handler) {
				this.on('select', this.handler, this.scope || this)
			}
		},
		menuHide: function(){
			if (this.hideOnClick) {
				this.hide(true);
			}
		}
	});
}




Ext.ux.GridRowBlind=Ext.extend(Ext.Panel, {
    baseCls: 'x-frameless',
    //baseCls: 'x-bubble',
    autoHide: false,

	floating: true,
	shim : false,
	
	shadow : !Ext.isIE6,
	
	//cls: 'x-blind',
	//cls: 'x-frameless',
	autoHeight: true,
	showDuration: 0.25,
	
	constructor: function(config) {
		
		Ext.apply(config, {
			bodyStyle: {
				'border-width': '0px'
			}
		});
		
		Ext.ux.GridRowBlind.superclass.constructor.apply(this, arguments);
		this.marginLeft = this.marginLeft || 0;
		this.marginRight = this.marginRight || 0;
	
	//  The delay between mouseover and show needs to be greater then the duration
	//  of the show/hide animation so that a new show does not interfere with
	//  the hiding from the moueout of the previous row.
		if (!this.destroyOnDismiss && !this.showDelay && (this.showDelay < this.showDuration)) {
			this.showDelay = this.showDuration + 0.1;
		}
	},

    init: function(client) {
        this.client = client;
        this.client.on('render', this.initGridEvents, this);
    },

    initGridEvents: function() {
        this.render(this.client.body);

//      Make the client GridPanel fire mouseover and mouseout events for its rows and cells.
        this.client.body.on('mouseover', this.gridOnMouseover, this.client);
        this.client.body.on('mouseout', this.gridOnMouseout, this.client);

//      Subscribe to the new events.
		if (this.autoEvents){
			Ext.each (this.autoEvents, function(event){
		        this.client.addEvents(event);
	        	this.client.on(event, this.onMouseShow, this);
			},this);
		}                  

    },

    gridOnMouseover : function(e){
        this.processEvent("mouseover", e);
    },

    gridOnMouseout : function(e){
        this.processEvent("mouseout", e);
    },
    
    
    show:function(rowIndex, items){
		if (!this.el) this.render(this.client.body);
		
        if (rowIndex != this.currentRow) {

//          In case it's shown for another row.
            this.hide();

            this.currentRow = rowIndex;
            this.target = Ext.get(this.client.view.getRow(rowIndex));

//          Cancel any pending show.
            clearTimeout(this.showTimer);

//          Show this in showDelay seconds unless something comes up which cancels.
			if (this.destroyOnDismiss){
				this.showInternal(rowIndex, items)
			}else{
				this.showTimer = this.showInternal.defer(this.showDelay * 1000, this, [rowIndex, items]);
			}
        }
    	
    },

    
    onMouseShow: function(g, rowIndex, e) {
        if (rowIndex != this.currentRow) this.show(rowIndex)
    },

    
    hide: function() {
//      Cancel any pending showInternal.
        clearTimeout(this.showTimer);

        if (this.isShown) {
            this.el.stopFx();
            this.el.disableShadow();
            
			if (this.autoEventsHide)
				Ext.each (this.autoEventsHide, function(event){
	            	Ext.getBody().un(event, this.checkForMouseout, this);
				},this);
            
            
            this.el.slideOut(this.slideAlign, {
                duration: this.showDuration,
                callback: function() {
                    this.el.visible = false;

			        if (this.panel){
			        	(function(){
				        	this.panel.hide();

			        		if (this.panel.items.items){
				        		Ext.each(this.panel.items.items, function(item){        			
				        			if (item.menu && item.menu.picker && item.menu.picker.hideMonthPicker){
				        				item.menu.picker.hideMonthPicker();
				        			}
				        		},this);
				        	}
							
							if (this.destroyOnDismiss) {
								(function(){
									this.destroy();									
									if (this.client.blinds && this.client.blinds[this.id]) delete this.client.blinds[this.id];
								}).defer(100,this);
							}							

				        }).defer(50,this);
			        }

                },
                scope: this
            });
            this.isShown = false;
        }
    },

    showInternal: function(rowIndex, items) {

        this.el.stopFx();
        this.fireEvent('beforeshow', this);

        this.target.dom.appendChild(this.el.dom);
        var rec = this.client.store.getAt(rowIndex);
        

        if (items){        	
	        if (this.panel) this.panel.destroy();
			
        	this.panel = items instanceof Ext.Component ? items : new Ext.Panel(items);
        	//items.frame=true;

			//items.collapsible=true;
			//items.header=true;

        	//this.panel.render(Ext.getBody());
        	this.panel.render(this.body);
			
        	
        }else if (this.blindTpl) {
	        if(typeof this.blindTpl == "string"){
	            this.blindTpl = new Ext.XTemplate(this.blindTpl);
	        }
			this.body.update(this.blindTpl.applyTemplate( Ext.DataView.prototype.prepareData(rec.data,rowIndex, rec) ) );
        	        	
        }else if (this.blindField) {
            this.body.update(rec.get(this.blindField));
        }

        this.el.disableShadow();
        
        this.syncSizeWithTarget();
        
        var freeSpaceBelow = this.client.body.getBox().bottom - this.target.getBox().bottom
        
        if (this.el.getBox().height > freeSpaceBelow) {
            this.slideAlign = 'b';
            this.el.alignTo(this.target, 'bl-tl', [this.marginLeft + 1, -1]);
            
            if (this.el.shadow) {
            	this.el.shadow.adjusts.t = -3;
            	this.el.shadow.adjusts.h = 3;
            }
            
        } else {
			this.slideAlign = 't';
			this.el.alignTo(this.target, 'tl-bl', [this.marginLeft + 1, -1]);
			
			if (this.el.shadow) {
	            this.el.shadow.adjusts.t = 0;
	            this.el.shadow.adjusts.h = 3;
			}
        }
        
        this.el.slideIn(this.slideAlign, {
            duration: this.showDuration,
            callback: this.onAfterShow,
            scope: this
        });
        
        this.isShown = true;
    },

    syncSizeWithTarget: function() {
		//this.setWidth(this.width || this.target.getSize(true).width - (this.marginLeft + this.marginRight + 3));
		
    },

    onAfterShow: function() {
        this.el.visible = true; // Ext bug. Flag not set causes enableShadow to fail.
        if (!(Ext.isIE6 || Ext.isIE7)) this.el.enableShadow(true); //for the hopeless browser
        
		if (this.autoEventsHide)
			Ext.each (this.autoEventsHide, function(event){
				//this.client.body.on(event, this.gridOnMouseout, this);

				Ext.getBody().on(event, this.checkForMouseout, this);
			},this);
    },

    
//  When the mouse enteres, any subsequent mouse movement outside the element hides.
    checkForMouseout : function(e) {
        if (e.within(this.target) || e.target == this.target.dom || e.target == this.el.dom || e.within(this.el)) return
        	
    	var hideNow = true
    	
    	if (this.el.withinBox(e.xy, 5))
        	hideNow = false
    	else 
        	if (this.panel && this.panel.items && this.panel.items.items)
        		Ext.each(this.panel.items.items, function (item) {        			
        			if (item.menu && item.menu.picker &&  item.menu.picker.el)
        				if (e.within(item.menu.picker.el)) {
				        	hideNow = false
				        	return false      					
        				}
        		}, this)

        		
    	if (hideNow) {
            delete this.currentRow
            this.hide()
        }
    }

});
Ext.ux.TabPanelSkin = Ext.extend(Ext.TabPanel, {
		
	tabsBgCls: undefined,
	tabBtnCls: undefined,
	tabsTitle: undefined,
	tabsTitleCls: undefined,

	onRender : function(ct, position){

		if (this.tabBtnCls){
			this.itemTpl= new Ext.XTemplate(
				'<li class="{cls}" id="{id}" style="overflow:hidden">',
					 '<a class="x-tab-right-empty" href="#" onclick="return false;">',
						'<em class="x-tab-left-empty">',
							'<span class="x-tab-strip-inner ',this.tabBtnCls,'">',
								'<span class="x-tab-strip-text {iconCls}">{text} {extra}</span>',
							'</span>',
						'</em>',
					'</a>',
				'</li>');
		}
		Ext.ux.TabPanelSkin.superclass.onRender.call(this, ct, position);
	},	

	afterRender : function() {
		Ext.ux.TabPanelSkin.superclass.afterRender.apply(this, arguments);

		var wrap = this.header.child('.x-tab-strip-wrap'), ul= wrap.child('ul');
		if (this.tabsBgCls) {
			wrap.addClass(this.tabsBgCls);
			ul.addClass('remove-background');
		}

		if (this.tabBtnCls) {
			if (ul.child('.x-tab-strip-inner')) ul.child('.x-tab-strip-inner').addClass(this.tabBtnCls);
			wrap.child('.x-tab-strip-top').addClass('remove-border');
		}
		
		if (this.tabsTitleCls){
			if (Ext.isIE && !Ext.isIE8){ //ie less than 8 problem when a width is specified on a parent container that is less than a child with a float
				wrap.child('.x-tab-strip-top').setStyle('width','auto');
				var msgWrap=wrap.child('ul').wrap({tag:'div'});
				msgWrap.setStyle('width','5000px');		
				msgWrap.createChild({tag:'div', cls:this.tabsTitleCls, html: this.tabsTitle},wrap.child('ul'));
			}else{
				wrap.createChild({tag:'div', cls:this.tabsTitleCls, html: this.tabsTitle},wrap.child('ul'));			
			}
		}

	}
	
});


Ext.ns('ExtX.Data.Store')


ExtX.Data.Store.Paged = Ext.extend(Ext.data.GroupingStore, {
    
    wholeDataSet       	: null,
    
    filteredCount		: null,
    
    
    pageSize			: 20,
    
    
    constructor : function () {
    	this.wholeDataSet = []
    	
    	ExtX.Data.Store.Paged.superclass.constructor.apply(this, arguments)
    },
    
    
    loadRecords : function(o, options, success) {
        if(!o || success === false){
            if(success !== false){
                this.fireEvent('load', this, [], options);
            }
            if(options.callback){
                options.callback.call(options.scope || this, [], options, false, o);
            }
            return;
        }
        var r = o.records, t = o.totalRecords || r.length;
        if(!options || options.add !== true){
            if(this.pruneModifiedRecords){
                this.modified = [];
            }
            for(var i = 0, len = r.length; i < len; i++){
                r[i].join(this);
            }
            if(this.snapshot){
                this.data = this.snapshot;
                delete this.snapshot;
            }
            this.data.clear();

            this.wholeDataSet = []
            var from = options.from || 0 
            var to = options.to || r.length - 1
            
            for (var i = from; i <= to; i++) this.wholeDataSet[i] = r[ i - from ]
            
            this.data.addAll(r);
            this.totalLength = t;
            this.applySort();
            this.fireEvent('datachanged', this);
        }else{
            
            var from = options.from 
            var to = options.to
            
            for (var i = from; i <= to; i++) this.wholeDataSet[i] = r[ i - from ]
            
            this.totalLength = Math.max(t, this.data.length+r.length);
            
            if (isNaN(this.totalLength)) this.totalLength = 'N/A'
            
            this.data.clear();
            this.data.addAll(r);
            this.fireEvent('datachanged', this);
        }
        
        this.fireEvent('load', this, r, options);
        if(options.callback){
            options.callback.call(options.scope || this, r, options, true);
        }
    },
    
    
    getTotalCount : function () {
    	if (!this.isFiltered()) return ExtX.Data.Store.Paged.superclass.getTotalCount.apply(this, arguments)
    	
    	return this.filteredCount
    },
    
    
    clearFilter : function () {
    	var wasFiltered = this.isFiltered()
    	
    	ExtX.Data.Store.Paged.superclass.clearFilter.apply(this, arguments)
    	
    	if (wasFiltered) this.refreshDataSet()
    	
    	this.setDataPage(0, this.pageSize - 1)
    },
    
    
    filterBy : function () {
//    	this.suspendEvents()
    	
    	
    	this.clearFilter()
    	
    	this.setDataPage(0, this.getTotalCount() - 1)
    	
    	ExtX.Data.Store.Paged.superclass.filterBy.apply(this, arguments)
    	
    	this.filteredCount = this.getCount()
    	
    	this.refreshDataSet()
    	
    	
//    	this.resumeEvents()
    	
    	this.setDataPage(0, this.pageSize - 1)
    },
    
    
    //will fire 'datachanged' and 'load' events to notify paging toolbar
    publishUpdates : function (from, to) {
    	this.setDataPage(from || 0, to || this.pageSize - 1)
    },
    
    
    loadData : function(o, append, from, to){
        var r = this.reader.readRecords(o)
        
        this.loadRecords(r, {
            add: append,
            from : from,
            to : to
        }, true);
    },
    
    
    removeAll : function () {
        this.wholeDataSet = []
        this.totalLength = 0
        
        ExtX.Data.Store.Paged.superclass.removeAll.apply(this, arguments)
        
        this.fireEvent('load', this, [], {})
    },    
    
        
    //(!) from&to are 0-based
    setDataPage : function (from, to) {
        this.data.clear()
        
        var newPage = this.wholeDataSet.slice(from, to + 1)
        
        this.data.addAll(newPage)
        
        this.fireEvent('load', this, newPage, {
            from : from,
            to : to
        })
        this.fireEvent('datachanged', this)
    },
    
    
    //(!) from&to are 0-based        
    haveDataPage : function (from, to) {
        for (var i = from; i <= to; i++) if (!this.wholeDataSet[i]) return false
        
        return true
    },
    
    
    //will refresh underlaying 'wholeDataSet' with the current state of the store
    refreshDataSet : function () {
    	this.wholeDataSet = this.data.items.slice()
    }

})
Ext.ns('ExtX.Toolbar')


ExtX.Toolbar.Paging = Ext.extend(Ext.PagingToolbar, {


    slots               : true,


    initComponent : function () {
        this.addEvents('show-page')

        ExtX.Toolbar.Paging.superclass.initComponent.call(this)
        
        this.remove(this.refresh)
        
        var indexOfLast = this.items.indexOf(this.last)
        
        this.remove(indexOfLast + 1)
    },


    //XXX can be removed after next bugfix release of Ext
    bindStore : function(store, initial){
        var doLoad;
        if(!initial && this.store){
            this.store.un('beforeload', this.beforeLoad, this);
            this.store.un('load', this.onLoad, this);
            this.store.un('exception', this.onLoadError, this);
            if(store !== this.store && this.store.autoDestroy){
                this.store.destroy();
            }
        }
        if(store){
            store = Ext.StoreMgr.lookup(store);
            store.on({
                scope: this,
                beforeload: this.beforeLoad,
                load: this.onLoad,
                exception: this.onLoadError
            });
            this.onLoad(store, null, {});
        }
        this.store = store;
    },


    refresh : function () {
        this.doLoad(this.cursor, true)
    },


    doLoad : function (from, isRefresh) {
        var to = from + this.pageSize - 1

        if (this.fireEvent('beforechange', this, from, to) !== false)
            this.fireEvent('show-page', this, from, to, isRefresh)
    },


    onLoad : function (store, r, o) {
        if(!this.rendered){
            this.dsLoaded = [store, r, o];
            return;
        }

        var p = this.getParams();

        this.cursor = o[ p.start || 'from' ] || 0

        var d = this.getPageData(), activePage = d.activePage, pages = d.pages;

        this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
        this.inputItem.setValue(activePage);
        this.first.setDisabled(activePage == 1);
        this.prev.setDisabled(activePage == 1);
        this.next.setDisabled(activePage == pages);
        this.last.setDisabled(activePage == pages || pages == 'N/A');
        this.refresh.hide();
        this.updateInfo();
        this.fireEvent('change', this, d);
    },


    getPageData : function(){
    	var store = this.store

        var total = store.getTotalCount()

        var data = {
            total : total,
            activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
            pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
        }

        if (isNaN(data.pages)) data.pages = 'N/A'

        return data
    }
})


Ext.reg('ExtX.Toolbar.Paging', ExtX.Toolbar.Paging)



Ext.ns('ExtX.form.ComboBox')



ExtX.form.ComboBox.Twin = Ext.extend(Ext.form.ComboBox,  {

	trigger1Class 		: null,
	
	trigger2Class 		: null,
	

    initComponent : function(){
        ExtX.form.ComboBox.Twin.superclass.initComponent.call(this);

        this.triggerConfig = {
            tag:'span', cls:'x-form-twin-triggers', cn:[
            {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
            {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
        ]};
        
        this.addEvents('trigger2click')
    },

    
    getTrigger : function(index){
        return this.triggers[index];
    },

    
    initTrigger : function(){
        var ts = this.trigger.select('.x-form-trigger', true);
        var triggerField = this;
        ts.each(function(t, all, index){
            var triggerIndex = 'Trigger'+(index+1);
            t.hide = function(){
                var w = triggerField.wrap.getWidth();
                this.dom.style.display = 'none';
                triggerField.el.setWidth(w-triggerField.trigger.getWidth());
                this['hidden' + triggerIndex] = true;
            };
            t.show = function(){
                var w = triggerField.wrap.getWidth();
                this.dom.style.display = '';
                triggerField.el.setWidth(w-triggerField.trigger.getWidth());
                this['hidden' + triggerIndex] = false;
            };

            if(this['hide'+triggerIndex]){
                t.dom.style.display = 'none';
                this['hidden' + triggerIndex] = true;
            }
            this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
            t.addClassOnOver('x-form-trigger-over');
            t.addClassOnClick('x-form-trigger-click');
        }, this);
        this.triggers = ts.elements;
    },

    
    getTriggerWidth: function(){
        var tw = 0;
        Ext.each(this.triggers, function(t, index){
            var triggerIndex = 'Trigger' + (index + 1),
                w = t.getWidth();
            if(w === 0 && !this['hidden' + triggerIndex]){
                tw += this.defaultTriggerWidth;
            }else{
                tw += w;
            }
        }, this);
        return tw;
    },

    
    // private
    onDestroy : function() {
        Ext.destroy(this.triggers);
        ExtX.form.ComboBox.Twin.superclass.onDestroy.call(this);
    },

    
    onTrigger1Click : function () {
    	this.onTriggerClick.apply(this, arguments)
    },
    
    
    onTrigger2Click : function () {
    	this.fireEvent('trigger2click', this)
    }
});


Ext.reg('ExtX.form.ComboBox.Twin', ExtX.form.ComboBox.Twin);

Ext.ns('RL.FieldTypes');


RL.FieldTypes.Airport = Ext.extend(Ext.form.ComboBox, {
	displayField:'GAIRPORT#SHORTNAME',
	valueField:'GAIRPORT#CODE',

	loadingText: 'Searching...',
	hideTrigger:true,
	typeAhead: false,

	tpl:'<tpl for="."><div class=search-item>'+
		'{GAIRPORT#SHORTNAME} ({GAIRPORT#CODE})'+
	'</div></tpl>',

	width: 150,
	listWidth: 300,
	minChars:2,

	itemSelector: 'div.search-item',

	initComponent : function () {
		RL.FieldTypes.Airport.superclass.initComponent.call(this);

		var dataRemote=Ext.jx.getTable('GAIRPORT').getData('',{remote: true});
		this.store=dataRemote.getStore({remote:true, loadNow: false, remoteUrlAppend:'screen=js::ext_data_store::GAIRPORT'});

	}

});

Ext.reg('airport', RL.FieldTypes.Airport);
Ext.ns('RL.FieldTypes');


RL.FieldTypes.Currency = Ext.extend(Ext.form.ComboBox, {
	displayField:'CURRENCY#CTYCODE',
	valueField:'CURRENCY#CTYCODE',

    typeAhead: true,
    mode: 'local',
    forceSelection: true,
    triggerAction: 'all',
    emptyText: 'Currencies',
    selectOnFocus: true,

	width: 150,

	itemSelector: 'div.search-item',

	onRender : function () {

		if (!this.tpl) this.tpl='<tpl for="."><div class=search-item>'+
			'{CURRENCY#CTYCODE} - {CURRENCY#COUNTRY}'+
		'</div></tpl>';

		this.value=Ext.jx.getStoreValue('GTAW#CADCURR');
		this.store=Ext.jx.getStore('CURRENCY');

		if (this.store.getCount() <= 1) this.hideItem();

		RL.FieldTypes.Currency.superclass.onRender.apply(this, arguments)


	}

});

Ext.reg('currency', RL.FieldTypes.Currency);
Ext.ns('RL.FieldTypes');


RL.FieldTypes.DateRange = Ext.extend(Ext.Container, {
	constructor : function(config) {

		Ext.applyIf(config,{
			startDateFieldName: '#DDATE',
			endDateFieldName: '#RDATE',
		    layout : 'form'
		});

		if (!config.startDateField) config.startDateField={};
		Ext.applyIf(config.startDateField,{
			xtype:'daterange_start',
			name: config.startDateFieldName,
			endDateField: config.endDateFieldName
		});

		if (!config.endDateField) config.endDateField={};
		Ext.applyIf(config.endDateField,{
			xtype:'daterange_end',
			name: config.endDateFieldName,
			startDateField: config.endDateFieldName
		});

		config.items=[config.startDateField];
		delete config.startDateField;
		if (config.spacerField){
			config.items.push(config.spacerField);
			delete config.spacerField;
		}
		config.items.push(config.endDateField);
		delete config.endDateField;

		RL.FieldTypes.DateRange.superclass.constructor.call(this, config);
	}

});

Ext.reg('daterange', RL.FieldTypes.DateRange);

Ext.ns('RL.FieldTypes');


RL.FieldTypes.DateRangeBase = Ext.extend(Ext.form.DateField, {
	width:100,
	format:'j M Y',
	vtype: 'daterange',
	allowBlank: false,
	showToday:false,//only for DateField
	renderTodayButton: false, //only for DateFieldPlus
	noOfMonth: 2, //only for DateFieldPlus

	goDefer : function(){
		this.deferFocusClick=true;

		(function(){
			this.deferFocusClick=false;
		}).defer(100, this);

	},
    onFocusCustom : function(e){

		if (!this.deferFocusClick) {
			//this.goDefer();
			this.onTriggerClick();
		}
	},

    onBlurCustom : function(e){

		this.goDefer();

	},
    initComponent : function(field){
		RL.FieldTypes.DateRangeBase.superclass.initComponent.apply(this, arguments);
		//if (Ext.isGecko){
		//	this.on('focus',this.onFocusCustom, this);
		//	this.on('blur',this.onBlurCustom, this);
		//}
	}


});


Ext.ns('RL.FieldTypes');


RL.FieldTypes.DateRangeEnd = Ext.extend(RL.FieldTypes.DateRangeBase, {

	name: '#RDATE',
	startDateField: '#RDATE',
	fieldLabel: 'End Date',
	emptyText: 'Ends',
	configMenu:{
		datePickerMsg: 'Select End Date'
	}

});

Ext.reg('daterange_end', RL.FieldTypes.DateRangeEnd);
Ext.ns('RL.FieldTypes');


RL.FieldTypes.DateRangeStart = Ext.extend(RL.FieldTypes.DateRangeBase, {

	name: '#DDATE',
	endDateField: '#RDATE',
	fieldLabel: 'Start Date',
	emptyText: 'Starts',
	minDays: 2,
	maxDays: 20,
	defaultDays: 5,
	defaultDaysOut: 5,
	minDaysOut: 5,
	configMenu:{
		datePickerMsg: 'Select Start Date'
	},

	onSelectDate: function(field){
		this.deferFocusClick=true;
		var ed=field.ownerCt.findField(field.endDateField) || field.ownerCt.ownerCt.findField(field.endDateField) || field.ownerCt.ownerCt.ownerCt.findField(field.endDateField);
		ed.focus.defer(200,  ed);//it needs some time to dismiss the date popup
		//ed.focus();
	},

    initComponent : function(field){
		this.minValue=Ext.jx.getStoreValue('DEPT#TQDATEMIN') ?  Date.parseDate(Ext.jx.getStoreValue('DEPT#TQDATEMIN'), 'm/d/Y') : this.minValue;
		this.maxValue=Ext.jx.getStoreValue('DEPT#TQDATEMAX') ?  Date.parseDate(Ext.jx.getStoreValue('DEPT#TQDATEMAX'), 'm/d/Y') : this.maxValue;
		this.defaultDays=Ext.jx.getStoreValue('DEPT#TQDAYSDEF') ?  Number(Ext.jx.getStoreValue('DEPT#TQDAYSDEF')) : this.defaultDays;
		this.minDays=Ext.jx.getStoreValue('DEPT#TQDAYSMIN') ?  Number(Ext.jx.getStoreValue('DEPT#TQDAYSMIN')) : this.minDays;
		this.maxDays=Ext.jx.getStoreValue('DEPT#TQDAYSMAX') ?  Number(Ext.jx.getStoreValue('DEPT#TQDAYSMAX')) : this.maxDays;
		this.defaultDaysOut=Ext.jx.getStoreValue('DEPT#TQNODAYS') ?  Number(Ext.jx.getStoreValue('DEPT#TQNODAYS')) : this.defaultDaysOut;
		this.defaultFixedDate=Ext.jx.getStoreValue('DEPT#TQFIXDATE') ?  Date.parseDate(Ext.jx.getStoreValue('DEPT#TQFIXDATE'), 'm/d/Y') : this.defaultFixedDate;

		if (!this.maxValue || !this.maxValue.subtractDt || this.maxValue.subtractDt(new Date()) < 0){ //the max value has already passed so clear both dates
			this.minValue=this.maxValue=undefined;
		}

		if (!this.value){
			if (this.defaultDaysOut){
				this.defaultDate=(new Date()).add('d', this.defaultDaysOut);
			}else if (this.defaultFixedDate){
				this.defaultDate=Date.parseDate(this.defaultFixedDate, 'm/d/Y')
			}

			if (this.minValue && this.defaultDate) this.defaultDate=this.defaultDate.getMaxDate(this.minValue);
		}

		RL.FieldTypes.DateRangeStart.superclass.initComponent.apply(this, arguments);

		if (Ext.isGecko){
			this.on('select',this.onSelectDate, this);
		}
	}

});

Ext.reg('daterange_start', RL.FieldTypes.DateRangeStart);
Ext.ns('RL.FieldTypes');


RL.FieldTypes.PerPersonSelector = Ext.extend(Ext.ux.form.SpinnerField, {
	fieldLabel: 'No. People',
	minValue:0,
	maxValue:100,
	width: 50
});


Ext.reg('perperson_selector', RL.FieldTypes.PerPersonSelector);
Ext.ns('Ext.ux')

Ext.ux.ContainerSpacer = Ext.extend(Ext.Panel, { //a frameless spacer

	frame:false,
	hideBorder:true,
	baseCls:'x-frameless',
    render : function(){
		if (!this.items && !this.html) this.html='&#160;';

        Ext.ux.ContainerSpacer.superclass.render.apply(this, arguments);
	}

});

Ext.reg('container_spacer', Ext.ux.ContainerSpacer);



Ext.ns('ExtX.Grid')


ExtX.Grid.RowHelper = Ext.extend(Ext.Panel, {
	
    floating			: true,
    shadow				: false,
    shim				: false,
    
    layout				: 'hbox',
    
    buttonAlign			: 'center',
    
    cls					: 'x-small-editor',
    baseCls				: 'x-row-helper',
    
    
    elements			: 'header,footer,body',
    frameWidth			: 5,
    buttonPad			: 3,
    clicksToEdit		: 'auto',
    focusDelay			: 250,

    defaults: {
        normalWidth: true
    },
    
    btns				: null,
    isHelperVisible		: false,
	grid				: null,

    
    initComponent: function () {
        ExtX.Grid.RowHelper.superclass.initComponent.call(this)
        
        this.addEvents('before-show-helper')
    },
    

    init: function (grid) {
        this.grid 		= grid
        this.ownerCt 	= grid
        
        if (this.clicksToEdit == 2)
            grid.on('rowdblclick', this.onRowDblClick, this)
        else {
            grid.on('rowclick', this.onRowClick, this)
            
            if(Ext.isIE) grid.on('rowdblclick', this.onRowDblClick, this)
        }


        grid.on({
            scope			: this,
            
            viewready 		: this.onGridViewReady,
            keydown			: function (e) { console.log('keypress from grid'); this.onKey(e) }, 
	        beforedestroy 	: this.beforedestroy,
	        destroy 		: this.destroy,
            bodyscroll		: {
                buffer: 250,
                fn: this.positionHelper
            }
        })
    },
    
    
    onGridViewReady : function (grid) {
    },

    
    beforedestroy: function() {
        Ext.destroy(this.btns)
    },
    
    
    onKey : function (e) {
        if (e.getKey() === e.ESC) {
            e.stopPropagation()
            
            this.hideHelper()
        }
    },
    

    showHelper: function (rowIndex) {
        if (Ext.isObject(rowIndex)) rowIndex = this.grid.getStore().indexOf(rowIndex)
        
        if(this.fireEvent('before-show-helper', this, rowIndex) !== false) {
            this.isHelperVisible = true
            
            var grid 		= this.grid
            var view 		= grid.getView()
            var row 		= view.getRow(rowIndex)
            var record 		= grid.store.getAt(rowIndex)
                
            this.record 	= record
            this.rowIndex 	= rowIndex
            
            if (!this.rendered) this.render(view.getEditorParent())
            
            this.verifyLayout(true)
            
            if (!this.isVisible())
                this.setPagePosition(Ext.fly(row).getXY())
            else
                this.el.setXY(Ext.fly(row).getXY(), { duration : 0.15 })

            if (!this.isVisible()) this.show().doLayout()
        }
    },
    

    hideHelper : function(){
        this.isHelperVisible = false
        
        if (!this.isVisible()) return
        
        this.hide()
    },
    

    verifyLayout : function (force) {
        if (this.el && (this.isVisible() || force === true)) {
            var row = this.grid.getView().getRow(this.rowIndex)
            this.setSize(Ext.fly(row).getWidth(), Ext.fly(row).getHeight())
            
            this.doLayout()
            this.positionHelper()
        }
    },

    
    onRowClick: function (g, rowIndex, e) {
        if (this.clicksToEdit == 'auto') {
            var li = this.lastClickIndex
            this.lastClickIndex = rowIndex
            if (li != rowIndex && !this.isVisible()) return
        }
        
        this.showHelper(rowIndex)
    },

    
    onRowDblClick: function(g, rowIndex, e){
        this.showHelper(rowIndex)
    },
    
    
    createRowHelper : function () {
    	return new Ext.Panel({
    		items : [
    			{
    				xtype : 'button',
    				text : 'override me'
    			}
    		]
    	})
    },

    
    onRender : function () {
        ExtX.Grid.RowHelper.superclass.onRender.apply(this, arguments)
        
//        this.el.swallowEvent(['keydown', 'keyup', 'keypress'])
//        this.el.on('keydown', function (e) {
//        	
//        	console.log('keypress from helper.el')
//        	
//        	this.onKey(e)
//        
//        }, this)
        
        this.el.dom.keydown = function (e) {
        	
        	console.log('keypress from helper.el')
        	
        	this.onKey(e)
        
        }.createDelegate(this)
        
        
        this.btns = this.createRowHelper()
        
        Ext.apply(this.btns, {
            baseCls: 'x-plain',
            cls: 'x-btns',
            elements : 'body'
        })
        
        this.btns.render(this.bwrap)
        
//        this.btns.el.on('keypress', this.onKey, this)
        this.btns.el.on('keydown', function (e) {
        	
        	console.log('keypress from btns.el')
        	
        	this.onKey(e)
        
        }, this)
        
    },

    
    afterRender: function () {
        ExtX.Grid.RowHelper.superclass.afterRender.apply(this, arguments)
        
        this.positionHelper()
    },

    
    onHide: function(){
        ExtX.Grid.RowHelper.superclass.onHide.apply(this, arguments)
        
        this.grid.getView().focusRow(this.rowIndex)
    },

    
    positionHelper : function () {
        if (!this.btns) return
        
        var grid 		= this.grid
		var height 		= this.el.dom.clientHeight
		var view 		= grid.getView()
		var scroll 		= view.scroller.dom.scrollLeft
		var helperWidth = this.btns.getWidth()
		var width 		= Math.min(grid.getWidth(), grid.getColumnModel().getTotalWidth())
            
		
        this.btns.el.shift({ 
        	left : width / 2 - helperWidth / 2 + scroll, 
        	top : height - 2,
        	
        	stopFx : true, 
        	duration : 0.2 
    	})
    }
})


Ext.preg('ExtX.Grid.RowHelper', ExtX.Grid.RowHelper)

