/**
* Filename.......: calendar.js
* Project........: Popup Calendar
* Last Modified..: $Date: 2002/07/22 18:17:05 $
* CVS Revision...: $Revision: 1.2 $
* Copyright......: 2001, 2002 Richard Heyes
*/

/**
* Global variables
*/
    dynCalendar_layers          = new Array();
    dynCalendar_mouseoverStatus = false;
    dynCalendar_mouseX          = 0;
    dynCalendar_mouseY          = 0;
	 is_a_package                = false;
	 
	 // In old version of this script, these were used to toggle start/end 
	 // dates for clickable dates. 
	 // Use of these is deprecated. Use publick functions setLimitDateFrom() and setLimitDateThru() instead.
	 limit_from                  = false;
	 limit_thru                  = false;

/**
* The calendar constructor
*
* @access public
* @param string objName      Name of the object that you create
* @param string callbackFunc Name of the callback function
* @param string OPTIONAL     Optional layer name
* @param string OPTIONAL     Optional images path
*/
    function dynCalendar(objName, callbackFunc)
    {
        /**
        * Properties
        */
        // Todays date
        this.today          = new Date();
        this.date           = this.today.getDate();
        this.month          = this.today.getMonth();
        this.year           = this.today.getFullYear();

        this.objName        = objName;
        this.callbackFunc   = callbackFunc;
        this.imagesPath     = arguments[2] ? arguments[2] : 'images/';
        this.layerID        = arguments[3] ? arguments[3] : 'dynCalendar_layer_' + dynCalendar_layers.length;

        this.offsetX        = 5;
        this.offsetY        = 5;

        this.useMonthCombo  = true;
        this.useYearCombo   = true;
        this.yearComboRange = 5;

        this.currentMonth   = this.month;
        this.currentYear    = this.year;
		  
		  // DJ An array of dates that are not clickable on calendar.
		  this.disabledDates  = [];	 	
		  
		  // DJ An array of days of the week that are not clickable on calendar [0-6 = Sun - Sat].
		  this.disabledDaysOfWeek = [];  
		  
		  // DJ: Init the from and thru dates that limit what range of dates in the calendar are clickable.
		  this._limitDateFrom = null;
		  this._limitDateThru = null;
		  
        /**
        * Public Methods
        */
        this.show              = dynCalendar_show;
        this.writeHTML         = dynCalendar_writeHTML;

        this.setOffset         = dynCalendar_setOffset;
        this.setOffsetX        = dynCalendar_setOffsetX;
        this.setOffsetY        = dynCalendar_setOffsetY;
        this.setImagesPath     = dynCalendar_setImagesPath;
        this.setMonthCombo     = dynCalendar_setMonthCombo;
        this.setYearCombo      = dynCalendar_setYearCombo;
        this.setCurrentMonth   = dynCalendar_setCurrentMonth;
        this.setCurrentYear    = dynCalendar_setCurrentYear;
        this.setYearComboRange = dynCalendar_setYearComboRange;
        this.setLimitDateFrom  = dynCalendar_setLimitDateFrom;
        this.setLimitDateThru  = dynCalendar_setLimitDateThru;
				
			// ARR100:limit year/month display
			// calandar months are 0 bases so January = 0
			this.minMonth          = (this.year * 100) + (this.month);
			this.maxMonth          = ((this.year+10) * 100) + (this.month);
			this.setMinMonth       = dynCalendar_setMinMonth;
			this.setMaxMonth       = dynCalendar_setMaxMonth;
			// ARR100:limit year/month display
				
				
			// DJ:set month and year of this calendar to those in another
			this.copyState = dynCalendar_copyState;			
			
			// DJ: Allows for passing in an array of disabled dates
			this.setDisabledDates = dynCalendar_setDisabledDates;
			
			// DJ: Allows for passing in an array of disabled days of the week
			this.setDisabledDaysOfWeek = dynCalendar_setDisabledDaysOfWeek;

		  // DJ: See if there are global scoped variables fro limit from or thru dates.
		  // This is to accommodate any pages that use the older version of this script.
		  // In the older version, global variables could be set in the calling page for from and through dates.
		  // This updated script uses public setLimitDateFrom() and setLimitDateThru() functions to set the limit dates.
		  // This patch looks for those and if present, sets up the calendar to use them.
		  if(limit_from){
		     this.setLimitDateFrom(new Date(from_year, from_month-1, from_day));
		  }
		  if(limit_thru){
		     this.setLimitDateThru(new Date(thru_year, thru_month-1, thru_day));
		  } 			
			
				
        /* Private methods
        */
        // Layer manipulation
        this._getLayer         = dynCalendar_getLayer;
        this._hideLayer        = dynCalendar_hideLayer;
        this._showLayer        = dynCalendar_showLayer;
        this._setLayerPosition = dynCalendar_setLayerPosition;
        this._setHTML          = dynCalendar_setHTML;

        // Miscellaneous
        this._getDaysInMonth   = dynCalendar_getDaysInMonth;
        this._mouseover        = dynCalendar_mouseover;

        /**
        * Constructor type code
        */
        dynCalendar_layers[dynCalendar_layers.length] = this;
        this.writeHTML();
    }

/**
* Shows the calendar, or updates the layer if
* already visible.
*
* @access public
* @param integer month Optional month number (0-11)
* @param integer year  Optional year (YYYY format)
*/
    function dynCalendar_show()
    {
        // Variable declarations to prevent globalisation
        var month, year, monthnames, numdays, thisMonth, firstOfMonth;
        var ret, row, i, cssClass, linkHTML, previousMonth, previousYear;
        var nextMonth, nextYear, prevImgHTML, prevLinkHTML, nextImgHTML, nextLinkHTML;
        var monthComboOptions, monthCombo, yearComboOptions, yearCombo, html;
		          
        this.currentMonth = month = arguments[0] != null ? arguments[0] : this.currentMonth;
        this.currentYear  = year  = arguments[1] != null ? arguments[1] : this.currentYear;

        monthnames = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
        numdays    = this._getDaysInMonth(month, year);

        thisMonth    = new Date(year, month, 1);
        firstOfMonth = thisMonth.getDay();

        // First few blanks up to first day
        ret = new Array(new Array());
        for(i=0; i<firstOfMonth; i++){
            ret[0][ret[0].length] = '<td>&nbsp;</td>';
        }

        // Main body of calendar
        row = 0;
        i   = 1;
        while(i <= numdays){
		  
            if(ret[row].length == 7){
                ret[++row] = new Array();
            }

            /**
            * Generate this cells' HTML
            */
            cssClass = (i == this.date && month == this.month && year == this.year) ? 'dynCalendar_today' : 'dynCalendar_day';
				
				
				/*
				* Is this date ok to link?
				*/
				
				linkok = true;
								 
				// Is this date is before today's date?
				if ((month == this.month) && (year == this.year) && (i < this.date)){ 				 
					linkok = false;				
				} 
				
				
				// Is date one of the disabled out dates?
				if (this.disabledDates.length > 0) { 
					for(var j=0; j<this.disabledDates.length; j++){	
						
						var disabledYear  = this.disabledDates[j].getFullYear();
						var disabledMonth = this.disabledDates[j].getMonth();
						var disabledDate  = this.disabledDates[j].getDate();

						if((year==disabledYear) && (month == (disabledMonth)) && (i == disabledDate)){
							linkok = false;				
						}
					}
				
				} 
				
				
				// Is date one of the disabled days of the week?
				if (this.disabledDaysOfWeek.length > 0) { 
					for(var j=0; j<this.disabledDaysOfWeek.length; j++){						
						if(new Date(year, month, i).getDay() == this.disabledDaysOfWeek[j]){
							linkok = false;				
						}
					}
				}
				
			// Is date before any start limit?	
			  if(this._limitDateFrom != null){
			    if((year == this._limitDateFrom.getFullYear()) && (month == this._limitDateFrom.getMonth()) && (i < this._limitDateFrom.getDate())){
					  linkok = false;
					}
			  }
			  
			  // Is date after any end limit?	
			  if(this._limitDateThru != null){
			    if((year == this._limitDateThru.getFullYear()) && (month == this._limitDateThru.getMonth()) && (i > this._limitDateThru.getDate())){
					  linkok = false;
					}
				}	
										
						
				if(linkok){
					linkHTML = '<a href="javascript: ' + this.callbackFunc + '(' + i + ', ' + (Number(month) + 1) + ', ' + year + '); ' + this.objName + '._hideLayer()">' + (i++) + '</a>';
				} else {
					linkHTML = '<font color="A9A8A8">'+ (i++) +'</font>';
				}
				
				ret[row][ret[row].length] = '<td align="center" class="' + cssClass + '">' + linkHTML + '</td>';
        }

        // Format the HTML
        for(i=0; i<ret.length; i++){
            ret[i] = ret[i].join('\n') + '\n';
        }

        previousYear  = thisMonth.getFullYear();
        previousMonth = thisMonth.getMonth() - 1;
        if(previousMonth < 0){
            previousMonth = 11;
            previousYear--;
        }
        
        nextYear  = thisMonth.getFullYear();
        nextMonth = thisMonth.getMonth() + 1;
        if(nextMonth > 11){
            nextMonth = 0;
            nextYear++;
        }

				//ARR100: Don't show < minMonth
				if (((previousYear * 100) + previousMonth) >= this.minMonth){
				//ARR100: Don't show < minMonth
          prevImgHTML  = '<img src="' + this.imagesPath + '/prev.gif" alt="<<" border="0" />';
          prevLinkHTML = '<a href="javascript: ' + this.objName + '.show(' + previousMonth + ', ' + previousYear + ')">' + prevImgHTML + '</a>';
				//ARR100: Don't show < minMonth
        } else {
				  prevImgHTML  = '&nbsp;'
				  prevLinkHTML  = '&nbsp;'
				}
				//ARR100: Don't show < minMonth

				//ARR100: Don't show > maxMonth
				if (((nextYear * 100) + nextMonth) <= this.maxMonth){
				  nextImgHTML  = '<img src="' + this.imagesPath + '/next.gif" alt=">>" border="0" />';
          nextLinkHTML = '<a href="javascript: ' + this.objName + '.show(' + nextMonth + ', ' + nextYear + ')">' + nextImgHTML + '</a>';
				} else {
				  nextImgHTML  = '&nbsp;'
				  nextLinkHTML  = '&nbsp;'
				}
				//ARR100: Don't show < maxMonth

        /**
        * Build month combo
        */
        if (this.useMonthCombo) {
            monthComboOptions = '';
            for (i=0; i<12; i++) {
                selected = (i == thisMonth.getMonth() ? 'selected="selected"' : '');
                monthComboOptions += '<option value="' + i + '" ' + selected + '>' + monthnames[i] + '</option>';
            }
            monthCombo = '<select name="months" onchange="' + this.objName + '.show(this.options[this.selectedIndex].value, ' + this.objName + '.currentYear)">' + monthComboOptions + '</select>';
        } else {
            monthCombo = monthnames[thisMonth.getMonth()];
        }
        
        /**
        * Build year combo
        */
        if (this.useYearCombo) {
            yearComboOptions = '';
            for (i = thisMonth.getFullYear() - this.yearComboRange; i <= (thisMonth.getFullYear() + this.yearComboRange); i++) {
                selected = (i == thisMonth.getFullYear() ? 'selected="selected"' : '');
                yearComboOptions += '<option value="' + i + '" ' + selected + '>' + i + '</option>';
            }
            yearCombo = '<select style="border: 1px groove" name="years" onchange="' + this.objName + '.show(' + this.objName + '.currentMonth, this.options[this.selectedIndex].value)">' + yearComboOptions + '</select>';
        } else {
            yearCombo = thisMonth.getFullYear();
        }

        html = '<table border="0" bgcolor="#eeeeee">';
        html += '<tr><td class="dynCalendar_header">' + prevLinkHTML + '</td><td colspan="5" align="center" class="dynCalendar_header">' + monthCombo + ' ' + yearCombo + '</td><td align="right" class="dynCalendar_header">' + nextLinkHTML + '</td></tr>';
        html += '<tr>';
        html += '<td class="dynCalendar_dayname">Sun</td>';
        html += '<td class="dynCalendar_dayname">Mon</td>';
        html += '<td class="dynCalendar_dayname">Tue</td>';
        html += '<td class="dynCalendar_dayname">Wed</td>';
        html += '<td class="dynCalendar_dayname">Thu</td>';
        html += '<td class="dynCalendar_dayname">Fri</td>';
        html += '<td class="dynCalendar_dayname">Sat</td></tr>';
        html += '<tr>' + ret.join('</tr>\n<tr>') + '</tr>';
        html += '</table>';

        this._setHTML(html);
        if (!arguments[0] && !arguments[1]) {
            this._showLayer();
            this._setLayerPosition();
        }
    }

/**
* Writes HTML to document for layer
*
* @access public
*/
    function dynCalendar_writeHTML()
    {
        if (is_ie5up || is_nav6up || is_gecko) {
            document.write('<a href="javascript: ' + this.objName + '.show()"><img align="absmiddle" src="' + this.imagesPath + 'dynCalendar.gif" border="0" width="16" height="16" /></a>');
            document.write('<div class="dynCalendar" id="' + this.layerID + '" onmouseover="' + this.objName + '._mouseover(true)" onmouseout="' + this.objName + '._mouseover(false)"></div>');
        }
    }

/**
* Sets the offset to the mouse position
* that the calendar appears at.
*
* @access public
* @param integer Xoffset Number of pixels for vertical
*                        offset from mouse position
* @param integer Yoffset Number of pixels for horizontal
*                        offset from mouse position
*/
    function dynCalendar_setOffset(Xoffset, Yoffset)
    {
        this.setOffsetX(Xoffset);
        this.setOffsetY(Yoffset);
    }

/**
* Sets the X offset to the mouse position
* that the calendar appears at.
*
* @access public
* @param integer Xoffset Number of pixels for horizontal
*                        offset from mouse position
*/
    function dynCalendar_setOffsetX(Xoffset)
    {
        this.offsetX = Xoffset;
    }

/**
* Sets the Y offset to the mouse position
* that the calendar appears at.
*
* @access public
* @param integer Yoffset Number of pixels for vertical
*                        offset from mouse position
*/
    function dynCalendar_setOffsetY(Yoffset)
    {
        this.offsetY = Yoffset;
    }
    
/**
* Sets the images path
*
* @access public
* @param string path Path to use for images
*/
    function dynCalendar_setImagesPath(path)
    {
        this.imagesPath = path;
    }

/**
* Turns on/off the month dropdown
*
* @access public
* @param boolean useMonthCombo Whether to use month dropdown or not
*/
    function dynCalendar_setMonthCombo(useMonthCombo)
    {
        this.useMonthCombo = useMonthCombo;
    }

/**
* Turns on/off the year dropdown
*
* @access public
* @param boolean useYearCombo Whether to use year dropdown or not
*/
    function dynCalendar_setYearCombo(useYearCombo)
    {
        this.useYearCombo = useYearCombo;
    }

/**
* Sets the current month being displayed
*
*  Current month will always be the current month or later
* @access public
* @param boolean month The month to set the current month to
*/
    function dynCalendar_setCurrentMonth(month)
    {
      var today = new Date();
      var thisMonth = today.getMonth();
      var thisYear = today.getFullYear();
      if((month < thisMonth) && (this.currentYear <= thisYear)){
         this.currentMonth = thisMonth;
      } else {
         this.currentMonth = month;
      }
    }

/**
* ARR100 sets the minmonth to allow
* Min month will always be the current month or later
*
* @access public
* @param boolean year The year to set the current year to
*/
    function dynCalendar_setMinMonth(month)
    {
      var today = new Date();
      var thisYear = today.getFullYear();
      var thisMonth = today.getMonth();
      if(String(today.getMonth()).length == 1){
         thisMonth = '0' + thisMonth;
      }
      var thisCombo = Number(String(thisYear) + String(thisMonth));
      if(month < thisCombo){
         this.minMonth = thisCombo;      
      } else {
         this.minMonth = month;
      } 
    }
/**
* ARR100 sets the maxmonth to allow
*
* @access public
* @param boolean year The year to set the current year to
*/
    function dynCalendar_setMaxMonth(month)
    {
        this.maxMonth = month;
    }


/**
* Sets the current month being displayed
*
* @access public
* @param boolean year The year to set the current year to
*/
    function dynCalendar_setCurrentYear(year)
    {
        this.currentYear = year;
    }

/**
* Sets the range of the year combo. Displays this number of
* years either side of the year being displayed.
*
* @access public
* @param integer range The range to set
*/
    function dynCalendar_setYearComboRange(range)
    {
        this.yearComboRange = range;
    }	 
	 
	 
/**
* DJ - 9/16/2005
* Sets the month and year of this calendar to the month and year values
* of the calendar object passed in as @cal
*
* @access public
* @cal a dynCalendar instance from which to copy the month and year
*/
	 function dynCalendar_copyState(cal){
	 	this.currentMonth = cal.currentMonth;
	 	this.currentYear = cal.currentYear;
	 } 
	 

/**
* Returns the layer object
*
* @access private
*/
    function dynCalendar_getLayer()
    {
        var layerID = this.layerID;

        if (document.getElementById(layerID)) {

            return document.getElementById(layerID);

        } else if (document.all(layerID)) {
            return document.all(layerID);
        }
    }

/**
* Hides the calendar layer
*
* @access private
*/
    function dynCalendar_hideLayer()
    {
        this._getLayer().style.visibility = 'hidden';
    }

/**
* Shows the calendar layer
*
* @access private
*/
    function dynCalendar_showLayer()
    {
        this._getLayer().style.visibility = 'visible';
    }

/**
* Sets the layers position
*
* @access private
*/
    function dynCalendar_setLayerPosition()
    {
        this._getLayer().style.top  = (dynCalendar_mouseY + this.offsetY) + 'px';
        this._getLayer().style.left = (dynCalendar_mouseX + this.offsetX) + 'px';
    }

/**
* Sets the innerHTML attribute of the layer
*
* @access private
*/
    function dynCalendar_setHTML(html)
    {
        this._getLayer().innerHTML = html;
    }

/**
* Returns number of days in the supplied month
*
* @access private
* @param integer month The month to get number of days in
* @param integer year  The year of the month in question
*/
    function dynCalendar_getDaysInMonth(month, year)
    {
        monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        if (month != 1) {
            return monthdays[month];
        } else {
            return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ? 29 : 28);
        }
    }

/**
* onMouse(Over|Out) event handler
*
* @access private
* @param boolean status Whether the mouse is over the
*                       calendar or not
*/
    function dynCalendar_mouseover(status)
    {
        dynCalendar_mouseoverStatus = status;
        return true;
    }

/**
* onMouseMove event handler
*/
    dynCalendar_oldOnmousemove = document.onmousemove ? document.onmousemove : new Function;

    document.onmousemove = function ()
    {
        if (is_ie5up || is_nav6up || is_gecko) {
            if (arguments[0]) {
                dynCalendar_mouseX = arguments[0].pageX;
                dynCalendar_mouseY = arguments[0].pageY;
            } else {
                dynCalendar_mouseX = event.clientX + document.body.scrollLeft;
                dynCalendar_mouseY = event.clientY + document.body.scrollTop;
                arguments[0] = null;
            }
    
            dynCalendar_oldOnmousemove();
        }
    }

/**
* Callbacks for document.onclick
*/
    dynCalendar_oldOnclick = document.onclick ? document.onclick : new Function;

    document.onclick = function ()
    {
        if (is_ie5up || is_nav6up || is_gecko) {
            if(!dynCalendar_mouseoverStatus){
                for(i=0; i<dynCalendar_layers.length; ++i){
                    dynCalendar_layers[i]._hideLayer();
                }
            }
    
            dynCalendar_oldOnclick(arguments[0] ? arguments[0] : null);
        }
    }
	 
	 
	 
	 
/**
* DJ - 12/06/2007
* Load the array of disabled dates for this calendar instance
*
* @access public
* @cal a dynCalendar instance from which to copy the month and year
* @dates an array of valid date objects.
*/
function dynCalendar_setDisabledDates(dates){
	this.disabledDates = dates;
}

 
	 
/**
* DJ - 12/06/2007
* Load the array of disabled days of the week for this calendar instance
*
* @access public
* @cal a dynCalendar instance from which to copy the month and year
* @dates an array of integers 0-9.
*/
function dynCalendar_setDisabledDaysOfWeek(days){
	this.disabledDaysOfWeek = days;
}



/**
* DJ - 08/18/2008
* Sets the first day that is clickable on the calendar.
* Sets up the min month values to match the start limit date.
* @access public
*
* @limitDate a Date object containing the first day that should be clickable.
* If @limitDate is not a date, then the limit date is set to null.
* You can pass a null value for no limit date.
* 
*/
function dynCalendar_setLimitDateFrom(limitDate){

	var _limitDate = arguments[0] ? arguments[0] : null;

	if(_limitDate != null && _limitDate.getDate){
	
		this._limitDateFrom   = _limitDate;
		var _limitYear        = _limitDate.getFullYear();
		var _limitMonth       = _limitDate.getMonth();
		var _limitMonthString = _limitDate.getMonth();
		
      if(String(_limitMonthString).length == 1){
         _limitMonthString = '0' + _limitMonthString;
      }		

		this.setMinMonth(String(_limitYear) + String(_limitMonthString));
		this.setCurrentYear(_limitYear);
		this.setCurrentMonth(_limitMonth);		
	
	} else {
		this._limitDateFrom = null;
	}

}



/**
* DJ - 08/18/2008
* Sets the last day that is clickable on the calendar.
* Sets up the max month values to match the end limit date.
* @access public
*
* @limitDate a Date object containing the last day that should be clickable.
* If @limitDate is not a date, then the limit date is set to null.
* You can pass a null value for no limit date.
* 
*/
function dynCalendar_setLimitDateThru(limitDate){

	var _limitDate = arguments[0] ? arguments[0] : null;
	
	if(_limitDate != null && _limitDate.getDate){
	
		this._limitDateThru   = _limitDate;		
		var _limitYear        = _limitDate.getFullYear();
		var _limitMonth       = _limitDate.getMonth();
		var _limitMonthString = _limitDate.getMonth();
		
      if(String(_limitMonthString).length == 1){
         _limitMonthString = '0' + _limitMonthString;
      }			

		this.setMaxMonth(String(_limitYear) + String(_limitMonthString));
		
	} else {
		this._limitDateThru = null;	
	}
}