/*  You may notice that a variety of array variables logically local
    to functions are declared globally here.  In JavaScript, construction
    of an array variable from source code occurs as the code is
    interpreted.  Making these variables pseudo-globals permits us
    to avoid overhead constructing and disposing of them in each
    call on the function in which they are used.  */

var J0000 = 1721424.5;                // Julian date of Gregorian epoch: 0000-01-01
var J1970 = 2440587.5;                // Julian date at Unix epoch: 1970-01-01
var JMJD  = 2400000.5;                // Epoch of Modified Julian Date system
var J1900 = 2415020.5;                // Epoch (day 1) of Excel 1900 date system (PC)
var J1904 = 2416480.5;                // Epoch (day 0) of Excel 1904 date system (Mac)

var NormLeap = new Array("Normal year", "Leap year");

var gregorian_year, gregorian_month, gregorian_day, gregorian_hour,
    gregorian_min, gregorian_sec, gregorian_leap;
var islamic_year, islamic_month, islamic_wmonth, islamic_day;
var persian_year, persian_month, persian_wmonth, persian_day;

/*  WEEKDAY_BEFORE  --  Return Julian date of given weekday (0 = Sunday)
                        in the seven days ending on jd.  */

function weekday_before(weekday, jd)
{
    return jd - jwday(jd - weekday);
}

/*  SEARCH_WEEKDAY  --  Determine the Julian date for: 

            weekday      Day of week desired, 0 = Sunday
            jd           Julian date to begin search
            direction    1 = next weekday, -1 = last weekday
            offset       Offset from jd to begin search
*/

function search_weekday(weekday, jd, direction, offset)
{
    return weekday_before(weekday, jd + (direction * offset));
}

//  Utility weekday functions, just wrappers for search_weekday

function nearest_weekday(weekday, jd)
{
    return search_weekday(weekday, jd, 1, 3);
}

function next_weekday(weekday, jd)
{
    return search_weekday(weekday, jd, 1, 7);
}

function next_or_current_weekday(weekday, jd)
{
    return search_weekday(weekday, jd, 1, 6);
}

function previous_weekday(weekday, jd)
{
    return search_weekday(weekday, jd, -1, 1);
}

function previous_or_current_weekday(weekday, jd)
{
    return search_weekday(weekday, jd, 1, 0);
}

function TestSomething()
{
}

//  LEAP_GREGORIAN  --  Is a given year in the Gregorian calendar a leap year ?

function leap_gregorian(year)
{
    return ((year % 4) == 0) &&
            (!(((year % 100) == 0) && ((year % 400) != 0)));
}

//  GREGORIAN_TO_JD  --  Determine Julian day number from Gregorian calendar date

var GREGORIAN_EPOCH = 1721425.5;

function gregorian_to_jd(year, month, day)
{
    return (GREGORIAN_EPOCH - 1) +
           (365 * (year - 1)) +
           Math.floor((year - 1) / 4) +
           (-Math.floor((year - 1) / 100)) +
           Math.floor((year - 1) / 400) +
           Math.floor((((367 * month) - 362) / 12) +
           ((month <= 2) ? 0 :
                               (leap_gregorian(year) ? -1 : -2)
           ) +
           day);
}

//  JD_TO_GREGORIAN  --  Calculate Gregorian calendar date from Julian day

function jd_to_gregorian(jd) {
    var wjd, depoch, quadricent, dqc, cent, dcent, quad, dquad,
        yindex, dyindex, year, yearday, leapadj;

    wjd = Math.floor(jd - 0.5) + 0.5;
    depoch = wjd - GREGORIAN_EPOCH;
    quadricent = Math.floor(depoch / 146097);
    dqc = mod(depoch, 146097);
    cent = Math.floor(dqc / 36524);
    dcent = mod(dqc, 36524);
    quad = Math.floor(dcent / 1461);
    dquad = mod(dcent, 1461);
    yindex = Math.floor(dquad / 365);
    year = (quadricent * 400) + (cent * 100) + (quad * 4) + yindex;
    if (!((cent == 4) || (yindex == 4))) {
        year++;
    }
    yearday = wjd - gregorian_to_jd(year, 1, 1);
    leapadj = ((wjd < gregorian_to_jd(year, 3, 1)) ? 0
                                                  :
                  (leap_gregorian(year) ? 1 : 2)
              );
    month = Math.floor((((yearday + leapadj) * 12) + 373) / 367);
    day = (wjd - gregorian_to_jd(year, month, 1)) + 1;

    return new Array(year, month, day);
}

//  ISO_TO_JULIAN  --  Return Julian day of given ISO year, week, and day

function n_weeks(weekday, jd, nthweek)
{
    var j = 7 * nthweek;

    if (nthweek > 0) {
        j += previous_weekday(weekday, jd);
    } else {
        j += next_weekday(weekday, jd);
    }
    return j;
}


/*  PAD  --  Pad a string to a given length with a given fill character.  */

function pad(str, howlong, padwith) {
    var s = str.toString();

    while (s.length < howlong) {
        s = padwith + s;
    }
    return s;
}

//  JULIAN_TO_JD  --  Determine Julian day number from Julian calendar date

var JULIAN_EPOCH = 1721423.5;

function leap_julian(year)
{
    return (year % 4) == ((year > 0) ? 0 : 3);
}

function julian_to_jd(year, month, day)
{

    /* Adjust negative common era years to the zero-based notation we use.  */

    if (year < 1) {
        year++;
    }

    /* Algorithm as given in Meeus, Astronomical Algorithms, Chapter 7, page 61 */

    if (month <= 2) {
        year--;
        month += 12;
    }

    return ((Math.floor((365.25 * (year + 4716))) +
            Math.floor((30.6001 * (month + 1))) +
            day) - 1524.5);
}

//  JD_TO_JULIAN  --  Calculate Julian calendar date from Julian day

function jd_to_julian(td) {
    var z, a, alpha, b, c, d, e, year, month, day;

    td += 0.5;
    z = Math.floor(td);

    a = z;
    b = a + 1524;
    c = Math.floor((b - 122.1) / 365.25);
    d = Math.floor(365.25 * c);
    e = Math.floor((b - d) / 30.6001);

    month = Math.floor((e < 14) ? (e - 1) : (e - 13));
    year = Math.floor((month > 2) ? (c - 4716) : (c - 4715));
    day = b - d - Math.floor(30.6001 * e);

    /*  If year is less than 1, subtract one to convert from
        a zero based date system to the common era system in
        which the year -1 (1 B.C.E) is followed by year 1 (1 C.E.).  */

    if (year < 1) {
        year--;
    }

    return new Array(year, month, day);
}


//  LEAP_ISLAMIC  --  Is a given year a leap year in the Islamic calendar ?

function leap_islamic(year)
{
    return (((year * 11) + 14) % 30) < 11;
}

//  ISLAMIC_TO_JD  --  Determine Julian day from Islamic date

var ISLAMIC_EPOCH = 1948439.5;
var ISLAMIC_WEEKDAYS = new Array("al-'ahad", "al-'ithnayn",
                                 "ath-thalatha'", "al-'arb`a'",
                                 "al-khamis", "al-jum`a", "as-sabt");
var ISLAMIC_MONTHS = new Array("Muharram","Safar", "Rabi`al-Awwal", "Rabi`ath-Thani", "Jumada l-Ula",
							   "Jumada t-Tania", "Rajab", "Sha`ban", "Ramadan", "Shawwal", "Dhu l-Qa`da", "Dhu l-Hijja");
var ISLAMIC_MONTHS_P = new Array("محرم","صفر", "ربیع‌الاول", "ربیع‌الثانی", "جمادی‌الاولی",
							   "جمادی‌الثانی", "رجب", "شعبان", "رمضان", "شوال", "ذیقعده", "ذیحجه");

function islamic_to_jd(year, month, day)
{
    return (day +
            Math.ceil(29.5 * (month - 1)) +
            (year - 1) * 354 +
            Math.floor((3 + (11 * year)) / 30) +
            ISLAMIC_EPOCH) - 1;
}

//  JD_TO_ISLAMIC  --  Calculate Islamic date from Julian day

function jd_to_islamic(jd)
{
    var year, month, day;

    jd = Math.floor(jd) + 0.5;
    year = Math.floor(((30 * (jd - ISLAMIC_EPOCH)) + 10646) / 10631);
    month = Math.min(12,
                Math.ceil((jd - (29 + islamic_to_jd(year, 1, 1))) / 29.5) ) + 1;
    day = (jd - islamic_to_jd(year, month, 1)) + 1- 1;
    return new Array(year, month, day);
}

//  LEAP_PERSIAN  --  Is a given year a leap year in the Persian calendar ?

function leap_persian(year)
{
    return ((((((year - ((year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682;
}

//  PERSIAN_TO_JD  --  Determine Julian day from Persian date

var PERSIAN_EPOCH = 1948320.5;
var PERSIAN_WEEKDAYS = new Array("Yekshanbeh", "Doshanbeh",
                                 "Seshhanbeh", "Chaharshanbeh",
                                 "Panjshanbeh", "Jomeh", "Shanbeh");
var PERSIAN_MONTHS = new Array("Farvardin", "Ordibehesht", "Khordad", "Tir", "Mordad", "Shahrivar",
							   "Mehr", "Aban", "Azar", "Dey", "Bahman", "Esfand");
var PERSIAN_MONTHS_P = new Array("فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور",
							   "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند");

var PERSIAN_WEEKDAYS_P = new Array("یكشنبه", "دوشنبه",
                                 "سه‌شنبه", "چهار‌شنبه",
                                 " پنج‌شنبه", "جمعه", "شنبه");
var PERSIAN_MONTHS_P = new Array("فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور",
							   "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند");

function persian_to_jd(year, month, day)
{
    var epbase, epyear;

    epbase = year - ((year >= 0) ? 474 : 473);
    epyear = 474 + mod(epbase, 2820);

    return day +
            ((month <= 7) ?
                ((month - 1) * 31) :
                (((month - 1) * 30) + 6)
            ) +
            Math.floor(((epyear * 682) - 110) / 2816) +
            (epyear - 1) * 365 +
            Math.floor(epbase / 2820) * 1029983 +
            (PERSIAN_EPOCH - 1);
}

//  JD_TO_PERSIAN  --  Calculate Persian date from Julian day

function jd_to_persian(jd)
{
    var year, month, day, depoch, cycle, cyear, ycycle,
        aux1, aux2, yday;


    jd = Math.floor(jd) + 0.5;

    depoch = jd - persian_to_jd(475, 1, 1);
    cycle = Math.floor(depoch / 1029983);
    cyear = mod(depoch, 1029983);
    if (cyear == 1029982) {
        ycycle = 2820;
    } else {
        aux1 = Math.floor(cyear / 366);
        aux2 = mod(cyear, 366);
        ycycle = Math.floor(((2134 * aux1) + (2816 * aux2) + 2815) / 1028522) +
                    aux1 + 1;
    }
    year = ycycle + (2820 * cycle) + 474;
    if (year <= 0) {
        year--;
    }
    yday = (jd - persian_to_jd(year, 1, 1)) + 1;
    month = (yday <= 186) ? Math.ceil(yday / 31) : Math.ceil((yday - 6) / 30);
    day = (jd - persian_to_jd(year, month, 1)) + 1;
    return new Array(year, month, day);
}


//  INDIAN_CIVIL_TO_JD  --  Obtain Julian day for Indian Civil date

var INDIAN_CIVIL_WEEKDAYS = new Array(
    "ravivara", "somavara", "mangalavara", "budhavara",
    "brahaspativara", "sukravara", "sanivara");

function indian_civil_to_jd(year, month, day)
{
    var Caitra, gyear, leap, start, jd, m;

    gyear = year + 78;
    leap = leap_gregorian(gyear);     // Is this a leap year ?
    start = gregorian_to_jd(gyear, 3, leap ? 21 : 22);
    Caitra = leap ? 31 : 30;

    if (month == 1) {
        jd = start + (day - 1);
    } else {
        jd = start + Caitra;
        m = month - 2;
        m = Math.min(m, 5);
        jd += m * 31;
        if (month >= 8) {
            m = month - 7;
            jd += m * 30;
        }
        jd += day - 1;
    }

    return jd;
}

//  JD_TO_INDIAN_CIVIL  --  Calculate Indian Civil date from Julian day

function jd_to_indian_civil(jd)
{
    var Caitra, Saka, greg, greg0, leap, start, year, yday, mday;

    Saka = 79 - 1;                    // Offset in years from Saka era to Gregorian epoch
    start = 80;                       // Day offset between Saka and Gregorian

    jd = Math.floor(jd) + 0.5;
    greg = jd_to_gregorian(jd);       // Gregorian date for Julian day
    leap = leap_gregorian(greg[0]);   // Is this a leap year?
    year = greg[0] - Saka;            // Tentative year in Saka era
    greg0 = gregorian_to_jd(greg[0], 1, 1); // JD at start of Gregorian year
    yday = jd - greg0;                // Day number (0 based) in Gregorian year
    Caitra = leap ? 31 : 30;          // Days in Caitra this year

    if (yday < start) {
        //  Day is at the end of the preceding Saka year
        year--;
        yday += Caitra + (31 * 5) + (30 * 3) + 10 + start;
    }

    yday -= start;
    if (yday < Caitra) {
        month = 1;
        day = yday + 1;
    } else {
        mday = yday - Caitra;
        if (mday < (31 * 5)) {
            month = Math.floor(mday / 31) + 2;
            day = (mday % 31) + 1;
        } else {
            mday -= 31 * 5;
            month = Math.floor(mday / 30) + 7;
            day = (mday % 30) + 1;
        }
    }

    return new Array(year, month, day);
}

/*  updateFromGregorian  --  Update all calendars from Gregorian.
                             "Why not Julian date?" you ask.  Because
                             starting from Gregorian guarantees we're
                             already snapped to an integral second, so
                             we don't get roundoff errors in other
                             calendars.  */

function updateFromGregorian(year, mon, mday)
{
    var j, hour, min, sec,
        weekday, julcal, hebcal, islcal, hmindex, utime, isoweek,
        may_countcal, mayhaabcal, maytzolkincal, bahcal, frrcal,
        indcal, isoday, xgregcal;

    hour = min = sec = 0;
    //  Update Julian day

    j = gregorian_to_jd(year, mon + 1, mday) +
           ((sec + 60 * (min + 60 * hour)) / 86400.0);

    julianday_day = j;
//    document.modifiedjulianday.day.value = j - JMJD;

    //  Update day of week in Gregorian box

    weekday = jwday(j);
    gregorian_wday = Weekdays[weekday];

    //  Update leap year status in Gregorian box

    gregorian_leap = NormLeap[leap_gregorian(year) ? 1 : 0];

    //  Update Julian Calendar

    julcal = jd_to_julian(j);
//    document.juliancalendar.year.value = julcal[0];
//    document.juliancalendar.month.selectedIndex = julcal[1] - 1;
//    document.juliancalendar.day.value = julcal[2];
//    document.juliancalendar.leap.value = NormLeap[leap_julian(julcal[0]) ? 1 : 0];


    //  Update Islamic Calendar

    islcal = jd_to_islamic(j);
    islamic_year = islcal[0];
    islamic_month = islcal[1] - 1;
    islamic_wmonth = ISLAMIC_MONTHS[islamic_month];
    islamic_day = islcal[2];
    islamic_wday = "yawm " + ISLAMIC_WEEKDAYS[weekday];
    islamic_leap = NormLeap[leap_islamic(islcal[0]) ? 1 : 0];

    //  Update Persian Calendar

    perscal = jd_to_persian(j);
    persian_year = perscal[0];
    persian_month = perscal[1] - 1;
    persian_wmonth = PERSIAN_MONTHS[persian_month];
    persian_day = perscal[2];
    persian_wday = PERSIAN_WEEKDAYS[weekday];
    persian_leap = NormLeap[leap_persian(perscal[0]) ? 1 : 0];

}

//  calcGregorian  --  Perform calculation starting with a Gregorian date

function calcGregorian(year, mon, mday)
{
    updateFromGregorian(year, mon, mday);
}

//  calcJulian  --  Perform calculation starting with a Julian date

function calcJulian(j)
{
    var date, time;

    date = jd_to_gregorian(j);
    time = jhms(j);
    gregorian_year = date[0];
    gregorian_month = date[1] - 1;
    gregorian_day = date[2];
    gregorian_hour = pad(time[0], 2, " ");
    gregorian_min = pad(time[1], 2, "0");
    gregorian_sec = pad(time[2], 2, "0");
    updateFromGregorian(gregorian_year, gregorian_month, gregorian_day);
}

//  setJulian  --  Set Julian date and update all calendars

function setJulian(j)
{
    julianday_day = new Number(j);
    calcJulian(julianday_day);
}


//  calcIslamic  --  Update from Islamic calendar

function calcIslamic(year, month, day)
{
    setJulian(islamic_to_jd(year, month, day));
}

//  calcPersian  --  Update from Persian calendar

function calcPersian(year, month, day)
{
    setJulian(persian_to_jd(year, month, day));
}

// formatted Islamic Output
function cnvToIslamicOLD(year, month, day)
{
	updateFromGregorian(year, month, day);
	return (islamic_day + ' ' + islamic_wmonth + ' ' + islamic_year + ' (' + islamic_wday + ')');
}

function toIslamic(year, mon, mday)
{
	var j, hour, min, sec,
	weekday, julcal, hebcal, islcal;

    hour = min = sec = 0;
    //  Update Julian day
    j = gregorian_to_jd(year, mon + 1, mday) +
           ((sec + 60 * (min + 60 * hour)) / 86400.0);

    //  Update day of week in Gregorian box
    weekday = jwday(j);
    //  Update Islamic Calendar

    islcal = jd_to_islamic(j);

	i_year = islcal[0];
    i_month = islcal[1] - 1;
    i_wmonth = ISLAMIC_MONTHS[i_month];
    i_wmonth_p = ISLAMIC_MONTHS_P[i_month];
    i_day = islcal[2];
    i_wday = "yawm " + ISLAMIC_WEEKDAYS[weekday];
    i_leap = NormLeap[leap_islamic(islcal[0]) ? 1 : 0];

	return new Array(i_year, i_month, i_wmonth, i_day, i_wday, i_leap, i_wmonth_p);
}

// formatted Islamic Output
function cnvToIslamicAll(year, month, day)
{

	islamic_v1 = toIslamic(year, month, day);

	var myDate = new Date();
	myDate.setFullYear(year,month,day);
	nextDate = myDate.getDate() + 1;
	myDate.setDate(nextDate);
	islamic_v2 = toIslamic(myDate.getFullYear(), myDate.getMonth(), myDate.getDate());
	nextnextDate = myDate.getDate() + 1;
	myDate.setDate(nextnextDate);
	islamic_v3 = toIslamic(myDate.getFullYear(), myDate.getMonth(), myDate.getDate());
    return new Array(islamic_v1[0], islamic_v1[1], islamic_v1[2], islamic_v1[3], islamic_v1[4], islamic_v1[5],
					 islamic_v2[0], islamic_v2[1], islamic_v2[2], islamic_v2[3], islamic_v2[4], islamic_v2[5],
					 islamic_v3[0], islamic_v3[1], islamic_v3[2], islamic_v3[3], islamic_v3[4], islamic_v3[5]);
}

// formatted Persian Output
function cnvToPersian(year, month, day)
{
	updateFromGregorian(year, month, day);
//	return (persian_day + ' ' + persian_wmonth + ' ' + persian_year + ' (' + persian_wday + ')');
	return (persian_day + ' ' + persian_wmonth + ' ' + persian_year );
}

function cnvToPersian_P(year, month, day)
{
	updateFromGregorian(year, month, day);
	return (persian_day + ' ' + PERSIAN_MONTHS_P[persian_month] + ' ' + persian_year );
}

// formatted Islamic Output
function cnvToIslamic(year, month, day)
{
	iscal = cnvToIslamicAll(year, month, day);
//	return (iscal[9] + ' ' + iscal[8] + ' ' +  iscal[0] + ' (' + iscal[4] + ')');
//	return (iscal[3] + ' ' + iscal[2] + ' ' +  iscal[0] + ' (' + iscal[4] + ')');
	return (iscal[3] + ' ' + iscal[2] + ' ' +  iscal[0] );
}

function cnvToIslamic_P(year, month, day)
{
	islamic = toIslamic(year, month, day);
	return (islamic[3] + ' ' + islamic[6] + ' ' +  islamic[0] );
}

var monthNames = new Array(
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
        );

var monthNames_P = new Array(
        'ژانویه',
        'فوریه',
        'مارس',
        'آوریل',
        'مه',
        'ژوئن',
        'ژوئیه',
        'آگوست',
        'سپتامبر',
        'اكتبر',
        'نوامبر',
        'دسامبر'
        );

var dayNames = new Array(
        'Sunday',
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday'
        );


function formatDate( year, month, day )
{
    return tdy.getDate() + " " + monthNames[tdy.getMonth()] + " " + tdy.getFullYear();  
}

function formatDate_P( year, month, day )
{
    return tdy.getDate() + " " + monthNames_P[tdy.getMonth()] + " " + tdy.getFullYear();  
}

function persianDay( day )
{
    return PERSIAN_WEEKDAYS_P[ day ];
}

function englishDay( day )
{
    return dayNames[ day ];
}


