﻿/**
 * @fileOverview This file is a source which be used to create a calendar
 * @author <a href="mailto:fogtower1979@gmail.com">Fog Tower</a>
 * @version 1.3
 */

/**
 * Construct a namespace.
 * @namespace JSCalender
 */
JSCalender = {

};

/**
 * Construct a new InlineCalender.
 * @class Represents a InlineCalender.
 * @constructor
 * @example 
 * var myCalender = new JSCalender.InlineCalender("containerId");
 * @param {String} targetId The id of the Calender's input element.
 * @param {String} onClickElementId The id of the Calender's input element.
 * @return A new instance of a Calender.
 */
JSCalender.InlineCalender = function(id, left, top){
   /**
    * The id of calender's parent node
    * @type String
    */
    this.id = id;
    
   /**
    * The left of calender
    * @type Number
    */
    this.left = left;
    
   /**
    * The top of calender
    * @type Number
    */
    this.top = top;
    
    this._initialize();
};

/**
* Show calendar.
* @public
*/
JSCalender.InlineCalender.prototype.show = function(){
    this._createCalendarContainer();
    if(document.inlineCalendar){
    	document.inlineCalendar = this;
    	this._addCalendarEvent();
    	return;
    }
    //add event after dom was created.
    this._addDocumentEvent();
    this._addCalendarEvent();
    this._addMonthComboboxEvent();
    this._addYearComboboxEvent();
    document.inlineCalendar = this;
};

/**
* Initialize calendar.
* @private
*/
JSCalender.InlineCalender.prototype._initialize = function(){
    /**
     * The id of calender's container
     * @default "fogtower-calendar-calendarcontainer"
     * @type String
     */
    this.calendarContainerId = this.id ? this.id + "-calendarcontainer" : "fogtower-calendar-calendarcontainer";

    /**
     * The config of calender
     * @type Object
     */
    this.config = Calender_Config;

    /**
     * The resource of calender
     * @type Object
     */
    this.resource = Calender_Resources[(this.config.language 
                    || (navigator.browserLanguage?navigator.browserLanguage:navigator.language)).toLowerCase()] || Calender_Resources["en"];
   
    /**
     * The first day in week
     * @example 
     * this.firstDayInWeek = 0;
     * The first day in week is Monday.
     * this.firstDayInWeek = 6;
     * The first day in week is Sunday.
     * @type Number
     */
    this.firstDayInWeek = this.config.firstDayInWeek;

    /**
     * The days' number in month
     * @type Array
     */
    this.monthDate = [31,28,31,30,31,30,31,31,30,31,30,31];

    /**
     * Today
     * @type Date
     */
    this.thisDay = new Date();

    /**
     * This year
     * @type Number
     */
    this.year = this.thisDay.getFullYear();

    /**
     * This month
     * @type Number
     */
    this.month = this.thisDay.getMonth();
    
};


/**
 * Get type of the browser.
 * @namespace The browser's type
 * @public
 */
JSCalender.InlineCalender.prototype.Browser = {
    /**
     * Browser is IE
     * @public
     * @type Boolean
     */
    IE : !!(window.attachEvent && !window.opera),

    /**
     * Browser is Firefox
     * @public
     * @type Boolean
     */
    FF : navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,

    /**
     * Browser is Opera
     * @public
     * @type Boolean
     */
    Opera : !!window.opera,

    /**
     * Browser is WebKit
     * @public
     * @type Boolean
     */
    WebKit : navigator.userAgent.indexOf('AppleWebKit/') > -1
};

/**
 * Create calendar's container.
 * @private
 */
JSCalender.InlineCalender.prototype._createCalendarContainer = function(){
    this.calendarContainer = document.getElementById(this.calendarContainerId);
    if(this.calendarContainer){
        this.calendarContainer.parentNode.removeChild(this.calendarContainer);
    }
    this.calendarContainer = document.createElement("div");
    this.calendarContainer.id = this.calendarContainerId;
    this.calendarContainer.className = this.config.css;
    this.calendarContainer.position = "relative";
    this.calendarContainer.onselectstart = function(){
        return false;
    };
    if(this.left){
    	this.calendarContainer.style.left = this.left + "px";
    }
    if(this.top){
    	this.calendarContainer.style.top = this.top + "px";
    }	
    var container = document.getElementById(this.id);
    if(container){
    	container.appendChild(this.calendarContainer);
    }
    else{
    	document.body.appendChild(this.calendarContainer);
    }
    this.calendarContainer.innerHTML = this._createCalendarPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
	this._checkSlidePanel(this._getDateString(this.thisDay.getFullYear(), this.thisDay.getMonth() + 1, this.thisDay.getDate()));
};

/**
 * Create calendar's panel.
 * @private
 * @returns {String} Create panel of calendar.
*/
JSCalender.InlineCalender.prototype._createCalendarPanel = function(){
    var str = "<table class='panel' cellspacing='0' cellpadding='0'><tr><td style='height:1px;'>"
            + "<div class='topleft'></div><div class='topright'></div><div class='monthcombobox' id='"
        	+ this.calendarContainerId
        	+ "-monthCombobox'>"
            + this._createMonthCombobox()
            + "</div><div class='yearcombobox' id='"
        	+ this.calendarContainerId
            + "-yearCombobox' >"
            + this._createYearCombobox()
            + "</div></td></tr><tr style='display:none;'><td>"
        	+ this._createCalendarSlide()
        	+ "</td></tr><tr><td>"
            + this._createCalendarTop()
            + "</td></tr><tr><td id='"
            + this.calendarContainerId
            + "-bodypanel'>"
            + this._createCalendarBody()
            + "</td></tr><tr><td style='height:10px;'>"
        	+ this._createCalendarInfoPanel()
        	+ "</td></tr><tr><td style='height:10px;'>"
            + this._createCalendarBottom()
            + "</td></tr></table>";
    return str; 
};


/**
 * Create calendar's slide panel.
 * @private
 * @returns {String} Create slide panel of calendar.
*/
JSCalender.InlineCalender.prototype._createCalendarSlide = function(){
	var str = "<table class='slidepanel' id='"
		    + this.calendarContainerId + "-slidePanel" 
			+ "'><tr><td></td><td class='slidepanel-text'></td></tr><tr><td class='slidepanel-text' colspan='2'></td></tr></table>";
	return str;
};

/**
 * Create calendar's top panel.
 * @private
 * @returns {String} Create top panel of calendar.
*/
JSCalender.InlineCalender.prototype._createCalendarTop = function(){
    var str = "<table class='toppanel'><tr>";
    var topPostionArray = this.config.topPanel;
    for(var i = 0, len = topPostionArray.length; i < len; i++){
        str += "<td onmouseover='javascript:this.className=\"toppanel-hover\";' "
            + " onmouseout='javascript:this.className=\"\";' ";
        if(topPostionArray[i].width){
            str +=" style='width:" + topPostionArray[i].width + ";' ";
        }
        str += " id='" + this.calendarContainerId + "-" + topPostionArray[i].id
            + "' title='" + (this.resource[topPostionArray[i].id] || "") + "' ";
        if(topPostionArray[i].backgroundImage){
            str += "style='backgroundImage : " + topPostionArray[i].backgroundImage + ";' ";
        }
        if(topPostionArray[i].width){
            str += "width='" + topPostionArray[i].width + "' ";
        }
        if(topPostionArray[i].text){
            str += ">" + topPostionArray[i].text;
        }
        else{
            str += ">";
        }
        str += "</td>";
    }
    return str + "</tr></table>";
};

/**
 * Create calendar's body panel.
 * @private
 * @returns {String} Create body panel of calendar.
*/
JSCalender.InlineCalender.prototype._createCalendarBody = function(){
    var str = "<table cellspacing='0' cellpadding='0' class='bodypanel'>"
            + this._createCalendarDays()
            + this._createCalendarDates()
            + "</table>";
    return str;
};

/**
 * Create calendar's days in body panel.
 * @private
 * @returns {String} Create days in body panel of calendar.
*/
JSCalender.InlineCalender.prototype._createCalendarDays = function(){
    var weekTitle = this.resource.weekTitle;
    var weekDays = this.resource.weekDays;
    var firstDayInWeek = this.firstDayInWeek;
    var arr = [];
    if(typeof firstDayInWeek != "number" || firstDayInWeek > 6 || firstDayInWeek < 0){
        firstDayInWeek = 6;
    }
    for(var i = 0; i < 7; i++){
        if(i >= firstDayInWeek){
            arr[i - firstDayInWeek] = weekDays[i];
        }
        else{
            arr[7- firstDayInWeek + i] = weekDays[i];
        }
    }
    var str = "<tr class='bodypanel-headrow'><td style='width:16%;' class='bodypanel-weekhead'>" + weekTitle + "</td>";
    for(var i = 0;i < 7; i++){
        str += "<td style='width:12%;' class='bodypanel-dayhead";
        if(arr[i] == weekDays[5] || arr[i] == weekDays[6]){
            str +=" bodypanel-weekendhead";
        }
        str += "'>" + arr[i] + "</td>";
    }
    str += "</tr>";
    return str;
};

/**
 * Create calendar's dates in body panel.
 * @private
 * @returns {String} Create dates in body panel of calendar.
*/
JSCalender.InlineCalender.prototype._createCalendarDates = function(){
    var firstDayInWeek = this.firstDayInWeek;
    var preMonthDate = this._getMonthDate((this.month == 0)?11:(this.month-1));
    var thisMonthDate = this._getMonthDate(this.month);
    var weekNumber = this._getYearWeek(new Date(this.year, this.month, 1));
    var _date = new Date(this.year, this.month, 1);
    var weekDay = _date.getDay() - 1 - firstDayInWeek;
    if(weekDay < 0){
        weekDay = weekDay + 7;
    }
    var str = "<tr><td class='bodypanel-week'>"
        + weekNumber++ 
        + "</td>";

    //create last month dates
    for(var i = 1; i < weekDay + 1; i++){
        var date = preMonthDate - weekDay + i;
        var dateString = "";
            if(this.month == 0){
                dateString = this._getDateString(this.year - 1, 12, date);
            }else{
                dateString = this._getDateString(this.year, this.month, date);
            }
        str += "<td class='bodypanel-othermonthday' date='" + dateString;
        str += "'>" + date + "</td>";
    }

    //create this month dates
    var count = weekDay;
    for(var j = 1; j < thisMonthDate + 1; j++){
        if(count != 0 && count%7 == 0){
            str += "<tr><td class='bodypanel-week'>"
                + weekNumber++ 
                + "</td>";
        }
        var _day = new Date(this.year, this.month, j).getDay();
        var dateString = this._getDateString(this.year, this.month+1, j);
        str += "<td  date='" + dateString + "' class='";
        if(_day == 0 || _day == 6){
            str += "bodypanel-date bodypanel-weekend";
        }
        else{
            str += "bodypanel-date";
        }
        var today = this._getDateString(this.thisDay.getFullYear(), this.thisDay.getMonth() + 1, this.thisDay.getDate());
        if(today == this._getDateString(this.year, this.month+1, j)){
            str += " bodypanel-today";
        }
        if(this.config.dates[dateString] && this.config.dates[dateString].text){
            str += "' title='"  + this.config.dates[dateString].text;
        }
        str += "'>" + j + "</td>";
        count++;
        if(count != 0 && count%7 == 0){
            str += "</tr>";
        }
    }

    //create next month dates
    _date = new Date(this.year, this.month + 1, 1);
    weekDay = _date.getDay();
    if(((weekDay == 0)?6:weekDay - 1) != firstDayInWeek){
        var dayOfMonth = (firstDayInWeek >= 0)?(firstDayInWeek):6;
        if(weekDay <= dayOfMonth){
            var nextMonthCount = dayOfMonth - weekDay;
        }
        else{
            var nextMonthCount = dayOfMonth + 7 - weekDay;
        }
        for(var n = 1; n < nextMonthCount + 2; n++){
            var dateString = "";
            if(this.month == 11){
                dateString = this._getDateString(this.year + 1,1, n);
            }else{
                dateString = this._getDateString(this.year, this.month + 2, n);
            }
            str += "<td class='bodypanel-othermonthday' date='"
                + dateString + "'>" + n + "</td>";
        }
    }
    str += "</tr>";
    return str;
};

/**
 * Get the number of dates by month.
 * @param {Number} month The month.
 * @return The number of dates.
 */
JSCalender.InlineCalender.prototype._getMonthDate = function(month){
    if(month == 1){
        return (0==this.year%4 && (this.year%100!=0 || this.year%400==0)) ? 29 : 28;
    }
    else{
        return this.monthDate[month];
    }
};

/**
 * Get the index of year's week by date.
 * @param {Date} date The date.
 * @return The index of week.
 */
JSCalender.InlineCalender.prototype._getYearWeek = function(date){
    var fistDay = new Date(this.year, 0, 1);
    var d = Math.round((date.valueOf() - fistDay.valueOf()) / 86400000);
    var weekDay = fistDay.getDay() - 1 - this.firstDayInWeek;
    if(weekDay < 0){
        weekDay = weekDay + 7;
    }
    var value;
    if(d==0){
        value = 1;
    }
    else{
        value = (d + weekDay)/7 + 1;
    }
    return parseInt(value, 10);
};

/**
 * Create calendar's info panel.
 * @private
 * @returns {String} Create info panel of calendar.
 */
JSCalender.InlineCalender.prototype._createCalendarInfoPanel = function(){
    return "<div class='inforpanel' id='" + this.calendarContainerId + "-info'>"
    + this.resource.noSelected + "</div>";
};

/**
 * Create calendar's bottom panel.
 * @private
 * @returns {String} Create bottom panel of calendar.
 */
JSCalender.InlineCalender.prototype._createCalendarBottom = function(){
    return "<div class='bottompanel'><div class='bottomleft'>&nbsp;</div><div class='bottomright'>&nbsp;</div></div>";
};


/**
 * Create month combobox.
 * @private
 * @returns {String} Create month combobox of calendar.
 */
JSCalender.InlineCalender.prototype._createMonthCombobox = function(){
    var months = this.resource.monthArray;
    var str = "";
    for(var i = 0; i < 12; i++){
        str += "<div value='" + i + "'";
        if(i == this.month){
            str += " class='monthcombobox-thismonth'";
        }
        str += ">";
        str += months[i];
        str += "</div>";
    }
    return str;
};

/**
 * Create year combobox.
 * @private
 * @returns {String} Create year combobox of calendar.
 */
JSCalender.InlineCalender.prototype._createYearCombobox = function(year){
    var thisYear = this.year;
    if(typeof year == "undefined"){
        year = thisYear;
    }
    var str = "<div class='plus'>-</div>";
    for(var i = year - 5; i < year + 6; i++){
        str += "<div value='" + i + "'";
        if(i == thisYear){
            str += " class='yearcombobox-thisyear'";
        }
        str += ">" + i + "</div>";
    }
    str += "<div class='plus'>+</div>";
    return str;
};

/**
 * Add event to calendar.
 * @private
 */
JSCalender.InlineCalender.prototype._addCalendarEvent = function(){
    var othis = this;
    var preYear = document.getElementById(this.calendarContainerId + "-preYear");
    preYear.onclick = function(){
        othis._preYear(false);
    };
    var nextYear = document.getElementById(this.calendarContainerId + "-nextYear");
    nextYear.onclick = function(){
        othis._nextYear(false);
    };
    var preMonth = document.getElementById(this.calendarContainerId + "-preMonth");
    preMonth.onclick = function(){
        othis._preMonth(false);
    };
    var nextMonth = document.getElementById(this.calendarContainerId + "-nextMonth");
    nextMonth.onclick = function(){
        othis._nextMonth(false);
    };

    var thisMonth = document.getElementById(this.calendarContainerId + "-thisMonth");
    thisMonth.onclick = function(){
        var monthCombobox = document.getElementById(othis.calendarContainerId + "-monthCombobox");
        monthCombobox.innerHTML = othis._createMonthCombobox();
        othis._addMonthComboboxEvent();
        monthCombobox.style.display = "block";
        var thisMonth = document.getElementById(othis.calendarContainerId + "-thisMonth");
        monthCombobox.style.left = thisMonth.offsetLeft + "px";
        monthCombobox.style.top = thisMonth.offsetTop + thisMonth.offsetHeight + "px";
        monthCombobox.style.width = thisMonth.offsetWidth + 15 + "px";
    };

    var thisYear = document.getElementById(this.calendarContainerId + "-thisYear");
    thisYear.onclick = function(){
        var yearCombobox = document.getElementById(othis.calendarContainerId + "-yearCombobox");
        yearCombobox.innerHTML = othis._createYearCombobox();
        othis._addYearComboboxEvent();
        yearCombobox.style.display = "block";
        var thisYear = document.getElementById(othis.calendarContainerId + "-thisYear");
        yearCombobox.style.left = thisYear.offsetLeft + "px";
        yearCombobox.style.top = thisYear.offsetTop + thisYear.offsetHeight + "px";
        yearCombobox.style.width = thisYear.offsetWidth-2 + "px";
    };

    var rows = document.getElementById(this.calendarContainerId + "-bodypanel").childNodes[0].rows;
    for(var i = 1; i < rows.length; i++){
        var row = rows[i];
        row.onmouseover = function(){
            othis._addClass(this, "bodypanel-daterow-hover");
        };
        row.onmouseout = function(){
            othis._removeClass(this, "bodypanel-daterow-hover");
        };
        var cells = rows[i].cells;        
        for(var j = 1; j < cells.length; j++){
            var date = cells[j];
            var dateValue = date.getAttribute("date");
            if(dateValue == this.selectedDateValue || this._inDateRange(dateValue)){
                this._addClass(date, "bodypanel-date-select");
            }
            date.onclick = function(event){
                var event = window.event || event;
                if(event.shiftKey){
                	othis.preSelectedDateValue = othis.selectedDateValue;
                }
                else{
                	othis.preSelectedDateValue = "";
                }
                othis._addClass(this, "bodypanel-date-select");
                othis.selectedDateValue = this.getAttribute("date");
                var month = parseInt(othis.selectedDateValue.split('-')[1],10);
                var thisMonth = othis.month + 1;
                if(thisMonth == 1 && month == 12){
                    othis._preMonth();
                }
                else if(thisMonth == 12 && month == 1){
                    othis._nextMonth();
                }
                else if(thisMonth > month){
                    othis._preMonth();
                }
                else if(thisMonth < month){
                    othis._nextMonth();
                }
                othis._freshDaysPanel();
                othis._print(othis.selectedDateValue);
                othis._checkSlidePanel(othis.selectedDateValue);
            };
            date.onmouseover = function(){
                othis._addClass(this, "bodypanel-date-hover");
            };
            date.onmouseout = function(){
                othis._removeClass(this, "bodypanel-date-hover");
            };
        }
    }
};

/**
 * Add event to window.document.
 * @private
 */
JSCalender.InlineCalender.prototype._addDocumentEvent = function(){
    if(document.addEventListener){
        document.addEventListener("keypress",this._keyEvent,false);
        document.addEventListener("mousedown",this._clickCheck,false);
    }
    else{
        document.attachEvent("onkeydown",this._keyEvent);
        document.attachEvent("onmousedown",this._clickCheck);
    }
};

/**
 * Hide calendar or combobox when clicked
 * @private
 */
JSCalender.InlineCalender.prototype._clickCheck = function(event){
    var calendar = document.inlineCalendar;
    if (!calendar) {
        return;
    }
    event = window.event || event;
    var element = event.srcElement || event.target;
    var calendarDom = document.getElementById(calendar.calendarContainerId);
    var monthCombobox = document.getElementById(calendar.calendarContainerId + "-monthCombobox");
    var yearCombobox = document.getElementById(calendar.calendarContainerId + "-yearCombobox");
    for (; element != null && element != calendarDom; element = element.parentNode);
    if (element == null) {
        monthCombobox.style.display = "none";
        yearCombobox.style.display = "none";
        calendar.focus = false;
        return;
    }
    else{
    	calendar.focus = true;
    }
    element = event.srcElement || event.target;
    for (; element != null && element != monthCombobox; element = element.parentNode);
    if (element == null) {
        monthCombobox.style.display = "none";
    }
    element = event.srcElement || event.target;
    for (; element != null && element != yearCombobox; element = element.parentNode);
    if (element == null) {
        yearCombobox.style.display = "none";
    }
};

/**
 * Check data to decide that whether show the slide panel
 * @param {String} date The date.
 * @private
 */
JSCalender.InlineCalender.prototype._checkSlidePanel = function(date){
    var dateInfo = this.config.dates[date];
    var defaultInfo = this.config.dates["default"];
    var slidePanelContainer = document.getElementById(this.calendarContainerId + "-slidePanel").parentNode.parentNode;
    if(dateInfo){
    	this._showSlidePanel(dateInfo.image, dateInfo.text, date);
    	slidePanelContainer.style.display = "";
    }
    else if(defaultInfo){
    	this._showSlidePanel(defaultInfo.image, defaultInfo.text, date);
    	slidePanelContainer.style.display = "";
    }
    else{
    	slidePanelContainer.style.display = "none";	
    }
};

/**
 * Process key event when press keyboard
 * @param {Object} event The event which comes from pressing keyboard.
 * @private
 */
JSCalender.InlineCalender.prototype._keyEvent = function(event){
    var othis = document.inlineCalendar;
    if(!othis.focus){
    	return;
    }
    var KEY = {
        LEFT  : 37,
        UP    : 38,
        RIGHT : 39,
        DOWN  : 40,
        ESC   : 27,
        ENTER : 13,
        SPACE : 32
    };
    var code = event.keyCode || event.charCode;
    if(event.ctrlKey){
        switch(code)
        {
            case KEY.LEFT:
                othis._preMonth();
                break;
            case KEY.RIGHT:
                othis._nextMonth();
                break;
            case KEY.UP:
                othis._preYear();
                break;
            case KEY.DOWN:
                othis._nextYear();
                break;
            default:
                break;
        }
    }
    else{
        switch(code){
            case KEY.LEFT:
                othis._parseDate(1);
                othis._print(othis.selectedDateValue);
                break;
            case KEY.RIGHT:
                othis._parseDate(-1);
                othis._print(othis.selectedDateValue);
                break;
            case KEY.UP:
                othis._parseDate(7);
                othis._print(othis.selectedDateValue);
                break;
            case KEY.DOWN:
                othis._parseDate(-7);
                othis._print(othis.selectedDateValue);
                break;
            case KEY.ESC:
                othis._unfocus();
                break;
            case KEY.ENTER:
                othis._print(othis.selectedDateValue);
                break;
            case KEY.SPACE:
                othis.selectedDateValue = othis._getDateString(othis.thisDay.getFullYear(),othis.thisDay.getMonth()+1,othis.thisDay.getDate());
                othis.year = othis.thisDay.getFullYear();
                othis.month = othis.thisDay.getMonth();
                document.getElementById(othis.calendarContainerId + "-thisYear").innerHTML = othis.year;
                document.getElementById(othis.calendarContainerId + "-thisMonth").innerHTML = othis.resource.monthArray[othis.month];
                othis._freshDaysPanel();
                othis._print(othis.selectedDateValue);
                break;
            default: 
                break;
        }
    }
    othis._checkSlidePanel(othis.selectedDateValue);
    if(event.preventDefault){
        event.preventDefault();
    }
    else{
        return false;
    }
};

/**
 * Get last year's calendar
 * @private
 */
JSCalender.InlineCalender.prototype._preYear = function(){
    this.year--;
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
};

/**
 * Get next year's calendar
 * @private
 */
JSCalender.InlineCalender.prototype._nextYear = function(){
    this.year++;
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
};

/**
 * Get last month's calendar
 * @private
 */
JSCalender.InlineCalender.prototype._preMonth = function(){
    if(this.month == 0){
        this.month = 11;
        this.year--;
        document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    }
    else{
        this.month--;
    }
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
};

/**
 * Get next month's calendar
 * @private
 */
JSCalender.InlineCalender.prototype._nextMonth = function(){
    if(this.month == 11){
        this.month = 0;
        this.year++;
        document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    }
    else{
        this.month++;
    }
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
};

/**
 * Fresh calendar when press keyboard
 * @param {Number} step The diff which compares with the selected date.
 * @private
 */
JSCalender.InlineCalender.prototype._parseDate = function(step){
    if(this.selectedDateValue){
        var arr = this.selectedDateValue.split('-');
        var date = new Date(new Date(arr[0],arr[1]-1,arr[2]) - 24*60*60*1000*step);
    }
    else{
        var date = new Date(this.thisDay - 24*60*60*1000*step);
    }
    this.year = date.getFullYear();
    this.month = date.getMonth();
    this.date = date.getDate();
    this.selectedDateValue = this._getDateString(this.year,this.month + 1,this.date);
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
};

/**
 * Convert Date to string
 * @param {Number} year The year.
 * @param {Number} month The month.
 * @param {Date} date The date.
 * @private
 */
JSCalender.InlineCalender.prototype._getDateString = function(year,month,date){
    return year + "-" + ((month < 10)?("0" + (month)):month) + "-" + ((date < 10)?("0" + (date)):date);
};


/**
 * Fresh calendar
 * @private
 */
JSCalender.InlineCalender.prototype._freshDaysPanel = function(){
    var str = "<table cellspacing='0' cellpadding='0' class='bodypanel'>"
            + this._createCalendarDays() + this._createCalendarDates()
            + "</table>";
    document.getElementById(this.calendarContainerId + "-bodypanel").innerHTML = str;
    this._addCalendarEvent();
};

/**
 * Show slide panel
 * @private
 */
JSCalender.InlineCalender.prototype._showSlidePanel = function(img, text, dateString){
	var slidePanel = document.getElementById(this.calendarContainerId + "-slidePanel");
	var imageContainer = slidePanel.rows[0].cells[0];
	var topTextContainer = slidePanel.rows[0].cells[1];
	var bottomTextContainer = slidePanel.rows[1].cells[0];
	if(img){
		imageContainer.style.display = "";
		imageContainer.innerHTML = "<img class='slidepanel-img' alt='" 
							     + dateString + "' src='" + img + "'/>";
	}
	else{
		imageContainer.style.display = "none";
	}
	if(text){
		var textArr = text.split('\n');
		
		if(textArr.length < 2){
			topTextContainer.style.display = "";
			bottomTextContainer.style.display = "none";
			topTextContainer.innerHTML = textArr[0];
		}
		else{
			topTextContainer.style.display = "";
			bottomTextContainer.style.display = "";
			topTextContainer.innerHTML = textArr[0];
			textArr[0] = "";
			bottomTextContainer.innerHTML = textArr.join(" ");
		}
	}
	else{
		topTextContainer.style.display = "none";
		bottomTextContainer.style.display = "none";
	}
};

/**
 * Add event to month combobox
 * @private
 */
JSCalender.InlineCalender.prototype._addMonthComboboxEvent = function(){
    var othis = this;
    var monthCombobox = document.getElementById(this.calendarContainerId + "-monthCombobox");
    for(var i = 0, len = monthCombobox.childNodes.length; i < len; i++){
        var row = monthCombobox.childNodes[i];
        row.onclick = function(){
            document.getElementById(othis.calendarContainerId + "-thisMonth").innerHTML = this.innerHTML;
            monthCombobox.style.display = "none";
            othis.month = parseInt(this.getAttribute("value"),10);
            othis._freshDaysPanel();
        };
        row.onmouseover = function(){
            othis._addClass(this, "monthcombobox-hover");
        };
        row.onmouseout = function(){
            othis._removeClass(this, "monthcombobox-hover");
        };
    }
};

/**
 * Add event to year combobox
 * @private
 */
JSCalender.InlineCalender.prototype._addYearComboboxEvent = function(){	
    var othis = this;
    var yearCombobox = document.getElementById(this.calendarContainerId + "-yearCombobox");

    for(var i = 0, len = yearCombobox.childNodes.length; i < len; i++){
        var row = yearCombobox.childNodes[i];
        if(i == 0){
            row.onclick = function(){
                var yearCombobox = document.getElementById(othis.calendarContainerId + "-yearCombobox");
                yearCombobox.innerHTML = othis._createYearCombobox(parseInt(yearCombobox.childNodes[5].innerHTML,10));
                othis._addYearComboboxEvent();
            };
        }
        else if(i == len - 1){
            row.onclick = function(){
                var yearCombobox = document.getElementById(othis.calendarContainerId + "-yearCombobox");
                yearCombobox.innerHTML = othis._createYearCombobox(parseInt(yearCombobox.childNodes[5].innerHTML,10) + 2);
                othis._addYearComboboxEvent();
            };
        }
        else{
            row.onclick = function(){
                document.getElementById(othis.calendarContainerId + "-thisYear").innerHTML = this.innerHTML;
                yearCombobox.style.display = "none";
                othis.year = parseInt(this.getAttribute("value"),10);
                othis._freshDaysPanel();
            };
        }
        row.onmouseover = function(){
            othis._addClass(this, "yearcombobox-hover");
        };
        row.onmouseout = function(){
            othis._removeClass(this, "yearcombobox-hover");
        };
    }
};

/**
 * Add css class to element.
 * @param {Object} el The element where adds class.
 * @param {String} className The css class which to be added..
 * @private
 */
JSCalender.InlineCalender.prototype._addClass = function(el, className) {
    this._removeClass(el, className);
    el.className += " " + className;
};

/**
 * Remove css class of element.
 * @param {Object} el The element where removes class.
 * @param {String} className The css class which to be removed.
 * @private
 */
JSCalender.InlineCalender.prototype._removeClass = function(el, className) {
    if (!(el && el.className)) {
        return;
    }
    var cssArr = el.className.split(" ");
    var newArr = new Array();
    for (var i = 0, len = cssArr.length; i< len; i++){
        if (cssArr[i] != className) {
            newArr[newArr.length] = cssArr[i];
        }
    }
    el.className = newArr.join(" ");
};

/**
 * Judge the date is in date range or not.
 * @param {String} dataString The date which to be judged.
 * @returns {Boolean} in date range or not.
 * @private
 */
JSCalender.InlineCalender.prototype._inDateRange = function(dataString) {
	if(!this.selectedDateValue || !this.preSelectedDateValue){
		return false;
	}
	if(this.selectedDateValue > this.preSelectedDateValue){
		var earlyDate = this.preSelectedDateValue;
		var oldDate = this.selectedDateValue;
	}
	else{
		var earlyDate = this.selectedDateValue;
		var oldDate = this.preSelectedDateValue;
	}
	if(dataString >= earlyDate && dataString <= oldDate){
		return true;
	}
	return false;
	
};

/**
 * Output the selected date
 * @param {String} dateString The format of outputed date
 * @private
 */
JSCalender.InlineCalender.prototype._print = function(dateString) {
	if(!dateString){
		return;
	}
    var arr = dateString.split('-');
    var printFormat = (this.config.printFormat || "").toLowerCase();
    printFormat = printFormat.replace("yyyy", arr[0]);
    printFormat = printFormat.replace("yy", arr[0].substr(2));
    printFormat = printFormat.replace("mm", arr[1]);
    printFormat = printFormat.replace("dd", arr[2]);
    var bottom = document.getElementById(this.calendarContainerId + "-info");
    var number = this._getSelectedDatesNumber();
    if(number > 1){
    	bottom.innerHTML = number + this.resource.selectedDates;
    }
    else{
    	bottom.innerHTML = this.resource.selectedSingleDate + printFormat;
    }
};

/**
 * Get the number of selected dates
 * @returns {Number} The number of selected dates
 * @private
 */
JSCalender.InlineCalender.prototype._getSelectedDatesNumber = function() {
	if(!this.selectedDateValue || !this.preSelectedDateValue){
		return 0;
	}
	if(this.selectedDateValue > this.preSelectedDateValue){
		var currentArr = this.selectedDateValue.split('-');
		var preArr = this.preSelectedDateValue.split('-');
	}
	else{
		var currentArr = this.preSelectedDateValue.split('-');
		var preArr = this.selectedDateValue.split('-');
	}
	var cuurentSelectedDate =  new Date(currentArr[0], currentArr[1], currentArr[2]);
	var preSelectedDate =  new Date(preArr[0], preArr[1], preArr[2]);
    var number = Math.round((cuurentSelectedDate.valueOf() - preSelectedDate.valueOf()) / 86400000) + 1;
    return number;
};

/**
 * Set the config of calendar
 * @param {String} config The config which to be set
 * @public
 */
JSCalender.InlineCalender.prototype.setConfig = function(config) {
	this.config = config;
};

/**
 * Set the language of calendar
 * @param {String} lang The language which to be set
 * @public
 */
JSCalender.InlineCalender.prototype.setLanguage = function(lang) {
	this.resource = Calender_Resources[lang.toLowerCase()]|| Calender_Resources["en"];
};

/**
 * Set the css of calendar
 * @param {String} css The css which to be set
 * @public
 */
JSCalender.InlineCalender.prototype.setCss = function(css) {
	this.config.css = css;
};

/**
 * Set the first day in week
 * @param {Number} day The day which to be set
 * @public
 */
JSCalender.InlineCalender.prototype.setFirstDayInWeek = function(day) {
	this.firstDayInWeek = day;
};

/**
 * Set the export format
 * @param {String} date The date which to be set
 * @param {String} image The image which to be show in slide panel
 * @param {String} text The text which to be show in slide panel
 * @public
 */
JSCalender.InlineCalender.prototype.setSlideDate = function(date, image, text) {
	this.config.dates[date] = {};
	this.config.dates[date].text = text;
	this.config.dates[date].image = image;
};

/**
 * Construct a new PopCalender.
 * @class Represents a PopCalender.
 * @constructor
 * @example 
 * var myCalender = new JSCalender.PopCalender("inputId", "buttonId");
 * @param {String} targetId The id of the Calender's input element.
 * @param {String} onClickElementId The id of the Calender's input element.
 * @return A new instance of a Calender.
 */
JSCalender.PopCalender = function(targetId, onClickElementId){
   /**
    * The id of input element where calender outputs
    * @type String
    */
    this.targetId = targetId;

    /**
     * The id of the element which be clicked to show calender
     * @type String
     */
    this.onClickElementId = onClickElementId;
    
    this._initialize();
};

/**
 * Show the calendar.
 * @public
 */
JSCalender.PopCalender.prototype.show = function(){
    if(document.popCalendar){
        if(document.popCalendar.calendarContainerId == this.calendarContainerId){
            this._show();
            return;
        }
        else{
            this._remove();
        }
    }
    this._createCalendarContainer();

    //add event after dom was created.
    this._addDocumentEvent();
    this._addCalendarEvent();
    this._addMonthComboboxEvent();
    this._addYearComboboxEvent();
    document.popCalendar = this;
};

/**
* Initialize calendar.
* @private
*/
JSCalender.PopCalender.prototype._initialize = function(){
    /**
     * The id of calender's container
     * @default "fogtower-calendar-calendarcontainer"
     * @type String
     */
    this.calendarContainerId = this.targetId ? this.targetId + "-calendarcontainer" : "fogtower-calendar-calendarcontainer";

    /**
     * The config of calender
     * @type Object
     */
    this.config = Calender_Config;

    /**
     * The resource of calender
     * @type Object
     */
    this.resource = Calender_Resources[(this.config.language 
                    || (navigator.browserLanguage?navigator.browserLanguage:navigator.language)).toLowerCase()] || Calender_Resources["en"];

    /**
     * The first day in week
     * @example 
     * this.firstDayInWeek = 0;
     * The first day in week is Monday.
     * this.firstDayInWeek = 6;
     * The first day in week is Sunday.
     * @type Number
     */
    this.firstDayInWeek = this.config.firstDayInWeek;

    /**
     * The days' number in month
     * @type Array
     */
    this.monthDate = [31,28,31,30,31,30,31,31,30,31,30,31];

    /**
     * Today
     * @type Date
     */
    this.thisDay = new Date();

    /**
     * This year
     * @type Number
     */
    this.year = this.thisDay.getFullYear();

    /**
     * This month
     * @type Number
     */
    this.month = this.thisDay.getMonth();
};


/**
 * Get type of the browser.
 * @namespace The browser's type
 * @public
 */
JSCalender.PopCalender.prototype.Browser = {
    /**
     * Browser is IE
     * @public
     * @type Boolean
     */
    IE : !!(window.attachEvent && !window.opera),

    /**
     * Browser is Firefox
     * @public
     * @type Boolean
     */
    FF : navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,

    /**
     * Browser is Opera
     * @public
     * @type Boolean
     */
    Opera : !!window.opera,

    /**
     * Browser is WebKit
     * @public
     * @type Boolean
     */
    WebKit : navigator.userAgent.indexOf('AppleWebKit/') > -1
};



/**
 * Create calendar's container.
 * @private
 */
JSCalender.PopCalender.prototype._createCalendarContainer = function(){
    this.calendarContainer = document.getElementById(this.calendarContainerId);
    if(this.calendarContainer){
        this.calendarContainer.parentNode.removeChild(this.calendarContainer);
    }
    var pos = this._getPosition(document.getElementById(this.onClickElementId));
    this.top = pos.top;
    this.left = pos.left;
    this.calendarContainer = document.createElement("div");
    this.calendarContainer.id = this.calendarContainerId;
    this.calendarContainer.className = this.config.css;
    this.calendarContainer.style.left = this.left + "px";
    this.calendarContainer.style.top = this.top + "px";
    document.body.appendChild(this.calendarContainer);
    this.calendarContainer.innerHTML = this._createCalendarPanel();
    if(this.Browser.IE){
        document.body.appendChild(this._createIframeShim());
    }
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
	this._checkSlidePanel(this._getDateString(this.thisDay.getFullYear(), this.thisDay.getMonth() + 1, this.thisDay.getDate()));
};

/**
 * Create a iframe to fix the combobox's bug of IE6.
 * @returns {Object} The created iframe.
 * @private
 */
JSCalender.PopCalender.prototype._createIframeShim = function(){
    var iframe = document.getElementById(this.calendarContainerId + "-iframeshim");
    if(iframe){
        iframe.parentNode.removeChild(iframe);
    }
    iframe = document.createElement("iframe");
    iframe.id = this.calendarContainerId + "-iframeshim";
    iframe.className = this.config.css + "-iframeshim";
    iframe.scrolling = "no";
    iframe.frameBorder = "0";
    iframe.style.position = "absolute";
    iframe.style.left = this.left + "px";
    iframe.style.top = this.top + "px";
    iframe.style.width = this.calendarContainer.offsetWidth + "px";
    iframe.style.height = this.calendarContainer.offsetHeight + "px";
    return iframe;
};

/**
 * Create calendar's panel.
 * @private
 * @returns {String} Create panel of calendar.
*/
JSCalender.PopCalender.prototype._createCalendarPanel = function(){
    var str = "<table class='panel' cellspacing='0' cellpadding='0'><tr><td style='height:1px;'>"
            + "<div class='topleft'></div><div class='topright'></div><div class='monthcombobox' id='"
        	+ this.calendarContainerId
        	+ "-monthCombobox'>"
            + this._createMonthCombobox()
            + "</div><div class='yearcombobox' id='"
        	+ this.calendarContainerId
            + "-yearCombobox' >"
            + this._createYearCombobox()
            + "</div></td></tr><tr style='display:none;'><td>"
        	+ this._createCalendarSlide()
        	+ "</td></tr><tr><td>"
            + this._createCalendarTop()
            + "</td></tr><tr><td id='"
            + this.calendarContainerId
            + "-bodypanel'>"
            + this._createCalendarBody()
            + "</td></tr><tr><td style='height:10px;'>"
            + this._createCalendarBottom()
            + "</td></tr></table>";
    return str; 
};


/**
 * Create calendar's slide panel.
 * @private
 * @returns {String} Create slide panel of calendar.
*/
JSCalender.PopCalender.prototype._createCalendarSlide = function(){
	var str = "<table class='slidepanel' id='"
		    + this.calendarContainerId + "-slidePanel" 
			+ "'><tr><td></td><td class='slidepanel-text'></td></tr><tr><td class='slidepanel-text' colspan='2'></td></tr></table>";
	return str;
};

/**
 * Create calendar's top panel.
 * @private
 * @returns {String} Create top panel of calendar.
*/
JSCalender.PopCalender.prototype._createCalendarTop = function(){
    var str = "<table class='toppanel'><tr>";
    var topPostionArray = this.config.topPanel;
    for(var i = 0, len = topPostionArray.length; i < len; i++){
        str += "<td onmouseover='javascript:this.className=\"toppanel-hover\";' "
            + " onmouseout='javascript:this.className=\"\";' ";
        if(topPostionArray[i].width){
            str +=" style='width:" + topPostionArray[i].width + ";' ";
        }
        str += " id='" + this.calendarContainerId + "-" + topPostionArray[i].id
            + "' title='" + (this.resource[topPostionArray[i].id] || "") + "' ";
        if(topPostionArray[i].backgroundImage){
            str += "style='backgroundImage : " + topPostionArray[i].backgroundImage + ";' ";
        }
        if(topPostionArray[i].width){
            str += "width='" + topPostionArray[i].width + "' ";
        }
        if(topPostionArray[i].text){
            str += ">" + topPostionArray[i].text;
        }
        else{
            str += ">";
        }
        str += "</td>";
    }
    return str + "</tr></table>";
};

/**
 * Create calendar's body panel.
 * @private
 * @returns {String} Create body panel of calendar.
*/
JSCalender.PopCalender.prototype._createCalendarBody = function(){
    var str = "<table cellspacing='0' cellpadding='0' class='bodypanel'>"
            + this._createCalendarDays()
            + this._createCalendarDates()
            + "</table>";
    return str;
};

/**
 * Create calendar's days in body panel.
 * @private
 * @returns {String} Create days in body panel of calendar.
*/
JSCalender.PopCalender.prototype._createCalendarDays = function(){
    var weekTitle = this.resource.weekTitle;
    var weekDays = this.resource.weekDays;
    var firstDayInWeek = this.firstDayInWeek;
    var arr = [];
    if(typeof firstDayInWeek != "number" || firstDayInWeek > 6 || firstDayInWeek < 0){
        firstDayInWeek = 6;
    }
    for(var i = 0; i < 7; i++){
        if(i >= firstDayInWeek){
            arr[i - firstDayInWeek] = weekDays[i];
        }
        else{
            arr[7- firstDayInWeek + i] = weekDays[i];
        }
    }
    var str = "<tr class='bodypanel-headrow'><td style='width:16%;' class='bodypanel-weekhead'>" + weekTitle + "</td>";
    for(var i = 0;i < 7; i++){
        str += "<td style='width:12%;' class='bodypanel-dayhead";
        if(arr[i] == weekDays[5] || arr[i] == weekDays[6]){
            str +=" bodypanel-weekendhead";
        }
        str += "'>" + arr[i] + "</td>";
    }
    str += "</tr>";
    return str;
};

/**
 * Create calendar's dates in body panel.
 * @private
 * @returns {String} Create dates in body panel of calendar.
*/
JSCalender.PopCalender.prototype._createCalendarDates = function(){
    var firstDayInWeek = this.firstDayInWeek;
    var preMonthDate = this._getMonthDate((this.month == 0)?11:(this.month-1));
    var thisMonthDate = this._getMonthDate(this.month);
    var weekNumber = this._getYearWeek(new Date(this.year, this.month, 1));
    var _date = new Date(this.year, this.month, 1);
    var weekDay = _date.getDay() - 1 - firstDayInWeek;
    if(weekDay < 0){
        weekDay = weekDay + 7;
    }
    var str = "<tr><td class='bodypanel-week'>"
        + weekNumber++ 
        + "</td>";

    //create last month dates
    for(var i = 1; i < weekDay + 1; i++){
        var date = preMonthDate - weekDay + i;
        var dateString = "";
            if(this.month == 0){
                dateString = this._getDateString(this.year - 1, 12, date);
            }else{
                dateString = this._getDateString(this.year, this.month, date);
            }
        str += "<td class='bodypanel-othermonthday' date='" + dateString;
        str += "'>" + date + "</td>";
    }

    //create this month dates
    var count = weekDay;
    for(var j = 1; j < thisMonthDate + 1; j++){
        if(count != 0 && count%7 == 0){
            str += "<tr><td class='bodypanel-week'>"
                + weekNumber++ 
                + "</td>";
        }
        var _day = new Date(this.year, this.month, j).getDay();
        var dateString = this._getDateString(this.year, this.month+1, j);
        str += "<td  date='" + dateString + "' class='";
        if(_day == 0 || _day == 6){
            str += "bodypanel-date bodypanel-weekend";
        }
        else{
            str += "bodypanel-date";
        }
        var today = this._getDateString(this.thisDay.getFullYear(), this.thisDay.getMonth() + 1, this.thisDay.getDate());
        if(today == this._getDateString(this.year, this.month+1, j)){
            str += " bodypanel-today";
        }
        if(this.config.dates[dateString] && this.config.dates[dateString].text){
            str += "' title='"  + this.config.dates[dateString].text;
        }
        str += "'>" + j + "</td>";
        count++;
        if(count != 0 && count%7 == 0){
            str += "</tr>";
        }
    }

    //create next month dates
    _date = new Date(this.year, this.month + 1, 1);
    weekDay = _date.getDay();
    if(((weekDay == 0)?6:weekDay - 1) != firstDayInWeek){
        var dayOfMonth = (firstDayInWeek >= 0)?(firstDayInWeek):6;
        if(weekDay <= dayOfMonth){
            var nextMonthCount = dayOfMonth - weekDay;
        }
        else{
            var nextMonthCount = dayOfMonth + 7 - weekDay;
        }
        for(var n = 1; n < nextMonthCount + 2; n++){
            var dateString = "";
            if(this.month == 11){
                dateString = this._getDateString(this.year + 1,1, n);
            }else{
                dateString = this._getDateString(this.year, this.month + 2, n);
            }
            str += "<td class='bodypanel-othermonthday' date='"
                + dateString + "'>" + n + "</td>";
        }
    }
    str += "</tr>";
    return str;
};

/**
 * Get the number of dates by month.
 * @param {Number} month The month.
 * @return The number of dates.
 */
JSCalender.PopCalender.prototype._getMonthDate = function(month){
    if(month == 1){
        return (0==this.year%4 && (this.year%100!=0 || this.year%400==0)) ? 29 : 28;
    }
    else{
        return this.monthDate[month];
    }
};

/**
 * Get the index of year's week by date.
 * @param {Date} date The date.
 * @return The index of week.
 */
JSCalender.PopCalender.prototype._getYearWeek = function(date){
    var fistDay = new Date(this.year, 0, 1);
    var d = Math.round((date.valueOf() - fistDay.valueOf()) / 86400000);
    var weekDay = fistDay.getDay() - 1 - this.firstDayInWeek;
    if(weekDay < 0){
        weekDay = weekDay + 7;
    }
    var value;
    if(d==0){
        value = 1;
    }
    else{
        value = (d + weekDay)/7 + 1;
    }
    return parseInt(value, 10);
};

/**
 * Create calendar's bottom panel.
 * @private
 * @returns {String} Create bottom panel of calendar.
 */
JSCalender.PopCalender.prototype._createCalendarBottom = function(){
    return "<div class='bottompanel'><div class='bottomleft'>&nbsp;</div><div class='bottomright'>&nbsp;</div></div>";
};

/**
 * Create month combobox.
 * @private
 * @returns {String} Create month combobox of calendar.
 */
JSCalender.PopCalender.prototype._createMonthCombobox = function(){
    var months = this.resource.monthArray;
    var str = "";
    for(var i = 0; i < 12; i++){
        str += "<div value='" + i + "'";
        if(i == this.month){
            str += " class='monthcombobox-thismonth'";
        }
        str += ">";
        str += months[i];
        str += "</div>";
    }
    return str;
};

/**
 * Create year combobox.
 * @private
 * @returns {String} Create year combobox of calendar.
 */
JSCalender.PopCalender.prototype._createYearCombobox = function(year){
    var thisYear = this.year;
    if(typeof year == "undefined"){
        year = thisYear;
    }
    var str = "<div class='plus'>-</div>";
    for(var i = year - 5; i < year + 6; i++){
        str += "<div value='" + i + "'";
        if(i == thisYear){
            str += " class='yearcombobox-thisyear'";
        }
        str += ">" + i + "</div>";
    }
    str += "<div class='plus'>+</div>";
    return str;
};

/**
 * Add event to calendar.
 * @private
 */
JSCalender.PopCalender.prototype._addCalendarEvent = function(){
    var othis = this;
    var preYear = document.getElementById(this.calendarContainerId + "-preYear");
    preYear.onclick = function(){
        othis._preYear(false);
    };
    var nextYear = document.getElementById(this.calendarContainerId + "-nextYear");
    nextYear.onclick = function(){
        othis._nextYear(false);
    };
    var preMonth = document.getElementById(this.calendarContainerId + "-preMonth");
    preMonth.onclick = function(){
        othis._preMonth(false);
    };
    var nextMonth = document.getElementById(this.calendarContainerId + "-nextMonth");
    nextMonth.onclick = function(){
        othis._nextMonth(false);
    };

    var thisMonth = document.getElementById(this.calendarContainerId + "-thisMonth");
    thisMonth.onclick = function(){
        var monthCombobox = document.getElementById(othis.calendarContainerId + "-monthCombobox");
        monthCombobox.innerHTML = othis._createMonthCombobox();
        othis._addMonthComboboxEvent();
        monthCombobox.style.display = "block";
        var thisMonth = document.getElementById(othis.calendarContainerId + "-thisMonth");
        monthCombobox.style.left = thisMonth.offsetLeft + "px";
        monthCombobox.style.top = thisMonth.offsetHeight + (othis.Browser.IE?5:4) + "px";
        monthCombobox.style.width = thisMonth.offsetWidth + 15 + "px";
    };

    var thisYear = document.getElementById(this.calendarContainerId + "-thisYear");
    thisYear.onclick = function(){
        var yearCombobox = document.getElementById(othis.calendarContainerId + "-yearCombobox");
        yearCombobox.innerHTML = othis._createYearCombobox();
        othis._addYearComboboxEvent();
        yearCombobox.style.display = "block";
        var thisYear = document.getElementById(othis.calendarContainerId + "-thisYear");
        yearCombobox.style.left = thisYear.offsetLeft + "px";
        yearCombobox.style.top = thisYear.offsetHeight + (othis.Browser.IE?5:4) + "px";
        yearCombobox.style.width = thisYear.offsetWidth-2 + "px";
    };

    var rows = document.getElementById(this.calendarContainerId + "-bodypanel").childNodes[0].rows;
    for(var i = 1; i < rows.length; i++){
        var row = rows[i];
        row.onmouseover = function(){
            othis._addClass(this, "bodypanel-daterow-hover");
        };
        row.onmouseout = function(){
            othis._removeClass(this, "bodypanel-daterow-hover");
        };
        var cells = rows[i].cells;
        for(var j = 1; j < cells.length; j++){
            var date = cells[j];
            var dateValue = date.getAttribute("date");
            if(dateValue == this.selectedDateValue){
                this._addClass(date, "bodypanel-date-select");
                othis.selectedDate = date;
            }
            date.onclick = function(){
                if(othis.selectedDate){
                    othis._removeClass(othis.selectedDate, "bodypanel-date-select");
                }
                othis._addClass(this, "bodypanel-date-select");
                othis.selectedDate = this;
                othis.selectedDateValue = this.getAttribute("date");
                var month = parseInt(othis.selectedDateValue.split('-')[1],10);
                var thisMonth = othis.month + 1;
                if(thisMonth == 1 && month == 12){
                    othis._preMonth();
                }
                else if(thisMonth == 12 && month == 1){
                    othis._nextMonth();
                }
                else if(thisMonth > month){
                    othis._preMonth();
                }
                else if(thisMonth < month){
                    othis._nextMonth();
                }
                othis._print(othis.selectedDateValue);
                othis._checkSlidePanel(othis.selectedDateValue);
            };
            date.ondblclick = function(){
                this.onclick();
                othis._hide();
            };
            date.onmouseover = function(){
                othis._addClass(this, "bodypanel-date-hover");
            };
            date.onmouseout = function(){
                othis._removeClass(this, "bodypanel-date-hover");
            };
        }
    }
};

/**
 * Add event to window.document.
 * @private
 */
JSCalender.PopCalender.prototype._addDocumentEvent = function(){
    if(document.addEventListener){
        document.addEventListener("keypress",this._keyEvent,false);
        document.addEventListener("mousedown",this._clickCheck,false);
    }
    else{
        document.attachEvent("onkeydown",this._keyEvent);
        document.attachEvent("onmousedown",this._clickCheck);
    }
};

/**
 * Hide calendar or combobox when clicked
 * @private
 */
JSCalender.PopCalender.prototype._clickCheck = function(event){
    var calendar = document.popCalendar;
    if (!calendar) {
        return;
    }
    event = window.event || event;
    var element = event.srcElement || event.target;
    var calendarDom = document.getElementById(calendar.calendarContainerId);
    var monthCombobox = document.getElementById(calendar.calendarContainerId + "-monthCombobox");
    var yearCombobox = document.getElementById(calendar.calendarContainerId + "-yearCombobox");
    for (; element != null && element != calendarDom; element = element.parentNode);
    if (element == null) {
        monthCombobox.style.display = "none";
        yearCombobox.style.display = "none";
        calendar._hide();
        return;
    }
    element = event.srcElement || event.target;
    for (; element != null && element != monthCombobox; element = element.parentNode);
    if (element == null) {
        monthCombobox.style.display = "none";
    }
    element = event.srcElement || event.target;
    for (; element != null && element != yearCombobox; element = element.parentNode);
    if (element == null) {
        yearCombobox.style.display = "none";
    }
};

/**
 * Check data to decide that whether show the slide panel
 * @param {String} date The date.
 * @private
 */
JSCalender.PopCalender.prototype._checkSlidePanel = function(date){
    var dateInfo = this.config.dates[date];
    var defaultInfo = this.config.dates["default"];
    var slidePanelContainer = document.getElementById(this.calendarContainerId + "-slidePanel").parentNode.parentNode;
    if(dateInfo){
    	this._showSlidePanel(dateInfo.image, dateInfo.text, date);
    	slidePanelContainer.style.display = "";
    }
    else if(defaultInfo){
    	this._showSlidePanel(defaultInfo.image, defaultInfo.text, date);
    	slidePanelContainer.style.display = "";
    }
    else{
    	slidePanelContainer.style.display = "none";	
    }
};

/**
 * Process key event when press keyboard
 * @param {Object} event The event which comes from pressing keyboard.
 * @private
 */
JSCalender.PopCalender.prototype._keyEvent = function(event){
    var othis = document.popCalendar;
    var KEY = {
        LEFT  : 37,
        UP    : 38,
        RIGHT : 39,
        DOWN  : 40,
        ESC   : 27,
        ENTER : 13,
        SPACE : 32
    };
    var code = event.keyCode || event.charCode;
    if(event.ctrlKey){
        switch(code)
        {
            case KEY.LEFT:
                othis._preMonth();
                break;
            case KEY.RIGHT:
                othis._nextMonth();
                break;
            case KEY.UP:
                othis._preYear();
                break;
            case KEY.DOWN:
                othis._nextYear();
                break;
            default:
                break;
        }
    }
    else{
        switch(code){
            case KEY.LEFT:
                othis._parseDate(1);
                break;
            case KEY.RIGHT:
                othis._parseDate(-1);
                break;
            case KEY.UP:
                othis._parseDate(7);
                break;
            case KEY.DOWN:
                othis._parseDate(-7);
                break;
            case KEY.ESC:
                othis._hide();
                break;
            case KEY.ENTER:
                othis._print(othis.selectedDateValue);
                othis._hide();
                break;
            case KEY.SPACE:
                othis.selectedDateValue = othis._getDateString(othis.thisDay.getFullYear(),othis.thisDay.getMonth()+1,othis.thisDay.getDate());
                othis.year = othis.thisDay.getFullYear();
                othis.month = othis.thisDay.getMonth();
                document.getElementById(othis.calendarContainerId + "-thisYear").innerHTML = othis.year;
                document.getElementById(othis.calendarContainerId + "-thisMonth").innerHTML = othis.resource.monthArray[othis.month];
                othis._freshDaysPanel();
                othis._print(othis.selectedDateValue);
                break;
            default: 
                break;
        }
    }
    othis._checkSlidePanel(othis.selectedDateValue);
    if(othis.Browser.IE){
        return false;
    }
    else{
        event.preventDefault();
    }
};

/**
 * Get last year's calendar
 * @private
 */
JSCalender.PopCalender.prototype._preYear = function(){
    this.year--;
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
};

/**
 * Get next year's calendar
 * @private
 */
JSCalender.PopCalender.prototype._nextYear = function(){
    this.year++;
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
};

/**
 * Get last month's calendar
 * @private
 */
JSCalender.PopCalender.prototype._preMonth = function(){
    if(this.month == 0){
        this.month = 11;
        this.year--;
        document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    }
    else{
        this.month--;
    }
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
};

/**
 * Get next month's calendar
 * @private
 */
JSCalender.PopCalender.prototype._nextMonth = function(){
    if(this.month == 11){
        this.month = 0;
        this.year++;
        document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    }
    else{
        this.month++;
    }
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
};

/**
 * Fresh calendar when press keyboard
 * @param {Number} step The diff which compares with the selected date.
 * @private
 */
JSCalender.PopCalender.prototype._parseDate = function(step){
    if(this.selectedDateValue){
        var arr = this.selectedDateValue.split('-');
        var date = new Date(new Date(arr[0],arr[1]-1,arr[2]) - 24*60*60*1000*step);
    }
    else{
        var date = new Date(this.thisDay - 24*60*60*1000*step);
    }
    this.year = date.getFullYear();
    this.month = date.getMonth();
    this.date = date.getDate();
    this.selectedDateValue = this._getDateString(this.year,this.month + 1,this.date);
    this._freshDaysPanel();
    document.getElementById(this.calendarContainerId + "-thisYear").innerHTML = this.year;
    document.getElementById(this.calendarContainerId + "-thisMonth").innerHTML = this.resource.monthArray[this.month];
};

/**
 * Convert Date to string
 * @param {Number} year The year.
 * @param {Number} month The month.
 * @param {Date} date The date.
 * @private
 */
JSCalender.PopCalender.prototype._getDateString = function(year,month,date){
    return year + "-" + ((month < 10)?("0" + (month)):month) + "-" + ((date < 10)?("0" + (date)):date);
};


/**
 * Fresh calendar
 * @private
 */
JSCalender.PopCalender.prototype._freshDaysPanel = function(){
    var str = "<table cellspacing='0' cellpadding='0' class='bodypanel'>"
            + this._createCalendarDays() + this._createCalendarDates()
            + "</table>";
    document.getElementById(this.calendarContainerId + "-bodypanel").innerHTML = str;
    this._addCalendarEvent();
    document.getElementById(this.calendarContainerId).focus();
};

/**
 * Show slide panel
 * @private
 */
JSCalender.PopCalender.prototype._showSlidePanel = function(img, text, dateString){
	var slidePanel = document.getElementById(this.calendarContainerId + "-slidePanel");
	var imageContainer = slidePanel.rows[0].cells[0];
	var topTextContainer = slidePanel.rows[0].cells[1];
	var bottomTextContainer = slidePanel.rows[1].cells[0];
	if(img){
		imageContainer.style.display = "";
		imageContainer.innerHTML = "<img class='slidepanel-img' alt='" 
							     + dateString + "' src='" + img + "'/>";
	}
	else{
		imageContainer.style.display = "none";
	}
	if(text){
		var textArr = text.split('\n');
		
		if(textArr.length < 2){
			topTextContainer.style.display = "";
			bottomTextContainer.style.display = "none";
			topTextContainer.innerHTML = textArr[0];
		}
		else{
			topTextContainer.style.display = "";
			bottomTextContainer.style.display = "";
			topTextContainer.innerHTML = textArr[0];
			textArr[0] = "";
			bottomTextContainer.innerHTML = textArr.join(" ");
		}
	}
	else{
		topTextContainer.style.display = "none";
		bottomTextContainer.style.display = "none";
	}
};

/**
 * Add event to month combobox
 * @private
 */
JSCalender.PopCalender.prototype._addMonthComboboxEvent = function(){
    var othis = this;
    var monthCombobox = document.getElementById(this.calendarContainerId + "-monthCombobox");
    for(var i = 0, len = monthCombobox.childNodes.length; i < len; i++){
        var row = monthCombobox.childNodes[i];
        row.onclick = function(){
            document.getElementById(othis.calendarContainerId + "-thisMonth").innerHTML = this.innerHTML;
            monthCombobox.style.display = "none";
            othis.month = parseInt(this.getAttribute("value"),10);
            othis._freshDaysPanel();
        };
        row.onmouseover = function(){
            othis._addClass(this, "monthcombobox-hover");
        };
        row.onmouseout = function(){
            othis._removeClass(this, "monthcombobox-hover");
        };
    }
};

/**
 * Add event to year combobox
 * @private
 */
JSCalender.PopCalender.prototype._addYearComboboxEvent = function(){	
    var othis = this;
    var yearCombobox = document.getElementById(this.calendarContainerId + "-yearCombobox");

    for(var i = 0, len = yearCombobox.childNodes.length; i < len; i++){
        var row = yearCombobox.childNodes[i];
        if(i == 0){
            row.onclick = function(){
                var yearCombobox = document.getElementById(othis.calendarContainerId + "-yearCombobox");
                yearCombobox.innerHTML = othis._createYearCombobox(parseInt(yearCombobox.childNodes[5].innerHTML,10));
                othis._addYearComboboxEvent();
            };
        }
        else if(i == len - 1){
            row.onclick = function(){
                var yearCombobox = document.getElementById(othis.calendarContainerId + "-yearCombobox");
                yearCombobox.innerHTML = othis._createYearCombobox(parseInt(yearCombobox.childNodes[5].innerHTML,10) + 2);
                othis._addYearComboboxEvent();
            };
        }
        else{
            row.onclick = function(){
                document.getElementById(othis.calendarContainerId + "-thisYear").innerHTML = this.innerHTML;
                yearCombobox.style.display = "none";
                othis.year = parseInt(this.getAttribute("value"),10);
                othis._freshDaysPanel();
            };
        }
        row.onmouseover = function(){
            othis._addClass(this, "yearcombobox-hover");
        };
        row.onmouseout = function(){
            othis._removeClass(this, "yearcombobox-hover");
        };
    }
};

/**
 * Get left and top of element
 * @param {Object} el The element which will be get left and top.
 * @private
 */
JSCalender.PopCalender.prototype._getPosition = function(el){
    var positionElement = el;
    var positionElementHeight = positionElement.offsetHeight;
    var positionElementWidth = positionElement.offsetWidth;

    var defaultTop = positionElement.offsetTop + positionElement.offsetHeight;
    var defaultLeft = positionElement.offsetLeft;
    while((positionElement = positionElement.offsetParent) != null){
        defaultTop += positionElement.offsetTop;
        defaultLeft += positionElement.offsetLeft;
    }
    return {top:defaultTop, left:defaultLeft};
};

/**
 * Add css class to element.
 * @param {Object} el The element where adds class.
 * @param {String} className The css class which to be added..
 * @private
 */
JSCalender.PopCalender.prototype._addClass = function(el, className) {
    this._removeClass(el, className);
    el.className += " " + className;
};

/**
 * Remove css class of element.
 * @param {Object} el The element where removes class.
 * @param {String} className The css class which to be removed..
 * @private
 */
JSCalender.PopCalender.prototype._removeClass = function(el, className) {
    if (!(el && el.className)) {
        return;
    }
    var cssArr = el.className.split(" ");
    var newArr = new Array();
    for (var i = 0, len = cssArr.length; i< len; i++){
        if (cssArr[i] != className) {
            newArr[newArr.length] = cssArr[i];
        }
    }
    el.className = newArr.join(" ");
};

/**
 * Remove evnent of window.document
 * @private
 */
JSCalender.PopCalender.prototype._removeEvent = function() {
    if(document.removeEventListener){
        document.removeEventListener("keypress",this._keyEvent,false);
        document.removeEventListener("mousedown",this._clickCheck,false);
    }
    else{
        document.detachEvent("onkeydown",this._keyEvent);
        document.detachEvent("onmousedown",this._clickCheck);
    }
};

/**
 * Show calendar
 * @private
 */
JSCalender.PopCalender.prototype._show = function() {
    this._addDocumentEvent();
    var calendar = document.getElementById(this.calendarContainerId);
    if(calendar){
        calendar.style.display = "";
    }
    var iframeShim = document.getElementById(this.calendarContainerId + "-iframeshim");
    if(iframeShim){
        iframeShim.style.display = "";
    }
};

/**
 * Hide calendar
 * @private
 */
JSCalender.PopCalender.prototype._hide = function() {
    this._removeEvent();
    var calendar = document.getElementById(this.calendarContainerId);
    if(calendar){
        calendar.style.display = "none";
    }
    var iframeShim = document.getElementById(this.calendarContainerId + "-iframeshim");
    if(iframeShim){
        iframeShim.style.display = "none";
    }
};

/**
 * Remove calendar
 * @private
 */
JSCalender.PopCalender.prototype._remove = function() {
    this._removeEvent();
    var calendarDom = document.getElementById(document.popCalendar.calendarContainerId);
    calendarDom.parentNode.removeChild(calendarDom);
    document.popCalendar = null;
};

/**
 * Output the selected date
 * @param {String} dateString The format of outputed date
 * @private
 */
JSCalender.PopCalender.prototype._print = function(dateString) {
	if(!dateString){
		return;
	}
    var arr = dateString.split('-');
    var printFormat = (this.config.printFormat || "").toLowerCase();
    printFormat = printFormat.replace("yyyy", arr[0]);
    printFormat = printFormat.replace("yy", arr[0].substr(2));
    printFormat = printFormat.replace("mm", arr[1]);
    printFormat = printFormat.replace("dd", arr[2]);
    var targetElement = document.getElementById(this.targetId);
    if(targetElement){
        targetElement.value = printFormat;
    }
};

/**
 * Set the config of calendar
 * @param {String} config The config which to be set
 * @public
 */
JSCalender.PopCalender.prototype.setConfig = function(config) {
	this.config = config;
};

/**
 * Set the language of calendar
 * @param {String} lang The language which to be set
 * @public
 */
JSCalender.PopCalender.prototype.setLanguage = function(lang) {
	this.resource = Calender_Resources[lang.toLowerCase()]|| Calender_Resources["en"];
};

/**
 * Set the css of calendar
 * @param {String} css The css which to be set
 * @public
 */
JSCalender.PopCalender.prototype.setCss = function(css) {
	this.config.css = css;
};

/**
 * Set the first day in week
 * @param {Number} day The day which to be set
 * @public
 */
JSCalender.PopCalender.prototype.setFirstDayInWeek = function(day) {
	this.firstDayInWeek = day;
};

/**
 * Set the export format
 * @param {String} printFormat The print format which to be set
 * @public
 */
JSCalender.PopCalender.prototype.setPrintFarmat = function(printFormat) {
	this.config.printFormat = printFormat;
};

/**
 * Set the export format
 * @param {String} date The date which to be set
 * @param {String} image The image which to be show in slide panel
 * @param {String} text The text which to be show in slide panel
 * @public
 */
JSCalender.PopCalender.prototype.setSlideDate = function(date, image, text) {
	this.config.dates[date] = {};
	this.config.dates[date].text = text;
	this.config.dates[date].image = image;
};

