﻿/********************************************************
Author: David Muto
Creation Date: April 5, 2007
********************************************************/

/********************************************************
Extensions for the date object...works just like SQL/VB DateAdd function
********************************************************/
Date.prototype.add = function(sInterval, iNum) {
  var dTemp = this.clone();
  if (!sInterval || iNum == 0) 
    return dTemp;
  
  switch (sInterval.toLowerCase()) {
    case "ms":
      dTemp.setMilliseconds(dTemp.getMilliseconds() + iNum);
      break;
    case "s":
      dTemp.setSeconds(dTemp.getSeconds() + iNum);
      break;
    case "n":
      dTemp.setMinutes(dTemp.getMinutes() + iNum);
      break;
    case "h":
      dTemp.setHours(dTemp.getHours() + iNum);
      break;
    case "d":
      dTemp.setDate(dTemp.getDate() + iNum);
      break;
    case "m":
      dTemp.setMonth(dTemp.getMonth() + iNum);
      break;
    case "yyyy":
      dTemp.setFullYear(dTemp.getFullYear() + iNum);
      break;
  }
  return dTemp;
}

//get the number of days in the month
Date.prototype.getDaysInMonth = function() {
    return 32 - (new Date(this.getFullYear(), this.getMonth(), 32)).getDate();
}

Date.prototype.sameDay = function(d) {
    return this.getDate() == d.getDate() &&
           this.getMonth() == d.getMonth() &&
           this.getFullYear() == d.getFullYear();
}

//copy the object instead of the reference to it
Date.prototype.clone = function() {
    //yr_num, mo_num, day_num [, hr_num, min_num, sec_num, ms_num]
    return new Date(this.getFullYear(), this.getMonth(), this.getDate());
}

//helper function to remove all child nodes
function removeAllChildNodes(node) {
    if (node && node.hasChildNodes && node.removeChild) {
        while (node.hasChildNodes()) {
            node.removeChild(node.firstChild);
        }
    }
}

/****************************************
Calendar Object
****************************************/
function Calendar(objName, container, blnShowYear, blnAbbrDays, blnAbbrMonths, blnAbbrYear) {
    this.JavaScriptObject = objName;
    this.Container = container;
    
    //names - changes to anything you want
    this.DayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
    this.DayAbbreviations = ['S','M','T','W','T','F','S'];
    this.MonthNames = ['January','February','March','April','May','June','July','August','September','October','November','December'];
    this.MonthAbbreviations = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'];
    
    //get today's date
    this.DisplayDate = new Date();
    this.SelectedDate = this.DisplayDate.clone();      
    this.AbbreviateDayNames = blnAbbrDays;
    this.AbbreviateMonthNames = blnAbbrMonths; 
    this.ShowYear = blnShowYear;
    this.AbbreviateYear = blnAbbrYear; 
    
    this.MinMonth = -1;
    this.MinYear = -1;
    this.MaxMonth = -1;
    this.MaxYear = -1;
    
    //css definitions    
    this.HeaderCssClass = '';
    this.DayHeaderCssClass = '';
    this.GridCssClass = '';
    this.GridItemCssClass = '';
    this.SelectedItemCssClass = '';
    this.TodayCssClass = '';
    
    //html templates
    this.PreviousHTML = null;
    this.NextHTML = null;
    
    //event handlers
    this.OnSelectedMonthChanged = null;
    this.OnSelectedDateChanged = null;
}

/****************************************
Calendar Object Methods
****************************************/
Calendar.prototype = {
    /****************************************
    Current Day/Month 
    ****************************************/
    getDay : function() {
        return this.DisplayDate.getDay();
    },
    getMonth : function() {
        return this.DisplayDate.getMonth();
    },
    getDate : function() {
        return this.SelectedDate.getDate();
    },    
    getDayName : function() {
        if(this.AbbreviateDayNames)
            return this.DayAbbreviations[this.getDay()];
        
        return this.DayNames[this.getDay()];
    },
    getMonthName : function() {
        if(this.AbbreviateMonthNames)
            return this.MonthAbbreviations[this.getMonth()];
        
        return this.MonthNames[this.getMonth()];
    },    
    setDate : function(jsDate) {
        this.DisplayDate = jsDate.clone();
        this.SelectedDate = this.DisplayDate.clone();
    },
    setDate : function(yyyy, mm, dd) {
        this.DisplayDate = new Date(yyyy, mm, dd);
        this.SelectedDate = this.DisplayDate.clone();
    },
    /****************************************
    Previous and Next Months
    ****************************************/
    nextMonth : function() {        
        this.DisplayDate = this.DisplayDate.add("m", 1);
        this.renderControl();        
    },
    prevMonth : function() {
        this.DisplayDate = this.DisplayDate.add("m", -1);                
        this.renderControl();
    },
    dateChanged : function(intDay) {
        //store selected date
        this.SelectedDate = this.DisplayDate.clone();
        
        //set the day of the month to the selected date
        this.SelectedDate.setDate(arguments[0]);
        
        //redraw the control
        this.renderControl();
        
        if(this.OnSelectedDateChanged)
            this.OnSelectedDateChanged.call(this, this.SelectedDate);
    },
    /****************************************
    Render Methods
    ****************************************/
    renderControl : function() {
        //get a reference to the container element
        var elm = document.getElementById(this.Container);            
            
        //get a reference to the header element
        var header = document.getElementById(this.Container +"_header");
        
        //if it doesn't exist...create it
        if(!header) {
            header = document.createElement("div");
            header.id = this.Container +"_header";
            header.className = this.HeaderCssClass;
            
            //add it to the container
            elm.appendChild(header);
        }
        
        //clear existing controls
        removeAllChildNodes(header);
                
        //create child controls for header element
        var div = document.createElement("div");
        div.id = this.Container +"_left";
        
        if(this.MinMonth != -1) {
            if(this.getMonth() >= this.MinMonth && this.DisplayDate.getFullYear() >= this.MaxYear) {
                if(!this.PreviousHTML) {
                    var anchor = document.createElement("a");
                    anchor.id = this.Container +"_prevLink";
                    anchor.href = "javascript:void "+ this.JavaScriptObject +".prevMonth();";
                    anchor.innerHTML = "&lt;&lt;";
                    div.appendChild(anchor);
                }
                else 
                    div.innerHTML = this.PreviousHTML;
            }
        }
        else {
            if(!this.PreviousHTML) {
                    var anchor = document.createElement("a");
                    anchor.id = this.Container +"_prevLink";
                    anchor.href = "javascript:void "+ this.JavaScriptObject +".prevMonth();";
                    anchor.innerHTML = "&lt;&lt;";
                    div.appendChild(anchor);
                }
            else 
                div.innerHTML = this.PreviousHTML;
        }
        
        header.appendChild(div);
        
        div = document.createElement("div");
        div.id = this.Container +"_center";
        
        var span = document.createElement("span");
        span.innerHTML = this.getMonthName();
        
        if(this.ShowYear) {
            span.innerHTML += " ";
            if(this.AbbreviateYear)
                span.innerHTML += this.DisplayDate.getFullYear().toString().substring(2);
            else
                span.innerHTML += this.DisplayDate.getFullYear();
        }
        
        div.appendChild(span);
        header.appendChild(div); 
        
        div = document.createElement("div");
        div.id = this.Container +"_right";
        
        if(this.MaxMonth != -1) {
            if ((this.DisplayDate.getMonth() <= this.MaxMonth && this.DisplayDate.getFullYear() <= this.MaxYear) || (this.DisplayDate.getFullYear() < this.MaxYear)) {
                if(!this.NextHTML) {
                    anchor = document.createElement("a");
                    anchor.id = this.Container +"_nextLink";
                    anchor.href = "javascript:void "+ this.JavaScriptObject +".nextMonth();";
                    anchor.innerHTML = "&gt;&gt;";
                    div.appendChild(anchor);
                }
                else
                    div.innerHTML = this.NextHTML;
            }
        }
        else {
            if(!this.NextHTML) {
                anchor = document.createElement("a");
                anchor.id = this.Container +"_nextLink";
                anchor.href = "javascript:void "+ this.JavaScriptObject +".nextMonth();";
                anchor.innerHTML = "&gt;&gt;";
                div.appendChild(anchor);
            }
            else
                div.innerHTML = this.NextHTML;
        }
        
        header.appendChild(div);
    
        //get a reference to the grid element
        var dateGrid = document.getElementById(this.Container +"_grid");
    
        //if it doesn't exist...create it
        if(!dateGrid) {
            dateGrid = document.createElement("div");
            dateGrid.id = this.Container +"_grid";    
            dateGrid.className = this.GridCssClass;
            
            //add grid to container
            elm.appendChild(dateGrid);
        }
    
        //clear all children
        removeAllChildNodes(dateGrid);    
        //write out the calendar
        dateGrid.innerHTML = this.getGrid();
    
        //call OnSelectedMonthChanged for this month
        if(this.OnSelectedMonthChanged)
            this.OnSelectedMonthChanged.call(this, this.getMonthName(), this.DisplayDate.getFullYear());
    },
    getGrid : function() {
        //create a mock multi-dimensional array
        var grid = new Array(7);
        for(var i=0;i<grid.length;i++)
            grid[i] = new Array(7);
        
        //first row is all day names
        for(var i=0;i<grid[0].length;i++) {
            if(this.AbbreviateDayNames)
                grid[0][i] = this.DayAbbreviations[i];
            else
                grid[0][i] = this.DayNames[i];
        }
        
        //copy the display date (really just for the month)
        var tmpDate = this.DisplayDate.clone(); 
        //this month only
        var thisMonth = tmpDate.getMonth();   
        //move to the first day of the month
        tmpDate.setDate(1);
        
        var blnOk = false;
        for(var i=1;i<grid.length;i++) {
            for(var j=0;j<grid[i].length;j++) {
              
                //if there are no more days in this month to fill the squares...                
                if(tmpDate.getMonth() != thisMonth) {
                    grid[i][j] = '&nbsp;';
                    continue;              
                }
                else {
                    //if first row                    
                    if(i == 1) {
                        //if we shouldn't write yet
                        if(!blnOk) {
                            if(j == tmpDate.getDay()) {
                                blnOk = true;
                                grid[i][j] = tmpDate.getDate();
                            }
                            else {
                                grid[i][j] = '&nbsp;';
                                continue;                          
                            }
                        }
                        else
                            grid[i][j] = tmpDate.getDate();
                    }
                    else 
                        grid[i][j] = tmpDate.getDate();
                }
          
                  //move to the next day
                tmpDate = tmpDate.add("d", 1);                    
            }
        }        
        
        //return the HTML for this month's grid      
        return this.getGridHTML(grid);
    },
    getGridHTML : function(matrix) {
        var html = "<table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\r\n";
	    var row, col, css, tmpDate;
	    for(row = 0;row < matrix.length;++row)
	    {
		    html += " <tr>\r\n";		
		    for(col = 0; col <matrix[row].length; ++col) {
		        if(row == 0) {
		            html += "  <td valign=\"middle\" align=\"center\" class=\""+ this.DayHeaderCssClass +"\">";
			        html += matrix[row][col];
			        html += "</td>\r\n";
		        }
		        else {
		            //set to first day of month
		            tmpDate = this.DisplayDate.clone();
		            tmpDate.setDate(matrix[row][col]);
		            
		            if(tmpDate.sameDay(new Date())) {
		                css = this.TodayCssClass;		                
		            }
		            else if(tmpDate.sameDay(this.SelectedDate)) {
		                css = this.SelectedItemCssClass;		                
		            }
		            else
		                css = this.GridItemCssClass;
		            
		            html += "  <td valign=\"middle\" align=\"center\" class=\""+ css +"\">";
			        html += "<a href=\"javascript:void "+ this.JavaScriptObject +".dateChanged('"+ matrix[row][col] +"');\">"+ matrix[row][col] +"</a>";
			        html += "</td>\r\n";
			    }
			}
			
		    html += " </tr>\r\n";
	    }
	    html += "</table>\r\n";
	
	    return html;
    }
}; 