﻿/*
 * Ext Scheduler v1.5 beta
 * Copyright(c) 2009-2010 Mats Bryntse Consulting
 * mats@ext-scheduler.com
 * http://www.ext-scheduler.com/license.html
 *
 */
 
Ext.apply(Date.prototype, {
    /**
     * Checks if this date is >= start and < end.
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Boolean} true if this date falls on or between the given start and end dates.
     */
    betweenLesser : function(start, end) {
        var t = this.getTime();
        return start.getTime() <= t && t < end.getTime();
    },
    
    // Override to add WEEK case
    add : function(interval, value) {
        var d = this.clone();
        if (!interval || value === 0) return d;

        switch(interval.toLowerCase()) {
            case Date.MILLI:
                d.setMilliseconds(this.getMilliseconds() + value);
                break;
            case Date.SECOND:
                d.setSeconds(this.getSeconds() + value);
                break;
            case Date.MINUTE:
                d.setMinutes(this.getMinutes() + value);
                break;
            case Date.HOUR:
                d.setHours(this.getHours() + value);
                break;
            case Date.DAY:
                d.setDate(this.getDate() + value);
                break;
            case Date.WEEK:
                d.setDate(this.getDate() + value * 7);
                break;
            case Date.MONTH:
                var day = this.getDate();
                if (day > 28) {
                    day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
                }
                d.setDate(day);
                d.setMonth(this.getMonth() + value);
                break;
            case Date.YEAR:
                d.setFullYear(this.getFullYear() + value);
                break;
        }
        return d;
    },
    
    // Rounds the date to nearest minute increment
    round : function(minuteIncrement) {
        var c = this.clone(),
            h = this.getHours(),
            min = this.getMinutes(),
            roundedMinutes = Math.round(((h * 60) + min) / minuteIncrement) * minuteIncrement,
            wholeHours = roundedMinutes % 60;
        c.setHours(wholeHours);
        c.setMinutes(roundedMinutes - (60 * wholeHours));
        return c;
    },
    
    // Floors the date to nearest minute increment
    floor : function(minuteIncrement) {
        var c = this.clone(),
            h = this.getHours(),
            min = this.getMinutes(),
            roundedMinutes = Math.floor(((h * 60) + min) / minuteIncrement) * minuteIncrement,
            wholeHours = roundedMinutes % 60;
        c.setHours(wholeHours);
        c.setMinutes(roundedMinutes - (60 * wholeHours));
        return c;
    }
});

Ext.applyIf(Date, {
    /**
     * Checks if the two dates are the same day
     * @param {Date} date1 First date
     * @param {Date} date2 Second date
     * @return {Boolean} Returns true if the two dates are the same day
     */
    isSameDay : function (date1, date2) {
        return date1.getFullYear() === date2.getFullYear() &&
               date1.getMonth() === date2.getMonth() &&
               date1.getDate() === date2.getDate();
    },
    
    /**
     * Returns the number of minutes between the two dates
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Int} true number of minutes between the two dates
     */
    getDurationInMinutes : function (start, end) {
        return (end - start) / 60000;
    },
    
    /**
     * Returns the number of hours between the two dates
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Int} true number of hours between the two dates
     */
    getDurationInHours : function (start, end) {
        return (end - start) / 3600000;
    },
    
    /**
     * Returns the number of whole days between the two dates
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Int} true number of days between the two dates
     */
    getDurationInDays : function (start, end) {
        return (end - start) / 86400000;
    },
    
    /**
     * Returns the number of business days between the two dates
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Int} true number of business days between the two dates
     */
    getDurationInBusinessDays : function (start, end) {
        var nbrDays = Math.round((end - start) / 86400000),
            nbrBusinessDays = 0,
            d;
        
        for (var i = 0; i < nbrDays; i++) {
            d = start.add(Date.DAY, i).getDay();
            if (d !== 6 && d !== 0) {
                nbrBusinessDays++;
            }
        }
        return nbrBusinessDays;
    },
    
    /**
     * Returns the number of whole months between the two dates
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Int} true number of whole months between the two dates
     */
    getDurationInMonths : function (start, end) {
        return ((end.getFullYear() - start.getFullYear()) * 12) + (end.getMonth() - start.getMonth());
    },
    
    /**
     * Returns the lesser of the two dates
     * @param {Date} date 1
     * @param {Date} date 2
     * @return {Date} Returns the lesser of the two dates
     */
    min : function (d1, d2) {
        return d1 < d2 ? d1 : d2;
    },
    
    /**
     * Returns the greater of the two dates
     * @param {Date} date 1
     * @param {Date} date 2
     * @return {Date} Returns the greater of the two dates
     */
    max : function (d1, d2) {
        return d1 > d2 ? d1 : d2;
    },
    
    /**
     * Returns true if dates intersect
     * @param {Date} start 1
     * @param {Date} end 1
     * @param {Date} start 2
     * @param {Date} end 2
     * @return {Boolean} Returns true if dates intersect
     */
    intersectSpans : function (date1Start, date1End, date2Start, date2End) {
        return date1Start.betweenLesser(date2Start, date2End) || 
               date2Start.betweenLesser(date1Start, date1End);
    },
    
    /**
     * Date interval constant
     * @static
     * @type String
     */
    WEEK : "w"
});


// Override to be able to save the previous value for record fields
Ext.override(Ext.data.Record, {
    set : function(name, value){
        var encode = Ext.isPrimitive(value) ? String : Ext.encode;
        if(encode(this.data[name]) == encode(value)) {
            return;
        }        
        this.dirty = true;
        
        // Keep previous Field value
        if(!this.previous){
            this.previous = {};
        }
        this.previous[name] = this.data[name];

        if(!this.modified){
            this.modified = {};
        }
        if(this.modified[name] === undefined){
            this.modified[name] = this.data[name];
        }

        this.data[name] = value;
        if(!this.editing){
            this.afterEdit();
        }
    },
    
    afterEdit: function(){
        if(this.store){
            this.store.afterEdit(this, this.previous);
        }
        
        // Don't need this anymore
        delete this.previous;
    },
    
    reject : function(silent){
        var m = this.modified,
            current = {};
        for(var n in m){
            if(typeof m[n] != "function"){
                // Save current value
                current[n] = this.data[n];
                this.data[n] = m[n];
            }
        }
        delete this.modified;
        this.dirty = false;
        this.editing = false;
        if(silent !== true){
            this.afterReject(current);
        }
    },
    
    // private
    afterReject: function(current){
        if(this.store){
            this.store.afterReject(this, current);
        }
    }
});

Ext.override(Ext.data.Store,{
    afterEdit : function(record, hashPrevious){
        if(this.modified.indexOf(record) == -1){
            this.modified.push(record);
        }
        this.fireEvent('update', this, record, Ext.data.Record.EDIT, hashPrevious);
    },

    // private
    afterReject : function(record, hashPrevious){
        this.modified.remove(record);
        this.fireEvent('update', this, record, Ext.data.Record.REJECT, hashPrevious);
    }
});


Ext.override(Ext.grid.ColumnModel, {
   // Override to handle bypass the destruction of column editors in setConfig (introduced in Ext 3.1.1)
    setConfig : function(config, initial, ignoreDestroy){
        var i, c, len;
        if(!initial){ // cleanup
            delete this.totalWidth;
            
            if (Ext.grid.Column.prototype.destroy && !ignoreDestroy) {
                for(i = 0, len = this.config.length; i < len; i++){
                    this.config[i].destroy();
                }
            }
        }
        
        // backward compatibility
        this.defaults = Ext.apply({
            width: this.defaultWidth,
            sortable: this.defaultSortable
        }, this.defaults);

        this.config = config;
        this.lookup = {};

        for(i = 0, len = config.length; i < len; i++){
            c = Ext.applyIf(config[i], this.defaults);
            // if no id, create one using column's ordinal position
            if(Ext.isEmpty(c.id)){
                c.id = i;
            }
            if(!c.isColumn){
                var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
                c = new Cls(c);
                config[i] = c;
            }
            this.lookup[c.id] = c;
        }
        if(!initial){
            this.fireEvent('configchange', this);
        }
    }
});

if (Ext.ux && Ext.ux.grid && Ext.ux.grid.LockingColumnModel) {
    Ext.override(Ext.ux.grid.LockingColumnModel, {
        getLockedCount : function(){
            for(var i = 0, len = this.config.length; i < len; i++){
                if(!this.isLocked(i)){
                    return i;
                }
            }
            return len;
        }
    });
}