/* autoenable toolbar overflow */
Ext.override(Ext.Toolbar, {
	enableOverflow: true
});

/* Standard patterns */
Date.patterns = {
	ISO8601Long:"Y-m-d H:i:s",
	ISO8601Short:"Y-m-d"
}

/* introduced in 3.1.0: getStyle('width') can return fractions on pixels, fixing regex */ 
Ext.override(Ext.Element, {
        addStyles : function(sides, styles){
            var val = 0,
                m = sides.match(/\w/g),
                s;
            for (var i=0, len=m.length; i<len; i++) {
                s = m[i] && parseFloat(this.getStyle(styles[m[i]]), 10);
                if (s) {
                    val += Math.abs(Math.round(s));
                }
            }
            return val;
        }
});



/* auto refreshing for stores */
Ext.override(Ext.data.Store, {
	autoRefresh : function(interval) {
		if(this.autoRefreshProcId){
			clearTimeout(this.autoRefreshProcId);
		}
		if(!this.refreshing) {
			return;
		}
		this.autoRefreshProcId = setTimeout(this.load.createDelegate(this, [{callback: this.autoRefresh.createDelegate(this, [interval])}]), interval);
	},
	startAutoRefresh : function(interval) {
		this.refreshing = true;
		this.load({callback: this.autoRefresh.createDelegate(this, [interval])});
	},
	stopAutoRefresh : function(){
		if(this.autoRefreshProcId){
			clearTimeout(this.autoRefreshProcId);
		}
		this.refreshing = false;
	}
});

/* reason: Deactivate all paging buttons when loading */
Ext.override(Ext.PagingToolbar, {
        beforeLoad : function(){
       		if(this.rendered && this.loading){
            		this.loading.disable();
			this.first.setDisabled(true);
			this.prev.setDisabled(true);
			this.next.setDisabled(true);
			this.last.setDisabled(true);
        	}

	}

});

//Fire the changecomplete event after the animation finished when click on a random position of the slider 
Ext.override(Ext.Slider, {
    setValue : function(v, animate, changeComplete){
        v = this.normalizeValue(v);
        if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){
            this.value = v;
	    // letting moveThumb handle the firing of changecomplete
            this.moveThumb(this.translateValue(v), animate !== false, changeComplete, v);
            this.fireEvent('change', this, v);
        }
    },
    moveThumb: function(v, animate, changeComplete, rawValue){
        if(!animate || this.animate === false){
            this.thumb.setLeft(v);
            if(changeComplete){
                this.fireEvent('changecomplete', this, rawValue);
            }
        }else{
		// firing of changecomplete as callback after shif animation
		var opts = {left: v, stopFx: true, duration:.35};
		if(changeComplete) {
			opts.callback = function(el, v) {
				this.fireEvent('changecomplete', this, v);
			}.createDelegate(this, [rawValue], true);
		}
            this.thumb.shift(opts);
        }
    }
});
	
// Reason: prevent loosing multiple selections when clicking fast on the checkboxes of a grid 
Ext.override(Ext.grid.CheckboxSelectionModel, {
    onMouseDown : function(e, t){
        if(e.button === 0 
		&& (t.className == 'x-grid3-row-checker' 
			// expanding to whole cell
			|| t.className.indexOf('x-grid3-td-checker') > -1
			// taking border above and below into account, cuz then the target is the row
			// when clicked max 20 pix away from left border, its likely to be the checkbox
			|| t.className.indexOf('x-grid3-row') > -1 && e.xy[0] - Ext.fly(t).getLeft() <= 20)
		){ 
            e.stopEvent();
            var row = e.getTarget('.x-grid3-row');
            if(row){
                var index = row.rowIndex;
                if(this.isSelected(index)){
                    this.deselectRow(index);
                }else{
                    this.selectRow(index, true);
                }
            }
        }
    }
});
	
/* disabling scrolling for menus */
Ext.override(Ext.menu.Menu, {
	enableScrolling : false
});

/* additional VTypes to use in TextFields for validation */
Ext.apply(Ext.form.VTypes, {
	'intRegex' : /^[0-9]+$/,
	'int' : function(v){
		return this.intRegex.test(v);
	},
	'intText' : 'This field should be a number.',
	'intMask' : /[0-9]/,

	'ipRegex' : /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/([0-9]{1,2}))?$/,
	'ip' : function(v){
		return this.ipRegex.test(v);
	},
	'ipText' : 'This field should be an IP address in the format "123.123.123.123" or "1.12.234.0/24".',
	'ipMask' : /[0-9\.\/]/,

	'iplistRegex': /^[0-9]{1,3}(\.[0-9]{1,3}){3}(\/[0-9]{1,2})?([;, \t\r\n]+[0-9]{1,3}(\.[0-9]{1,3}){3}(\/[0-9]{1,2})?)*[;, \t\r\n]*$/,
	'iplist'     : function(p) {return this.iplistRegex.test(p);},
	'iplistText' : 'This field must be a space separated list of IP/Mask addresses, like 1.12.123.0/28 .',
	'iplistMask' : /[0-9\.\/;, \t]/,

	'timeRegex' : /^[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$/,
	'time' : function(v){
		if(this.timeRegex.test(v)) {
			var t = v.split(":");
			var h = parseInt(t[0]);
			var m = parseInt(t[1]);
			var s = !t[2] ? 0 : parseInt(t[2]);
			return h >= 0 && h < 24 && m >= 0 && m < 60 && s >= 0 && s < 60;
		}
		return false;
	},
	'timeText' : 'This field should be a time in the format "9:12" or "9:12:35".',
	'timeMask' : /[0-9:]/

});

Ext.override(Ext.data.JsonReader, {
	readResponse : function(action, response) {
        var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
        if(!o) {
            throw new Ext.data.JsonReader.Error('response');
        }
        if (Ext.isEmpty(this.getSuccess(o))) {
            throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
        }
        // TODO, separate empty and undefined exceptions.
        if (action === Ext.data.Api.actions.create && this.getSuccess(o) !== false) {
            var root = this.getRoot(o),
                def = Ext.isDefined(root);
            if (def && Ext.isEmpty(root)) {
                throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
            }
            else if (!def) {
                throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
            }
        }
        return o;
    }	
});


Ext.override(Ext.form.DateField, {
	format : Date.patterns.ISO8601Long
});

/* Add time field to default DatePicker, to prevent this, set dateOnly = true */
Ext.override(Ext.DatePicker, {

	dateOnly: false,
	format : Date.patterns.ISO8601Long,

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

        	this.value = this.value || new Date();

		this.addEvents(
		    /**
		     * @event select
		     * Fires when a date is selected
		     * @param {DatePicker} this DatePicker
		     * @param {Date} date The selected date
		     */
		    'select'
		);

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

		this.initDisabledDays();

		if(!this.dateOnly){
			Ext.apply(this, {
				// abusing Today button for closing the picker
				todayText : 'Ok',
				todayTip : '',


				// dont fire select event just clicking on date, user has to click Ok
				handleDateClick : function(e, t){
					e.stopEvent();
					if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
					    this.setValue(new Date(t.dateValue));
					    //this.fireEvent('select', this, this.value);
					}
				},

				// abusing this function to close the picker, acutally user clicked "Ok"
				selectToday : function(button) {
					if(this.timeField.isValid()) {
						this.fireEvent("select", this, this.getValue());
					}
				},

				// prevent clearTime() call
				setValue : function(value){
					this.value = value;
					this.update(this.value);
				},

				// enhancing getValue to add time
				getValue : function() {
					var date = this.value.clearTime(true);
					if(this.timeField && this.timeField.getValue()) {
						var t = this.timeField.getValue().split(":");
						var h = parseInt(t[0]);
						var m = parseInt(t[1]);
						var s = !t[2] ? 0 : parseInt(t[2]);
						date = date.add(Date.HOUR, h);
						date = date.add(Date.MINUTE, m);
						date = date.add(Date.SECOND, s);
					}
					return date;
				}
			});
		} else {
			this.format = Date.patterns.ISO8601Short;

			this.value.clearTime(true);
		}
	},

	onRender : Ext.DatePicker.prototype.onRender.createSequence(function() {
		if(!this.dateOnly){
			// getting bottom row
			var bottom = this.el.child("td.x-date-bottom");
			// moving the field to the left, otherwise the list looks bad
			bottom.dom.align = 'left';
			// moving the button to the right
			bottom.child("table.x-btn").setStyle('float', 'right');

			this.timeField = new Ext.form.TextField({
				emptyText: 'Time',
				vtype: 'time',
				width: 100,
				value: this.value.format("H:i:s"),
				renderTo: bottom,
				listeners: {
					focus: function() {
						this.keyNav.disable();
					}.createDelegate(this),
					blur: function() {
						this.keyNav.enable();
					}.createDelegate(this)
				}
			});
		}
	})
});

/* disable left and right cursor keys in menu to allow cursor nav in text fields */
Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
    function up(e, m){
        if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
            m.tryActivate(m.items.length-1, -1);
        }
    }
    function down(e, m){
        if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
            m.tryActivate(0, 1);
        }
    }
    return {
        constructor : function(menu){
            Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
            this.scope = this.menu = menu;
        },

        doRelay : function(e, h){
            var k = e.getKey();
//          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
            if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
                return false;
            }
            if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
                this.menu.tryActivate(0, 1);
                return false;
            }
            return h.call(this.scope || this, e, this.menu);
        },

        tab: function(e, m) {
            e.stopEvent();
            if (e.shiftKey) {
                up(e, m);
            } else {
                down(e, m);
            }
        },

        up : up,

        down : down,

	/*
        right : function(e, m){
            if(m.activeItem){
                m.activeItem.expandMenu(true);
            }
        },

        left : function(e, m){
            m.hide();
            if(m.parentMenu && m.parentMenu.activeItem){
                m.parentMenu.activeItem.activate();
            }
        },
	*/

        enter : function(e, m){
            if(m.activeItem){
                e.stopPropagation();
                m.activeItem.onClick(e);
                m.fireEvent('click', this, m.activeItem);
                return true;
            }
        }
    };
}());
