/*
       Yarzheit functions by 7Bear Software

        Programmed by Shimon Shore
        
                http://www.7Bear.com/
    
    Copyright 2010 by 7Bear Software - All Rights Reserved.
       
*/

/*       
       JavaScript functions for the Fourmilab Calendar Converter
                  by John Walker  --  September, MIM
*/

/*  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 whey 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 debug;

function calcYarzheit()
{   
    var niftardate, niftardatelist, niftar_jd, sunset;

    niftardate = document.yarzheit.niftardate.value;
    
    //Get Julian Date
    niftardatelist = new Array();
    niftardatelist = niftardate.split("/", 3);
    
    if (niftardate.length < 8)
    {
        document.getElementById('YarzheitString').innerHTML = "<br/>The date is not correct. Please enter in format mm/dd/yyyy.";
        return 0;
    } 

    //Check Sundown combo box 
    sunset = parseInt(document.yarzheit.sunset.selectedIndex);
    if (sunset == 0)
    {
        document.getElementById('YarzheitString').innerHTML = "<br/>Please specify if your loved one pass away before sunset or after sunset.";
        return 0;
    } 
        
    var nDay;
    nDay = parseInt(remove_leading_zero(niftardatelist[1]));
    if (nDay < 1 || nDay > 31)
    {   
       document.getElementById('YarzheitString').innerHTML = "<br/>Please recheck the date you entered. The day does not appear to be correct (" + nDay + ").";
       return 0;
    }
    
    var nMonth;
    nMonth = parseInt(remove_leading_zero(niftardatelist[0]));
    if (nMonth < 1 || nMonth > 12)
    {   
       document.getElementById('YarzheitString').innerHTML = "<br/>Please recheck the date you entered. The month does not appear to be correct (" + nMonth + ").";
       return 0;
    }
    
    var nYear;
    nYear = parseInt(remove_leading_zero(niftardatelist[2]));
    if (nYear < 1000 || nYear > 2100)
    {   
       document.getElementById('YarzheitString').innerHTML = "<br/>Please recheck the date you entered. The year does not appear to be correct (" + nYear + ").";
       return 0;
    }
            
    niftar_jd =
        gregorian_to_jd(nYear, nMonth, nDay);
        
    debug = "Julian Date: " + niftar_jd + "<br/>";
           
    //Add a day for after Sunset
    if (sunset == 2)
    {
        ++niftar_jd;
    }
  
    //Get Jewish Date
    var niftardatehebrew = new Array();
    niftardatehebrew = jd_to_hebrew(niftar_jd);
    
    debug += " Hebrew Niftar Date: " + niftardatehebrew[0] + "/" + niftardatehebrew[1] + "/" + niftardatehebrew[2] + "<br/>";    
    
    //adjust yarzheit for months that change how many days they have
    niftardatehebrew = adjust_yarzheit_date(niftardatehebrew[0], niftardatehebrew[1], niftardatehebrew[2]);

    debug += " Adjusted Hebrew Niftar Date for Variable Months: " + niftardatehebrew[0] + "/" + niftardatehebrew[1] + "/" + niftardatehebrew[2] + "<br/>";    
    
    //Get Current Hebrew Year
    var today_hebrew = new Array();
    today_hebrew = getTodayHebrew();
    debug += " Current Hebrew Date: " + today_hebrew[0] + "/" + today_hebrew[1] + "/" + today_hebrew[2] + "<br/>";    
    
    //Get Yarzheit Current Hebrew Year
    var y_current_year = new Array();
    y_current_year = yarzheit_current_year(niftardatehebrew[0], niftardatehebrew[1], niftardatehebrew[2], today_hebrew[0]);
    debug += " Yarzheit based on Current Hebrew: " + y_current_year[0] + "/" + y_current_year[1] + "/" + y_current_year[2] + "<br/>";    

    //Get Extra Hebrew Yarzheit when user does not know if death was before or after sunset
    var bExtraYarzheit;
    var niftardatehebrew_extra = new Array();
    var y_current_year_extra = new Array();
    bExtraYarzheit = (sunset == 3);
    if (bExtraYarzheit)
    {
    	niftardatehebrew_extra = jd_to_hebrew(niftar_jd + 1);
	debug += " Extra Hebrew Niftar Date: " + niftardatehebrew_extra[0] + "/" + niftardatehebrew_extra[1] + "/" + niftardatehebrew_extra[2] + "<br/>"; 
	
	niftardatehebrew_extra = adjust_yarzheit_date(niftardatehebrew_extra[0], niftardatehebrew_extra[1], niftardatehebrew_extra[2]);
	debug += " Extra Adjusted Hebrew Niftar Date for Variable Months: " + niftardatehebrew_extra[0] + "/" + niftardatehebrew_extra[1] + "/" + niftardatehebrew_extra[2] + "<br/>";    

        y_current_year_extra = yarzheit_current_year(niftardatehebrew_extra[0], niftardatehebrew_extra[1], niftardatehebrew_extra[2], today_hebrew[0]);
        debug += " Extra Yarzheit based on Current Hebrew: " + y_current_year_extra[0] + "/" + y_current_year_extra[1] + "/" + y_current_year_extra[2] + "<br/>";    
    }
    
    //check if Yarzheit has passed
    if ((heb_month_passed(y_current_year[1], today_hebrew[1]))
        || (y_current_year[1] == today_hebrew[1] && y_current_year[2] < today_hebrew[2]))
    {   
        y_current_year = yarzheit_current_year(niftardatehebrew[0], niftardatehebrew[1], niftardatehebrew[2], today_hebrew[0]+1);
        debug += "Yarzheit Passed in Current Year<br/>";    
        debug += "Yarzheit in Next Hebrew Year: " + y_current_year[0] + "/" + y_current_year[1] + "/" + y_current_year[2] + "<br/>";    

	if (bExtraYarzheit)
	{
            y_current_year_extra = yarzheit_current_year(niftardatehebrew_extra[0], niftardatehebrew_extra[1], niftardatehebrew_extra[2], today_hebrew[0]+1);
	    debug += "Extra Yarzheit in Next Hebrew Year: " + y_current_year_extra[0] + "/" + y_current_year_extra[1] + "/" + y_current_year_extra[2] + "<br/>";    
	}
    }
    
    //Convert yarzheit to Julian  
    var jd_yarzheit = hebrew_to_jd(y_current_year[0], y_current_year[1], y_current_year[2]);
    debug += " Julian Date: " + jd_yarzheit + "<br/>";   
    
    //Convert Julian to Gregorian
    var gregdate = new Array();
    gregdate = jd_to_gregorian(jd_yarzheit); 
    debug += " Gregorian Date: " + gregdate[0] + "/" + gregdate[1] + "/" + gregdate[2] + "<br/>";
    
    var weekday;
    weekday = jwday(jd_yarzheit);
    debug += " Day of Week: " + weekday + "<br/>";
            
    var sFmtYDate;
    sFmtYDate = FormatGregDate(gregdate[1], gregdate[2], gregdate[0]);
    debug += " Formatted Gregorian Date: " + sFmtYDate + "<br/>";

    //Add an extra day if sundown/sunset unknown
    var jd_extra_yarzheit, extra_gregdate, extra_weekday, sFmt_Extra_YDate;
    if (bExtraYarzheit)
    {
        jd_extra_yarzheit = hebrew_to_jd(y_current_year_extra[0], y_current_year_extra[1], y_current_year_extra[2]);
	debug += " Extra Julian Date: " + jd_extra_yarzheit + "<br/>";   

        extra_gregdate = new Array();
        extra_gregdate = jd_to_gregorian(jd_extra_yarzheit); 
    	debug += " Extra Gregorian Date: " + extra_gregdate[0] + "/" + extra_gregdate[1] + "/" + extra_gregdate[2] + "<br/>";

        extra_weekday = jwday(jd_extra_yarzheit);
    	debug += " Extra Day of Week: " + extra_weekday + "<br/>";
    
        sFmt_Extra_YDate = FormatGregDate(extra_gregdate[1], extra_gregdate[2], extra_gregdate[0]);
    	debug += " Extra Formatted Gregorian Date: " + sFmt_Extra_YDate + "<br/>";
    }

    var bDoubleYarzheit, jd_yarzheit2, gregdate2, weekday2, sFmtYDate2;
    var bDoubleYarzheit_extra, jd_yarzheit2_extra, gregdate2_extra, weekday2_extra, sFmtYDate2_extra;
    bDoubleYarzheit = (y_current_year.length > 3);
    if (bDoubleYarzheit)
    {
        jd_yarzheit2 = hebrew_to_jd(y_current_year[3], y_current_year[4], y_current_year[5]);
        gregdate2 = new Array();
        gregdate2 = jd_to_gregorian(jd_yarzheit2); 
        weekday2 = jwday(jd_yarzheit2);
        sFmtYDate2 = FormatGregDate(gregdate2[1], gregdate2[2], gregdate2[0]);
	if (bExtraYarzheit)
	{
		jd_yarzheit2_extra = hebrew_to_jd(y_current_year_extra[3], y_current_year_extra[4], y_current_year_extra[5]);
		gregdate2_extra = new Array();
		gregdate2_extra = jd_to_gregorian(jd_yarzheit2_extra); 
		weekday2_extra = jwday(jd_yarzheit2_extra);
		sFmtYDate2_extra = FormatGregDate(gregdate2_extra[1], gregdate2_extra[2], gregdate2_extra[0]);
	}  
    }
    var elementslist = document.getElementsByName("debug");
    if (elementslist.length > 0)
    {
        document.yarzheit.debug.value = debug;
    }    
    
    var sOutputStr;
    if (bExtraYarzheit)
    {
    	sOutputStr = "<p>When you don't know whether the time of passing is before sundown or after sundown, all of the following days should be observed as the Yahrzeit for your loved one:</p>";
    }
    else
    {
    	sOutputStr = "<p>The Yahrzeit for your loved one is on:</p>";
    }
    
    sOutputStr += "<p align=center><font color=\"#009000\"><b>";
    if (bDoubleYarzheit)
    {
        sOutputStr += Weekdays[weekday2] + " " + sFmtYDate2;
        sOutputStr += "</b></font>";
	if (bExtraYarzheit)
	{
		sOutputStr += " and <font color=\"#009000\"><b>";
		sOutputStr += Weekdays[weekday2_extra] + " " + sFmtYDate2_extra;
		sOutputStr += "</b></font>";
	}
        sOutputStr += "<br/><font color=\"#009000\"><b>";
    }
    sOutputStr += Weekdays[weekday] + " " + sFmtYDate;
    if (bExtraYarzheit)
    {
    	sOutputStr += "</b></font> and <font color=\"#009000\"><b>"
	sOutputStr += Weekdays[extra_weekday] + " " + sFmt_Extra_YDate;
    }
    sOutputStr += "</b></font></p> <p>The Hebrew date of the Yahrzeit is:</p><p align=center><font color=\"#009000\"><b>";
    if (bDoubleYarzheit)
    {
        sOutputStr += FormatHebDate(y_current_year[4], y_current_year[5], y_current_year[3]);
        sOutputStr += "</b></font>";
	if (bExtraYarzheit)
	{
		sOutputStr += " and <font color=\"#009000\"><b>";
		sOutputStr += FormatHebDate(y_current_year_extra[4], y_current_year_extra[5], y_current_year_extra[3]);
		sOutputStr += "</b></font>";
	}
        sOutputStr += "<br/><font color=\"#009000\"><b>";
    }
    sOutputStr += FormatHebDate(y_current_year[1], y_current_year[2], y_current_year[0]);   
    if (bExtraYarzheit)
    {
    	sOutputStr += "</b></font> and <font color=\"#009000\"><b>"
	sOutputStr += FormatHebDate(y_current_year_extra[1], y_current_year_extra[2], y_current_year_extra[0]);
    }
    sOutputStr += "</b></font></p> ";
    
    sOutputStr += "<p>The Yahrzeit begins at nightime (i.e. when you see three stars), on the evening preceding the Yahrzeit, and ends the next evening.</p>";
    sOutputStr += "<p>If a Yahrzeit falls on Saturday then it begins 18 minutes before Sunset on the preceding Friday through when the first 3 stars come out on Saturday night.</p>";
    /*if (bDoubleYarzheit)
    {
        if ((weekday == 6) || (weekday2 == 6))
        {
            sOutputStr += "The Yahrzeit on Saturday begins 18 minutes before Sunset (on that Friday) through when the first 3 stars come out on Saturday night. ";
            sOutputStr += "The other Yahrzeit begins at nightime (i.e. when you see three stars), on the evening preceding the Yahrzeit, and ends the next evening.";
        }
        else
        {
            sOutputStr += "The Yahrzeit begins at nightime (i.e. when you see three stars), on the evening preceding the Yahrzeit, and ends the next evening.";
        }
    }
    else
    {
        if (weekday == 6)
        {
            sOutputStr += "The Yahrzeit on Saturday begins 18 minutes before Sunset (on that Friday) through when the first 3 stars come out on Saturday night.";
        }
        else
        {
            sOutputStr += "The Yahrzeit begins at nightime (i.e. when you see three stars), on the evening preceding the Yahrzeit, and ends the next evening.";
        }
    }*/
    
    //sOutputStr += "<p>" + debug + "</p>";
    
    document.getElementById('YarzheitString').innerHTML = sOutputStr;
    
    return 0;
}

function remove_leading_zero(str)
{
    return str.replace(/^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);
}

//  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)));
}

/*  JD_TO_HEBREW  --  Convert Julian date to Hebrew date
                      This works by making multiple calls to
                      the inverse function, and is this very
                      slow.  */

function jd_to_hebrew(jd)
{
    var year, month, day, i, count, first;

    jd = Math.floor(jd) + 0.5;
    count = Math.floor(((jd - HEBREW_EPOCH) * 98496.0) / 35975351.0);
    year = count - 1;
    for (i = count; jd >= hebrew_to_jd(i, 7, 1); i++) {
        year++;
    }
    first = (jd < hebrew_to_jd(year, 1, 1)) ? 7 : 1;
    month = first;
    for (i = first; jd > hebrew_to_jd(year, i, hebrew_month_days(year, i)); i++) {
        month++;
    }
    day = (jd - hebrew_to_jd(year, month, 1)) + 1;
    return new Array(year, month, day);
}


//  HEBREW_TO_JD  --  Determine Julian day from Hebrew date

var HEBREW_EPOCH = 347995.5;

//  Is a given Hebrew year a leap year ?

function hebrew_leap(year)
{
    return mod(((year * 7) + 1), 19) < 7;
}

//  How many months are there in a Hebrew year (12 = normal, 13 = leap)

function hebrew_year_months(year)
{
    return hebrew_leap(year) ? 13 : 12;
}

//  Test for delay of start of new year and to avoid
//  Sunday, Wednesday, and Friday as start of the new year.

function hebrew_delay_1(year)
{
    var months, days, parts;

    months = Math.floor(((235 * year) - 234) / 19);
    parts = 12084 + (13753 * months);
    day = (months * 29) + Math.floor(parts / 25920);

    if (mod((3 * (day + 1)), 7) < 3) {
        day++;
    }
    return day;
}

//  Check for delay in start of new year due to length of adjacent years

function hebrew_delay_2(year)
{
    var last, present, next;

    last = hebrew_delay_1(year - 1);
    present = hebrew_delay_1(year);
    next = hebrew_delay_1(year + 1);

    return ((next - present) == 356) ? 2 :
                                     (((present - last) == 382) ? 1 : 0);
}

//  How many days are in a Hebrew year ?

function hebrew_year_days(year)
{
    return hebrew_to_jd(year + 1, 7, 1) - hebrew_to_jd(year, 7, 1);
}

//  How many days are in a given month of a given year

function hebrew_month_days(year, month)
{
    //  First of all, dispose of fixed-length 29 day months

    if (month == 2 || month == 4 || month == 6 ||
        month == 10 || month == 13) {
        return 29;
    }

    //  If it's not a leap year, Adar has 29 days

    if (month == 12 && !hebrew_leap(year)) {
        return 29;
    }

    //  If it's Heshvan, days depend on length of year

    if (month == 8 && !(mod(hebrew_year_days(year), 10) == 5)) {
        return 29;
    }

    //  Similarly, Kislev varies with the length of year

    if (month == 9 && (mod(hebrew_year_days(year), 10) == 3)) {
        return 29;
    }

    //  Nope, it's a 30 day month

    return 30;
}

//  Finally, wrap it all up into...

function hebrew_to_jd(year, month, day)
{
    var jd, mon, months;

    months = hebrew_year_months(year);
    jd = HEBREW_EPOCH + hebrew_delay_1(year) +
         hebrew_delay_2(year) + day + 1;

    if (month < 7) {
        for (mon = 7; mon <= months; mon++) {
            jd += hebrew_month_days(year, mon);
        }
        for (mon = 1; mon < month; mon++) {
            jd += hebrew_month_days(year, mon);
        }
    } else {
        for (mon = 7; mon < month; mon++) {
            jd += hebrew_month_days(year, mon);
        }
    }

    return jd;
}

//  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);
}

function getTodayHebrew()
{
    var today = new Date();

    /*  The following idiocy is due to bizarre incompatibilities
        in the behaviour of getYear() between Netscrape and
        Exploder.  The ideal solution is to use getFullYear(),
        which returns the actual year number, but that would
        break this code on versions of JavaScript prior to
        1.2.  So, for the moment we use the following code
        which works for all versions of JavaScript and browsers
        for all year numbers greater than 1000.  When we're willing
        to require JavaScript 1.2, this may be replaced by
        the single line:
            document.gregorian.year.value = today.getFullYear();
        Thanks to Larry Gilbert for pointing out this problem.
    */

    var y = today.getYear();
    if (y < 1000) 
    {
        y += 1900;
    }    
    
    var month, day
    month = today.getMonth() + 1;
    day = today.getDate() + 1;
    
    //allow override of today date
    var elementslist = document.getElementsByName("today");
    if (elementslist.length > 0)
    {
        var todaydate, todaydatelist;
        todaydate = document.yarzheit.today.value;
        if (todaydate.length > 0)
        {
            todaydatelist = new Array();
            todaydatelist = todaydate.split("/", 3);
            y = parseInt(todaydatelist[2]);    
            month = parseInt(todaydatelist[0]);
            day = parseInt(todaydatelist[1]);
        }
    }
            
    debug += " Today Gregorian Date: " + y + "/" + month + "/" + day + "<br/>";   
    
    var today_jd = gregorian_to_jd(y, month, day);
    
    debug += " Today Julian Date: " + today_jd + "<br/>";   
    
    //Get Jewish Date
    today_hebrew = new Array();
    today_hebrew = jd_to_hebrew(today_jd);
    
    return today_hebrew;
}

//Get Yarzheit Current Hebrew Year
function yarzheit_current_year(year, month, day, currentyear)
{
    var y_current_year = new Array();
    y_current_year[0] = currentyear;
    y_current_year[1] = month;
    y_current_year[2] = day;
    
    //leap years
    if (month == 12)
    {
        if (hebrew_leap(currentyear))
        {
            if (! hebrew_leap(year))
            {
                //make the later date the first one listed so
                //comparing to see if this list is over will be easy
                y_current_year[3] = currentyear;
                y_current_year[4] = 12;
                y_current_year[5] = day;
                
                y_current_year[1] = 13;
            }  
        }    
        else
        {
            if (day == 30)
            {
                y_current_year[1] = 11;
                y_current_year[2] = 30;
            }
        }
    }
    else if (month == 13)
    {
        if (! hebrew_leap(currentyear))
        {
            y_current_year[1] = 12;        
        }       
    }
    
    //Adjust for Cheshvan/Kislev only having 29 days
    if ((month == 8) || (month == 9))
    {
        if (day == 30)
        {
            var days_in_month;
            days_in_month = hebrew_month_days(currentyear, month);
            
            if (days_in_month == 29)
            {
                y_current_year[2] = 29;
            }        
        }
    }

    return y_current_year;
}

//Adjust Yarzheit date to deal with Kislev/Cheshvan that change from 29 to 30 days
function adjust_yarzheit_date(year, month, day)
{
    var adjust_yarzheit = new Array();
    adjust_yarzheit[0] = year;
    adjust_yarzheit[1] = month;
    adjust_yarzheit[2] = day;
    
    if ((month == 8) || (month == 9))
    {
        if (day == 30)
        {
            var days_in_month;
            days_in_month = hebrew_month_days(year + 1, month);
            
            if (days_in_month == 29)
            {
                adjust_yarzheit[2] = 1;
                adjust_yarzheit[1] = month + 1;
            }        
        }
    }

    return adjust_yarzheit;
}

function heb_month_passed(month, currentmonth)
{
    var normalized_month, current_normalized_month;
    
    if (month > 6)
    {
        normalized_month = month - 6;
    }
    else
    {
        normalized_month = month + 7;
    }

    if (currentmonth > 6)
    {
        current_normalized_month = currentmonth - 6;
    }
    else
    {
        current_normalized_month = currentmonth + 7;
    }

    debug += " Hebrew Month Passed: C-" + current_normalized_month + " Y-" + normalized_month + "<br/>";
    return (current_normalized_month >  normalized_month);
}

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

function FormatGregDate(month, day, year)
{
    var sDate;
    sDate = Months[month];    

    sDate += " " + day + ", " + year;
    
    return sDate;
}

var HebMonths = new Array( "", "Nissan", "Iyar", "Sivan", "Tamuz",
                          "Menachem Av", "Elul", "Tishri", "Cheshvan",
                          "Kislev", "Tevet", "Shevat", "Adar", "Adar II" );

function FormatHebDate(month, day, year)
{
    var sDate;
    sDate = day + " ";
    
    sDate += HebMonths[month];
    if (month == 12)
    {
        if (hebrew_leap(year))
        {
            sDate += " I";
        }
    }
    
    sDate +=  ", " + year;
    
    return sDate;
}


