﻿//Fix Gettime, It will getTime from 1/1/1 00:00:00 From gregorian date:
//Default getTime will return from 1970/1/1 00:00:00
Date.prototype.fixGetTime = function() {
    var oldOffset = this.getTime();
    var firstYear = new Date();
    firstYear.setFullYear(1, 0, 1);
    firstYear.setHours(0, 0, 0, 0);
    var newOffset = firstYear.getTime();
    return (oldOffset + (Math.abs(newOffset)));
}
var v = {};
/*--------------  Base Calendar Type (Gregorian) default based ---------------------*/
v.cal = function() {
    this.m365 = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
    this.m366 = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
    this.GetDatePart = function(ticks, part) {
        var num1 = parseInt((ticks / 86400000));
        var num2 = parseInt(num1 / 146097);
        num1 = (num1 - (num2 * 146097));
        var num3 = parseInt(num1 / 36524);
        if (num3 == 4) {
            num3 = 3
        };
        num1 = (num1 - (num3 * 36524));
        var num4 = parseInt(num1 / 1461);
        num1 = (num1 - (num4 * 1461));
        var num5 = parseInt(num1 / 365);
        if (num5 == 4) {
            num5 = 3;
        };
        if (part == 0) {
            return (((((num2 * 400) + (num3 * 100)) + (num4 * 4)) + num5) + 1);
        };
        num1 = (num1 - (num5 * 365));
        if (part == 1) {
            return (num1 + 1);
        };
        var numArray1 = (this.isLeapYear()) ? this.m366 : this.m365;
        var num6 = parseInt(num1 >> 6);
        while (num1 >= numArray1[num6]) {
            num6 += 1;
        };
        if (part == 2) {
            return num6
        };
        return ((num1 - numArray1[(num6 - 1)]) + 1);
    };
    this.GetAbsoluteDate = function(year, month, day) {
        if (((year >= 1) || (year <= 9999)) || ((month >= 1) || (month <= 12))) {
            var numArray1 = (this.isLeapYear(year)) ? this.m366 : this.m365;
            if ((day >= 1) && (day <= (numArray1[month] - numArray1[(month - 1)]))) {
                var num1 = parseInt(year - 1);
                var num2 = parseInt(((((((num1 * 365) + (num1 / 4)) - (num1 / 100)) + (num1 / 400)) + numArray1[(month - 1)]) + day) - 1);
                return parseFloat(num2);
            };
        };
    };
    this.DateToTicks = function(year, month, day) {
        return (this.GetAbsoluteDate(year, month, day) * 864000000000);
    };
}
v.cal.prototype.check_y = function(year) {
    return (year >= 1) && (year <= 9999);
}
v.cal.prototype.check_y_m = function(year, month) {
    return (this.check_y(year) && (month >= 1) && (month <= 12));
}
v.cal.prototype.check_y_m_d = function(year, month, day) {
    var valid = this.check_y_m(year, month);
    if (valid) {
        var days = this.getDaysInMonth(year, month);
        valid = ((day >= 1) && (day <= days));
    };
    return valid;
}
v.cal.prototype.info = { name: "Gregorian", version: "1" };
v.cal.prototype.strings = {
    lmonth: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    smonth: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
    ldays: ["Sunday", "Monday", "Thuesday", "Wednesday", "Thursday", "Friyday", "Saturday"],
    sdays: ["Sun", "Mon", "Thu", "Wed", "Thur", "Fri", "Sat"],
    years: ["مار", "اسب", "گوسفند", "میمون", "مرغ", "سگ", "خوک", "موش", "گاو", "پلنگ", "خرگوش", "نهنگ"],
    dll: ["A.M", "P.M"],
    dls: ["am", "pm"]
}
v.cal.prototype.toDateTime = function(year, month, day, hour, min, sec, mili) {
    var dt = new Date(year, (month - 1), day, hour, min, sec, mili);
    return dt;
}
v.cal.prototype.isLeapYear = function(year) {
    if ((year % 4) != 0) {
        return false;
    };
    if ((year % 100) == 0) {
        return ((year % 400) == 0);
    };
    return true;
}
v.cal.prototype.isLeapMonth = function(year, month) {
    return false;
}
v.cal.prototype.isLeapDay = function(year, month, day) {
    if (this.isLeapYear(year) && ((month == 2) && (day == 29))) {
        return true;
    };
    return false;
}
v.cal.prototype.getYear = function(datetime) {
    return this.GetDatePart(datetime.fixGetTime(), 0);
}
v.cal.prototype.getMonthsInYear = function(year) {
    return 12;
}
v.cal.prototype.getMonth = function(datetime) {
    return this.GetDatePart(datetime.fixGetTime(), 2);
}
v.cal.prototype.getDaysInYear = function(year) {
    if (this.isLeapYear(year))
        return 366;
    else
        return 365;
}
v.cal.prototype.getDaysInMonth = function(year, month) {
    var numArray1 = (this.isLeapYear(year)) ? this.m366 : this.m365;
    return (numArray1[month] - numArray1[(month - 1)]);
}
v.cal.prototype.getDayOfYear = function(datetime) {
    return this.GetDatePart(datetime.fixGetTime(), 1);
}
v.cal.prototype.getDayOfWeek = function(datetime) {
    return ((parseInt(datetime.fixGetTime() / 86400000) + 1) % 7);
}
v.cal.prototype.getDayOfMonth = function(datetime) {
    return this.GetDatePart(datetime.fixGetTime(), 3);
}
v.cal.prototype.getTicks = function(year, month, day, hour, min, sec, mili) {
    var rMe = this.toDateTime(year, month, day, hour, min, sec, mili);
    return rMe.getTime();
}
v.cal.prototype.addMonths = function(datetime, months) {
    if ((months < -120000) || (months > 120000)) {
        alert("Invalid Month"); return null;
    };
    var num1 = parseInt(this.GetDatePart(datetime.fixGetTime(), 0));
    var num2 = parseInt(this.GetDatePart(datetime.fixGetTime(), 2));
    var num3 = parseInt(this.GetDatePart(datetime.fixGetTime(), 3));
    var num4 = parseInt(((num2 - 1) + months));
    if (num4 >= 0) {
        num2 = ((num4 % 12) + 1);
        num1 = (num1 + (num4 / 12));
    } else {
        num2 = (12 + ((num4 + 1) % 12));
        num1 = (num1 + ((num4 - 11) / 12));
    };
    var numArray1 = (this.isLeapYear(num1)) ? this.m366 : this.m365;
    var num5 = parseInt(numArray1[num2] - numArray1[(num2 - 1)]);
    if (num3 > num5) {
        num3 = num5;
    };
    var num6 = parseFloat(this.DateToTicks(num1, num2, num3) + (datetime.fixGetTime() % 86400000));
    var rValue = new Date();
    rValue.setTime(num6);
    return rValue;
}
v.cal.prototype.addYears = function(datetime, year) {
    return this.addMonths(datetime, (years * 12));
}
/*----------------------- End Base Calendar functions ---------------- */

/*---------------------- (Persian Calendar) (solar) or (Hijri shamsi) -----------------------*/
/* overiding functions for new calendar type */
v.pcal = new v.cal();
v.pcal.info.name = "Persian";
v.pcal.info.version = "1.0";
v.pcal.strings = {
    lmonth: ["فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"],
    smonth: ["حمل", "ثور", "جوزا", "سرطان", "اسد", "سنبله", "میران", "عقرب", "قوس", "جدی", "دلو", "حوت"],
    ldays: ["یکشنبه", "دوشنبه", "سه شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه"],
    sdays: ["ی", "د", "س", "چ", "پ", "ج", "ش"],
    years: ["مار", "اسب", "گوسفند", "میمون", "مرغ", "سگ", "خوک", "موش", "گاو", "پلنگ", "خرگوش", "نهنگ"],
    dll: ["صبح", "عصر"],
    dls: ["ق.ظ", "ب.ظ"]
}
v.pcal.DaysToMonth = [0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336];
v.pcal.leapYears33 = [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0];
v.pcal.isLeapYear = function(year) {
    return (this.leapYears33[(year % 33)] == 1);
}
v.pcal.check_y = function(year) {
    return ((year >= 1) && (year <= 9378));
}
v.pcal.check_y_m = function(year, month) {
    var valid = this.check_y(year);
    if (valid) {
        if ((year == 9378) && (month > 10)) {
            valid = false;
        };
        if (valid) {
            valid = (month >= 1 && month <= 12);
        };
    };
    return valid;
}
v.pcal.getDaysInMonth = function(year, month) {
    if ((month == 10) && (year == 9378)) {
        return 10;
    };
    if (month == 12) {
        if (this.isLeapYear(year))
            return 30;
        else
            return 29;
    };
    if (month <= 6)
        return 31;
    else
        return 30;
}
v.pcal.DaysUpToPersianYear = function(pyear) {
    var num2 = parseInt((pyear - 1) / 33);
    var num3 = parseInt((pyear - 1) % 33);
    var num1 = parseFloat((num2 * 12053) + 226894);
    while (num3 > 0) {
        num1 = (num1 + 365);
        if (this.isLeapYear(num3)) {
            num1 += 1;
        };
        num3 -= 1;
    };
    return num1;
}
v.pcal.GetAbsoluteDatePersian = function(year, month, day) {
    return (((this.DaysUpToPersianYear(year) + this.DaysToMonth[(month - 1)]) + day) - 1);
}
v.pcal.getDaysInYear = function(year) {
    if (year == 9378) {
        return (this.DaysToMonth[9] + 10);
    };
    if (!this.isLeapYear(year)) {
        return 365;
    };
    return 366;
}
v.pcal.TimeToTicks = function(Hour, Minute, Second, Mili) {
    return (Mili + (Second * 1000) + (Minute * 60 * 1000) + (Hour * 60 * 60 * 1000));
}
v.pcal.ToTime = function(year, month, day, hour, minute, second, millisecond) {
    var daysInCalendar = this.GetAbsoluteDatePersian(year, month, day);
    var totalTime = (daysInCalendar * 86400000) + this.TimeToTicks(hour, minute, second, millisecond);
    return totalTime;
}
v.pcal.GetDatePart = function(ticks, part) {
    var num4 = parseFloat((ticks / 86400000) + 1);
    var num1 = parseInt(parseInt((((num4 - 226894) * 33) / parseFloat(12053))) + 1);
    var num5 = parseFloat(this.DaysUpToPersianYear(num1));
    var num6 = parseFloat(this.getDaysInYear(num1));
    if (num4 < num5) {
        num5 = (num5 - num6);
        num1 -= 1;
    } else if (num4 == num5) {
        num1 -= 1;
        num5 = (num5 - this.getDaysInYear(num1));
    } else if (num4 > (num5 + num6)) {
        num5 = (num5 + num6);
        num1 += 1;
    };
    if (part != 0) {
        num4 = (num4 - num5);
        if (part != 1) {
            var num2 = 0;
            while ((num2 < 12) && (num4 > this.DaysToMonth[num2])) {
                num2 += 1;
            };
            if (part != 2) {
                var num3 = parseInt(parseInt(num4) - this.DaysToMonth[(num2 - 1)]);
                return num3;
            };
            return num2;
        };
        return parseInt(num4);
    };
    return num1;
}
v.pcal.addMonths = function(DateTime, months) {
    if ((months < -120000) || (months > 120000)) {
        alert("Invalid month"); return null;
    };
    var num1 = parseInt(this.GetDatePart(DateTime.fixGetTime(), 0));
    var num2 = parseInt(this.GetDatePart(DateTime.fixGetTime(), 2));
    var num3 = parseInt(this.GetDatePart(DateTime.fixGetTime(), 3));
    var num4 = parseInt(((num2 - 1) + months));
    if (num4 >= 0) {
        num2 = ((num4 % 12) + 1);
        num1 = (num1 + (num4 / 12));
    } else {
        num2 = (12 + ((num4 + 1) % 12));
        num1 = (num1 + ((num4 - 11) / 12));
    };
    var num5 = parseInt(this.getDaysInMonth(num1, num2));
    if (num3 > num5) {
        num3 = num5;
    };
    var num6 = parseFloat((this.GetAbsoluteDatePersian(num1, num2, num3) * 86400000) + (DateTime.fixGetTime() % 86400000));
    var rValue = new Date();
    rValue.setTime(num6);
    return rValue;
}
v.pcal.toDateTime = function(year, month, day, hour, minute, second, millisecond) {
    var returnMe = new Date();
    var num1 = this.getDaysInMonth(year, month);
    var num2 = this.GetAbsoluteDatePersian(year, month, day);
    var totalTicks = (num2 * 86400000) + this.TimeToTicks(hour, minute, second, millisecond);
    var bTicks = new Date();
    bTicks.setFullYear(1, 0, 1);
    bTicks.setHours(0, 0, 0, 0);
    totalTicks -= Math.abs(bTicks.getTime());
    returnMe.setTime(totalTicks);
    return returnMe;
}
v.pcal.isLeapMonth = function(year, month) {
    return (this.isLeapYear(year) && (month == 12));
}
v.pcal.isLeapDay = function(year, month, day) {
    if (this.isLeapYear(year) && (month == 12)) {
        return (day == 30);
    };
    return false;
}
v.pcal.getMonthsInYear = function(year) {
    if (year == 9378)
        return 10;
    else return 12;
}
v.pcal.getTicks = function(year, month, day, hour, minute, second, millisecond) {
    var totalTime = this.ToTime(year, month, day, hour, minute, second, millisecond);
    var negativeTime = new Date();
    negativeTime.setFullYear(1, 0, 1);
    negativeTime.setHours(0, 0, 0, 0);
    totalTime -= Math.abs(negativeTime.getTime());
    return totalTime;
}
/*--------- end pcal ---------*/

/*---------------  v.date object  -------------------------- */
///New Date function it can do like Date, also we can change or add anything that it need.
v.date = function(year, month, day, hour, min, sec, calendar) {
    var dt = new Date();
    this.vars = {};
    this.vars.cal = (calendar) ? calendar : new v.cal();
    this.vars.year = year || this.vars.cal.getYear(dt);
    this.vars.month = month || this.vars.cal.getMonth(dt);
    this.vars.day = day || this.vars.cal.getDayOfMonth(dt);
    this.vars.hour = hour || dt.getHours();
    this.vars.minute = min || dt.getMinutes();
    this.vars.second = sec || dt.getSeconds();
    this.twodigit = function(iNumber) {
        if (iNumber.toString().length < 2) {
            return '0' + iNumber.toString();
        } else {
            return iNumber.toString();
        };
    };
    this.daylight = function(longf) {
        if (this.vars.hour < 12) {
            return (longf) ? this.vars.cal.strings.dll[0] : this.vars.cal.strings.dls[0];
        } else {
            return (longf) ? this.vars.cal.strings.dll[1] : this.vars.cal.strings.dls[1];
        };
    };
    this.hour12 = function() {
        if (this.vars.hour == 0) {
            return 12;
        };
        if (this.vars.hour > 12) {
            return this.vars.hour - 12;
        } else {
            return this.vars.hour;
        };
    };
    this.checkTime = function(part, mode) {
        switch (mode) {
            case 'h':
                return (part >= 0 && part <= 23);
                break;
            case 'ms':
                return (part >= 0 && part <= 59);
                break;
        };
    };
    this.validate = function() {
        var d = new Date();
        if (!this.vars.cal.check_y(this.vars.year))
            this.vars.year = this.vars.cal.getYear(d);
        if (!this.vars.cal.check_y_m(this.vars.year, this.vars.month))
            this.vars.month = this.vars.cal.getMonth(d);
        if (!this.vars.cal.check_y_m_d(this.vars.year, this.vars.month, this.vars.day))
            this.vars.day = this.vars.cal.getDayOfMonth(d);
        if (!this.checkTime(this.vars.hour, 'h'))
            this.vars.hour = d.getHours();
        if (!this.checkTime(this.vars.minute, 'ms'))
            this.vars.minute = d.getMinutes();
        if (!this.checkTime(this.vars.second, 'ms'))
            this.vars.second = d.getSeconds();
    };
    this.validate();
    this.dt = this.vars.cal.toDateTime(this.vars.year, this.vars.month, this.vars.day, this.vars.hour, this.vars.minute, this.vars.second, 0);
    this.f = ['y{4}', 'y{3}', 'y{2}', 'M{4}', 'M{3}', 'M{2}', 'M{1}', 'd{4}', 'd{2}', 'd{1}', 'H{2}', 'H{1}', 'h{2}', 'h{1}', 'm{2}', 'm{1}', 's{2}', 's{1}', 'g{2}'];
}
v.date.prototype.setDate = function(datetime) {
    this.vars.year = this.vars.cal.getYear(datetime);
    this.vars.month = this.vars.cal.getMonth(datetime);
    this.vars.day = this.vars.cal.getDayOfMonth(datetime);
    this.vars.hour = datetime.getHours();
    this.vars.minute = datetime.getMinutes();
    this.vars.second = datetime.getSeconds();
    this.dt = datetime;
}
v.date.prototype.getYear = function() { return this.vars.year; }
v.date.prototype.getMonth = function() { return this.vars.month; }
v.date.prototype.getDate = function() { return this.vars.day; }
v.date.prototype.getHours = function() { return this.vars.hour; }
v.date.prototype.getMinutes = function() { return this.vars.minute; }
v.date.prototype.getSeconds = function() { return this.vars.second; }
v.date.prototype.getFullYear = function() { return this.getYear(); }
v.date.prototype.isLeapYear = function() { return this.vars.cal.isLeapYear(this.getYear()); }
v.date.prototype.getTwoDigitYear = function() {
    var iYear = this.getYear();
    iYear = iYear + "";
    return iYear.substring((iYear.length - 2), iYear.length);
}
v.date.prototype.getDay = function() { return this.vars.cal.getDayOfWeek(this.dt); }
v.date.prototype.getDayName = function(LongFormat) {
    var index = this.getDay();
    return (LongFormat) ? this.vars.cal.strings.ldays[index] : this.vars.cal.strings.sdays[index];
}
v.date.prototype.getYearName = function() {
    return this.vars.cal.strings.years[(this.getYear() % 12)];
}
v.date.prototype.getMonthName = function(LongFormat) {
    var index = this.getMonth() - 1;
    return (LongFormat) ? this.vars.cal.strings.lmonth[index] : this.vars.cal.strings.smonth[index];
}
v.date.prototype.getMonthLength = function() {
    return this.vars.cal.getDaysInMonth(this.getYear(), this.getMonth());
}
v.date.prototype.getTime = function() {
    return this.vars.cal.getTicks(this.getYear(), this.getMonth(), this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds(), 0);
}
v.date.prototype.valueOf = function() { return this.getTime(); }
v.date.prototype.getDayOfYear = function() { return this.vars.cal.getDayOfYear(this.dt); }
v.date.prototype.toString = function(format) {
    if (format) {
        var strValues = [
		this.getYear(), this.getYearName(), this.getTwoDigitYear(),
		this.getMonthName(true), this.getMonthName(false), this.twodigit(this.getMonth()), this.getMonth(),
		this.getDayName(true), this.twodigit(this.getDate()), this.getDate(),
		this.twodigit(this.getHours()), this.getHours(), this.twodigit(this.hour12()), this.hour12(),
		this.twodigit(this.getMinutes()), this.getMinutes(),
		this.twodigit(this.getSeconds()), this.getSeconds(),
		this.daylight()
		];
        var re = new RegExp();
        var returnMe = new String();
        returnMe = format;
        for (i = 0; i < this.f.length; i++) {
            re.compile("%" + this.f[i], 'g');
            returnMe = returnMe.replace(re, strValues[i]);
        };
        return returnMe;
    } else {
        return (this.getDayName(true) + ' ' + this.getDate() + ' ' + this.getMonthName(true) + ' ' + this.getYear());
    };
}
v.date.prototype.setFullYear = function(year, month, day) {
    this.vars.year = year || this.vars.year;
    this.vars.month = month || this.vars.month;
    this.vars.day = day || this.vars.day;
    this.validate();
    var dt = this.vars.cal.toDateTime(this.vars.year, this.vars.month, this.vars.day, this.getHours(), this.getMinutes(), this.getSeconds(), 0);
    this.setDate(dt);
}
v.date.prototype.calendar = function() { return this.vars.cal; }
v.date.prototype.changeCalendar = function(cal) {
    this.vars.cal = cal;
    this.setDate(this.dt);
}
