﻿/*
 *  Copyright Outpost Technology Frontier Corp.
 *
 */



/**
 * Dropdown Calendar
 * @requires Date Extension
 * @requires HtmlExtension Class
 */
DropdownCalendar = function() {
    //  Private Properties
    var dateObj = new Date();
    dateObj.clearTime();
    //  Privileged Properties
    this.minDate = new Date(1900, 1, 1, 0, 0, 0);
    this.maxDate = new Date(2050, 12, 31, 0, 0, 0);
    //  HTML Objects
    this.parentDiv = null;
    this.ddMonth = null;
    this.optMonths = {};
    this.ddDate = null;
    this.optDates = {};
    this.ddYear = null;
    this.optYears = {};
    //  Event Handlers
    this.onDateChanged = null;
    //----------------------------------------
    //  Getter / Setter (Privileged Methods)
    //----------------------------------------
    /**
     * Returns the current date object used by the calendar
     * @return {Date} date object used by the calendar
     */
    this.getDateObject = function () { return dateObj; };
    /**
     * Returns the current date of the month
     * @return {number} Date of the month
     */
    this.getDate = function () { return dateObj.getDate(); };
    /**
     * Sets a new date for the calendar. If a number is provided, then only the date value is set, if a date object is provided, then the current date object is replaced
     * @param date can either be a number or a date object
     */
    this.setDate = function (date) {
        if ((typeof date) == 'object') {
            //  remove time data
            date.clearTime();
            if (!dateObj.equalsToDateOnly(date)) {
                //  ensure that it does not go over or less the max & min dates
                var chkDate = Date.ensureDateInRange(date, this.minDate, this.maxDate);
                if (!date.equalsToDateOnly(chkDate))
                    this.ddDate.value = dateObj.getDate();
                //  if curDate has changed, reflect it to the calendar
                else if (!dateObj.equalsToDateOnly(chkDate)) {
                    dateObj = new Date(chkDate.getFullYear(), chkDate.getMonth(), chkDate.getDate());
                    
                    if (this.isInitialized()) {
                        this.updateStyles();
                        this.selectCurrentDate();

                        if (this.onDateChanged)
                            this.onDateChanged(dateObj);
                    }
                }
            }
        }
        else {
            if (dateObj.getDate() != date) {
                //  try setting the year, specifying to disallow overflows
                var curDate = Date.setDate(dateObj, date, false);
                //  ensure that it does not go over or less the max & min dates
                var chkDate = Date.ensureDateInRange(curDate, this.minDate, this.maxDate);
                
                if (!chkDate.equalsToDateOnly(curDate))
                    this.ddDate.value = dateObj.getDate();
                //  if curDate has changed, reflect it to the calendar
                else if (!dateObj.equalsToDateOnly(chkDate)) {
                    dateObj = new Date(chkDate.getFullYear(), chkDate.getMonth(), chkDate.getDate());

                    if (this.isInitialized()) {
                        this.updateStyles();
                        this.selectCurrentDate();

                        if (this.onDateChanged)
                            this.onDateChanged(dateObj);
                    }
                }
            }
        }
    };
    this.getMonth = function () { return dateObj.getMonth(); };
    this.setMonth = function (month) {
    
        if (this.getMonth() != month) {
            //  try setting the month, specifying to disallow overflow
            var curDate = Date.setMonth(dateObj, month, false);
            //  ensure that it does not go over or less of the max & min dates
            var chkDate = Date.ensureDateInRange(curDate, this.minDate, this.maxDate);
            //  if user tried to assign it to a previous month, revert it back
            if (!curDate.equalsToDateOnly(chkDate)) {
                dateObj.setMonth(chkDate.getMonth(), 1);
                this.ddMonth.value = chkDate.getMonth();
                this.setDate(chkDate.getDate());
            }
            //  if curDate has changed, reflect it to the calendar
            else if (!dateObj.equalsToDateOnly(chkDate)) {
                dateObj = new Date(chkDate.getFullYear(), chkDate.getMonth(), chkDate.getDate());

                if (this.isInitialized()) {
                    this.updateStyles();
                    this.selectCurrentDate();
                    
                    if (this.onDateChanged)
                        this.onDateChanged(dateObj);
                }
            }
        }
    };

    this.getFullYear = function () { return dateObj.getFullYear(); };
    this.setFullYear = function (year) {
        if (!this.getFullYear() != year) {
            //  try setting the year, specifying to disallow overflows
            var curDate = Date.setYear(dateObj, year, false);
            //  ensure that it does not go over or less the max & min dates
            curDate = Date.ensureDateInRange(curDate, this.minDate, this.maxDate);
            //  if curDate has changed, reflect it to the calendar
            if (!dateObj.equalsToDateOnly(curDate)) {
                dateObj = new Date(curDate.getFullYear(), curDate.getMonth(), curDate.getDate());
            
                if (this.isInitialized()) {
                    this.updateStyles();
                    this.selectCurrentDate();
                    
                    if (this.onDateChanged)
                        this.onDateChanged(dateObj);
                }
            }
        }
    };
    
    //  methods
    this.print = function (str) { return dateObj.print(str); };
    this.isInitialized = function () { return (this.parentDiv)? true : false; };
};

/**
 * Creates the dropdown calendar in the specified Html Element
 * @param {HTMLElement} parent element where the dropdown calendar will be attached
 */
DropdownCalendar.prototype.create = function(parent) {
    //  create the parent div where everything will be attached
    this.parentDiv = HtmlUtility.createElement("div", parent);
    var option;
    
    //  attach the current object to the parent div
    this.parentDiv.ddcalendar = this;
    
    //  instantiate months
    this.ddMonth = HtmlUtility.createElement("select", this.parentDiv);
    this.optMonths = new Array(12);
    this.ddMonth.optionList = this.optMonths;
    for (var i = 0; i < 12; i++) {
        this.optMonths[i] = HtmlUtility.createElement("option", this.ddMonth);
        this.optMonths[i].text = Date._MONTH_SHORTNAMES[i];
        this.optMonths[i].value = i;
        this.optMonths[i].enabled = true;
    }
    
    
    //  instantiate dates
    this.ddDate = HtmlUtility.createElement("select", this.parentDiv);
    this.optDates = new Array(31);
    this.ddDate.optionList = this.optDates;
    for (var i = 0; i < 31; i++) {
        this.optDates[i] = HtmlUtility.createElement("option", this.ddDate);
        this.optDates[i].text = (i+1);
        this.optDates[i].value = (i+1);
        this.optDates[i].enabled = true;
    }   
    
    //  instantiate year dropdown
    this.ddYear = HtmlUtility.createElement("select", this.parentDiv);
    this.updateYearList();
    
    //  attach events
    HtmlUtility.addEvent(this.ddMonth, "change", DropdownCalendar.onMonthChanged);
    HtmlUtility.addEvent(this.ddDate, "change", DropdownCalendar.onDateChanged);
    HtmlUtility.addEvent(this.ddYear, "change", DropdownCalendar.onYearChanged);
    
    //  select current date
    this.selectCurrentDate();
    //  update ui
    this.updateStyles();
};

DropdownCalendar.prototype.updateStyles = function () {
    //  make sure this object has already been initialized
    if (!this.isInitialized())
        return;
    
    HtmlUtility.addClass(this.ddMonth, "dropdownform01");
    HtmlUtility.addClass(this.ddDate, "dropdownform01");
    HtmlUtility.addClass(this.ddYear, "dropdownform01");
    
    var dateObj = this.getDateObject();
    
    //  get min/max month & year
    var isMaxYear = (this.maxDate.getFullYear() == dateObj.getFullYear());
    var isMinYear = (this.minDate.getFullYear() == dateObj.getFullYear());
    var maxMonth = this.maxDate.getMonth();
    var minMonth = this.minDate.getMonth();
    
    var isDisabled;
    for (var i = 12; --i >= 0;) {
        isDisabled = false;
        //  check if month has exceeded max month
        if (isMaxYear && i > maxMonth)
            isDisabled = true;
        //  check if month has fallen behind the min month
        else if (isMinYear && i < minMonth)
            isDisabled = true;
        
        if (!HtmlUtility.is_ie && !HtmlUtility.is_safari)
            this.optMonths[i].style.display = (isDisabled)? "none" : "";
        else if (isDisabled)
            DropdownCalendar.disableOption(this.optMonths[i], this.ddMonth);
        else
            DropdownCalendar.enableOption(this.optMonths[i], this.ddMonth);
    }
    
    var maxMonthDate = dateObj.getMonthMaxDate(dateObj.getMonth());
    for (var i = 31; --i >= 0;) {
        isDisabled = false;
        if ((i+1) > maxMonthDate)
            isDisabled = true;
        else if (isMaxYear && maxMonth == dateObj.getMonth() && (i+1) > this.maxDate.getDate())
            isDisabled = true;
        else if (isMinYear && minMonth == dateObj.getMonth() && (i+1) < this.minDate.getDate())
            isDisabled = true;
        
        if (!HtmlUtility.is_ie && !HtmlUtility.is_safari)
            this.optDates[i].style.display = (isDisabled)? "none" : "";
        else if (isDisabled)
            DropdownCalendar.disableOption(this.optDates[i], this.ddDate);
        else
            DropdownCalendar.enableOption(this.optDates[i], this.ddDate);
    }
};

DropdownCalendar.prototype.updateYearList = function () {
    var minYear = this.minDate.getFullYear();
    var maxYear = this.maxDate.getFullYear();
    
    var yearCount = (maxYear - minYear + 1);

    if (this.optYears.length > 0 && 
        this.optYears[0] == minYear && 
        this.optYears[this.optYears.length - 1] == maxYear)
        return;
    
    //  remove the old options
    HtmlUtility.removeAllElements(this.ddYear);

    //  reallocate the array, if needed
    if (yearCount != this.optYears.length)
        this.optYears = new Array(yearCount);
  
    var j = 0;
    for (var i = minYear; i <= maxYear; i++, j++) {
        this.optYears[j] = HtmlUtility.createElement("option", this.ddYear);
        this.optYears[j].text = i;
        this.optYears[j].value = i;
    }
};

DropdownCalendar.prototype.updateMaxDate = function () {
    var maxDate = this.date.getMonthMaxDate(this.date.getMonth());

    if (!HtmlUtility.is_ie && !HtmlUtility.is_safari) {
        this.ddDate.options[28].style.display = (maxDate < 29)? "none" : "";
        this.ddDate.options[29].style.display = (maxDate < 30)? "none" : "";
        this.ddDate.options[30].style.display = (maxDate < 31)? "none" : "";
    }
    else {
        if (maxDate < 29)
            DropdownCalendar.disableOption(this.ddDate.options[28], this.ddDate);
        else
            DropdownCalendar.enableOption(this.ddDate.options[28], this.ddDate);

        if (maxDate < 30)
            DropdownCalendar.disableOption(this.ddDate.options[29], this.ddDate);
        else
            DropdownCalendar.enableOption(this.ddDate.options[29], this.ddDate);

        if (maxDate < 31)
            DropdownCalendar.disableOption(this.ddDate.options[30], this.ddDate);
        else
            DropdownCalendar.enableOption(this.ddDate.options[30], this.ddDate);
    }
};

DropdownCalendar.prototype.selectCurrentDate = function () {
    //  make sure this object has already been initialized
    if (!this.isInitialized())
        return;
    
    var dateObj = this.getDateObject();
    //  select dates
    
    //  month
    var tmp = dateObj.getMonth();
    for (var index = this.ddMonth.options.length; --index >= 0;) {
        if (this.ddMonth.options[index].value == tmp) {
            this.ddMonth.selectedIndex = index;
            break;
        }
    }
    
    //  date
    tmp = dateObj.getDate();
    for (var index = this.ddDate.options.length; --index >= 0;) {
        if (this.ddDate.options[index].value == tmp) {
            this.ddDate.selectedIndex = index;
            break;
        }
    }
    
    //  year
    tmp = dateObj.getFullYear();
    for (var index = this.ddYear.options.length; --index >= 0;) {
        if (this.ddYear.options[index].value == tmp) {
            this.ddYear.selectedIndex = index;
            break;
        }
    }
};

/**
 * Retrieves the parent div where this calendar object is attached to
 * @returns {object} HTML Div Element
 */
DropdownCalendar.getParentDiv = function(evt) {
	var f = HtmlUtility.is_ie ? window.event.srcElement : evt.currentTarget;
	while (!(/^div$/i.test(f.tagName)))
		f = f.parentNode;
	return f;
};

DropdownCalendar.enableOption = function (option, select) {

    if (!option || option.enabled)
        return;

    if (!select || !select.optionList)
        return;

    var val;
    var listIndex = DropdownCalendar.findOption(select.optionList, option.value);
    if (listIndex != -1) {
        val = parseInt(select.optionList[listIndex].value);
        var len = select.options.length;
        for (var i = 0; i < len; i++) {
            if (select.options[i].value > val) {
                select.insertBefore(select.optionList[listIndex], select.options[i]);
                select.optionList[listIndex].enabled = true;
                break;
            }
        }
        if (!option.enabled) {
            select.appendChild(select.optionList[listIndex]);
            select.optionList[listIndex].enabled = true;
        }
    }
}

DropdownCalendar.disableOption = function(option, select) {   
    if (!option || !option.enabled)
        return;
    
    if (!select || !select.optionList)
        return;
    
    //  search for the option being referred to
    var activeOptionIndex = DropdownCalendar.findOption(select.options, option.value);
    var listIndex = DropdownCalendar.findOption(select.optionList, option.value);
    if (activeOptionIndex != -1 && listIndex != -1) {
        select.optionList[listIndex] = select.options[activeOptionIndex].cloneNode(true);
        select.optionList[listIndex].innerHTML = select.options[activeOptionIndex].innerHTML;
        select.optionList[listIndex].enabled = false;
        select.remove(activeOptionIndex);
    }
}

DropdownCalendar.findOption = function (options, value) {
    if (!options)
        return -1;

    for (var index = options.length; --index >= 0;) {
        if (options[index].value == value)
            return index;
    }
    return -1;
}


//-------------------------
//  EVENT HANDLERS
//-------------------------

DropdownCalendar.onDateChanged = function (evt) {
    //  retrieve current selected date
    var src = HtmlUtility.getEventSource(evt);
    var option = src.options[src.selectedIndex];
    var date = option.value;
    //  retrieve dropdown calendar object
    var el = DropdownCalendar.getParentDiv(evt);
    var ddcalendar = el.ddcalendar;
    
    //  set date    
    ddcalendar.setDate(date);
};

DropdownCalendar.onMonthChanged = function (evt) {
    //  retrieve current selected month
    var src = HtmlUtility.getEventSource(evt);
    var option = src.options[src.selectedIndex];
    var month = option.value;
    //  retrieve dropdown calendar object
    var el = DropdownCalendar.getParentDiv(evt);
    var ddcalendar = el.ddcalendar;
       
    //  set month
    ddcalendar.setMonth(month);
};

DropdownCalendar.onYearChanged = function (evt) {   
    //  retrieve current selected year
    var src = HtmlUtility.getEventSource(evt);
    var option = src.options[src.selectedIndex];
    var year = option.value;
    //  retrieve dropdown calendar object
    var el = DropdownCalendar.getParentDiv(evt);
    var ddcalendar = el.ddcalendar;
    
    //  set year
    ddcalendar.setFullYear(year);
};