/* DHTML Calendar
 * Copyright (C) 2005, Bill Smith
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 *
 * DHTML Calendar contains some code from a calender script by Mihai Bazon
 * that can be found at http://www.dynarch.com/projects/calendar
 */

function Calendar(){}

Calendar._C = [];
Calendar._shown = null;

Calendar.show = function(trigger, month, year, callback, tagRoot)
{
  if (tagRoot === undefined){ tagRoot = "dc_"; }

  if (Calendar._C[tagRoot] === undefined)
  {
    Calendar._C[tagRoot] = new _Calendar(tagRoot);
  }
    
  Calendar._C[tagRoot].show(trigger, month, year, callback);
};

Calendar.closeAll = function()
{
  for (var tagRoot in Calendar._C)
  {
    Calendar._C[tagRoot].close(false);
  }
};

function _Calendar(tagRoot)
{
  this.tagRoot = tagRoot;
}

_Calendar.prototype.show = function(trigger, month, year, callback)
{
	
 this.trigger = trigger;	
	
  if (Calendar._shown){ Calendar._C[_Calendar._shown].close(false); }
  Calendar._shown = this.tagRoot;
  this.parent = parent;
  var d = new Date();
  
  month = parseInt(month, 10);
  if (month >= 0 && month <= 11)
  {
    this.month = month;
  }
  else
  {
    this.month = d.getMonth();
  }
    
  year = parseInt(year, 10);
  if (year >= 1582 && year <= 5000)
  {
    this.year = year; 
  }
  else
  {
    this.year = d.getFullYear();
  }
    
  this.callback = callback;
  
  var div = _Calendar.createElement("div");
  this.addClass(div, "calendarframe");
  div.style.position = "absolute";
  div.id = this.tagRoot;
  this.frame = div;

  var top = _Calendar.findPosY(trigger) + trigger.offsetHeight;
  div.style.top = top + "px";
  var left = _Calendar.findPosX(trigger);
  div.style.left = left + "px";
    
	var table = _Calendar.createElement("table", div);
  this.addClass(table, "calendar");
	table.cellSpacing = 0;
	table.cellPadding = 0;
	
	var thead = _Calendar.createElement("thead", table);

	var tr = _Calendar.createElement("tr", thead);
  this.addClass(tr, "titlerow");
	
	var td = _Calendar.createElement("td", tr);
	td.unselectable = "on";

  td = _Calendar.createElement("td", tr);
  td.colSpan = 5;
  this.addClass(td, "title");
	td.unselectable = "on";
  td.id = this.tagRoot + "title";
  td.innerHTML = _Calendar.getMonthName(this.month) + ", " + this.year;

	td = _Calendar.createElement("td", tr);
  this.addClass(td, "button");
	td.title = "close";
	td.innerHTML = "&times;";
	td.unselectable = "on";
	td.onclick = _Calendar.closeOnClick;
	td.calendar = this;
  
  tr = _Calendar.createElement("tr", thead);
  this.addClass(tr, "headrow");
  
	td = _Calendar.createElement("td", tr);
  this.addClass(td, "button");
  this.addClass(td, "nav");
	td.title = "previous year";
	td.innerHTML = "&laquo;";
	td.unselectable = "on";
	td.onclick = _Calendar.prevYearOnClick;
	td.onmousedown = _Calendar.prevYearOnMouseDown;
	td.onmouseup = _Calendar.prevYearOnMouseUp;
	td.onmouseout = _Calendar.prevYearOnMouseOut;
	td.calendar = this;
  
	td = _Calendar.createElement("td", tr);
  this.addClass(td, "button");
  this.addClass(td, "nav");
	td.title = "previous month";
	td.innerHTML = "&lsaquo;";
	td.unselectable = "on";
	td.onclick = _Calendar.prevMonthOnClick;
	td.calendar = this;

	td = _Calendar.createElement("td", tr);
	td.colSpan = 3;
  this.addClass(td, "button");
	td.title = "use today's date";
	td.unselectable = "on";
	td.onclick = _Calendar.todayOnClick;
	td.innerHTML = "Today";
	td.calendar = this;
  
	td = _Calendar.createElement("td", tr);
  this.addClass(td, "button");
  this.addClass(td, "nav");
	td.title = "next month";
	td.unselectable = "on";
	td.innerHTML = "&rsaquo;";
	td.onclick = _Calendar.nextMonthOnClick;
	td.calendar = this;

	td = _Calendar.createElement("td", tr);
  this.addClass(td, "button");
  this.addClass(td, "nav");
	td.title = "next year";
	td.innerHTML = "&raquo;";
	td.unselectable = "on";
	td.onclick = _Calendar.nextYearOnClick;
	td.onmousedown = _Calendar.nextYearOnMouseDown;
	td.onmouseup = _Calendar.nextYearOnMouseUp;
	td.onmouseout = _Calendar.nextYearOnMouseOut;
	td.calendar = this;

  tr = _Calendar.createElement("tr", thead);
  this.addClass(tr, "daynames");

  for (var i = 0; i < 7; i++)
  {
    td = _Calendar.createElement("td", tr);
    this.addClass(td, "name");
	  td.unselectable = "on";
	  if (i === 0 || i == 6){ this.addClass(td, "weekend"); }
	  td.innerHTML = _Calendar.getWeekdayName(i);
	}
	
	var tbody = _Calendar.createElement("tbody", table);

  var wdStart = _Calendar.getWeekdayValue(this.month, 1, this.year);
  var days = _Calendar.getDaysInMonth(this.month, this.year);
  var today = new Date();
  var is_opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
  
  for (i = 0; i < 6; i++)
  {
  	tr = _Calendar.createElement("tr", tbody);
	  this.addClass(tr, "daysrow");
	  
    for (var j = 0; j < 7; j++)
    {	
	    td = _Calendar.createElement("td", tr);
  	  this.addClass(td, "day");
	    td.unselectable = "on";
	    td.calendar = this;
  	  if (j === 0 || j == 6){ this.addClass(td, "weekend"); }

      var cellNum = i * 7 + j + 1;
      var cellDay = cellNum - wdStart;
      td.id = this.tagRoot + "day" + cellNum;
      if (cellDay > 0 && cellDay <= days)
      {
        this.addClass(td, "button");
        
        if (!is_opera)
        {
          td.onmouseover = _Calendar.dayOnMouseOver;
          td.onmouseout = _Calendar.dayOnMouseOut;
        }
        td.onclick = _Calendar.dayOnClick;

        td.innerHTML = cellDay;

        if (today.getDate() == cellDay && today.getMonth() == this.month && today.getFullYear() == this.year)
        {
          this.addClass(td, "today");
        }
      }
	  }
	}	

  var tfoot = _Calendar.createElement("tfoot", table);
  tr = _Calendar.createElement("tr", tfoot);
  this.addClass(tr, "footrow");

	td = _Calendar.createElement("td", tr);
	td.colSpan = 7;
  this.addClass(td, "ttip");
  td.id = this.tagRoot + "ttip";
  td.unselectable = "on";
	td.innerHTML = "Select date";
	
	document.getElementsByTagName("body")[0].appendChild(div);
};

// callback takes three args: month, day, year

_Calendar.prototype.close = function(callbackEnabled)
{
  if (callbackEnabled && this.callback !== null)
  {
    this.callback(this.month, this.day, this.year,this.trigger);
  }
  
  this.frame.style.display = "none";
  Calendar._shown = null;
  if (this.parent !== null) 
  {
    this.frame.parentNode.removeChild(this.frame);
    this.parent = null;
  }
};

_Calendar.prototype.redraw = function()
{
  var frame = document.getElementById(this.tagRoot);

  var is_opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
  
  if (is_opera){ frame.style.display = "none"; }

  var td = document.getElementById(this.tagRoot + "title");
  td.innerHTML = _Calendar.getMonthName(this.month) + ", " + this.year;  

  var wdStart = _Calendar.getWeekdayValue(this.month, 1, this.year);
  var days = _Calendar.getDaysInMonth(this.month, this.year);
  var today = new Date();
  
  for (var i = 0; i < 6; i++)
  {
    for (var j = 0; j < 7; j++)
    {	
      var cellNum = i * 7 + j + 1;
      var cellDay = cellNum - wdStart;
      td = document.getElementById(this.tagRoot + "day" + cellNum);
	    this.addClass(td, "day");
  	  if (j === 0 || j == 6){ this.addClass(td, "weekend"); }
      td.onmouseover = null;
      td.onmouseout = null;
      td.onclick = null;
      td.innerHTML = "";
      
      if (cellDay > 0 && cellDay <= days)
      {
        this.addClass(td, "button");
        if (!is_opera)
        {
          td.onmouseover = _Calendar.dayOnMouseOver;
          td.onmouseout = _Calendar.dayOnMouseOut;
        }
        td.onclick = _Calendar.dayOnClick;

        td.innerHTML = cellDay;

        if (today.getDate() == cellDay && today.getMonth() == this.month && today.getFullYear() == this.year)
        {
          this.addClass(td, "today");
        }
        else
        {
          this.removeClass(td, "today");
        }
      }
	  }
	}	
	
  frame.style.display = "block";
};

_Calendar.getEventTarget = function(evt)
{
  var e=evt?evt:window.event?window.event:null; 
  var target=e.target?e.target:e.srcElement?e.srcElement:null; 
  if(target.nodeType==3){ target=target.parentNode; } // handle Safari bug    
  return target;
};

_Calendar.processTimerEntry = function(tagRoot)
{
  Calendar._C[tagRoot].processTimer();
};

_Calendar.prototype.processTimer = function()
{
  this.year += this.yearJump;   
  this.lastYearJump = Math.abs(this.yearJump);
  if (this.yearJump < 0 && this.year < 1582){ this.year = 1582; }
  if (this.yearJump > 0 && this.year > 5000){ this.year = 5000; }
  this.redraw();  
};

_Calendar.closeOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  target.calendar.close(false);
};

_Calendar.dayOnMouseOver = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  target.calendar.addClass(target, "hilite");
};

_Calendar.dayOnMouseOut = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  target.calendar.removeClass(target, "hilite");
};

_Calendar.dayOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  cal.day = parseInt(target.innerHTML, 10);
  cal.close(true);
};

_Calendar.prevYearOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.lastYearJump > 1)
  {
    cal.lastYearJump = 0;
    return;
  }
  if (cal.year <= 1582){ return; }
  cal.year--;  
  cal.redraw();
};

_Calendar.prevYearOnMouseDown = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  cal.yearJump = (-10);
  cal.timer = setInterval("_Calendar.processTimerEntry('" + cal.tagRoot + "')", 600);
};

_Calendar.prevYearOnMouseUp = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.timer !== null)
  {
    clearInterval(cal.timer);
    cal.timer = null; 
  }
};

_Calendar.prevYearOnMouseOut = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.timer !== null)
  {
    clearInterval(cal.timer);
    cal.timer = null; 
  }
};

_Calendar.prevMonthOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.month <= 0 && cal.year <= 1582){ return; }
  cal.month--;
  if (cal.month < 0)
  { 
    cal.month = 11;
    cal.year--;
  }
  
  cal.redraw();
};

_Calendar.todayOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  var d = new Date();
  cal.month = d.getMonth();
  cal.day = d.getDate();
  cal.year = d.getFullYear();
  cal.close(true);
};

_Calendar.nextMonthOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.month >= 11 && cal.year >= 5000){ return; }
  cal.month++;
  if (cal.month > 11)
  { 
    cal.month = 0;
    cal.year++;
  }
  
  cal.redraw();
};

_Calendar.nextYearOnMouseDown = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  cal.yearJump = 10;
  cal.timer = setInterval("_Calendar.processTimerEntry('" + cal.tagRoot + "')", 600);
};

_Calendar.nextYearOnMouseUp = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.timer !== null)
  {
    clearInterval(cal.timer);
    cal.timer = null; 
  }
};

_Calendar.nextYearOnMouseOut = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.timer !== null)
  {
    clearInterval(cal.timer);
    cal.timer = null; 
  }
};

_Calendar.nextYearOnClick = function(evt)
{
  var target = _Calendar.getEventTarget(evt);
  var cal = target.calendar;
  if (cal.lastYearJump > 1)
  {
    cal.lastYearJump = 0;
    return;
  }
  if (cal.year >= 5000){ return; }
  cal.year++;
  
  cal.redraw();
};

_Calendar.isLeapYear = function(year)
{
  if ((year % 4) === 0)
  {
    if ((year % 100) === 0)
    {
      if ((year % 400) === 0)
      {
        return true;      
      }
      else
      {
        return false;
      }
    }
    return true;
  }
  return false;
};

/////////////////////////////////////////////////////////////
// calendar calculations
/////////////////////////////////////////////////////////////

_Calendar._daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];

_Calendar.getDaysInMonth = function(month, year)
{
  var days = _Calendar._daysInMonth[month];
  if (month == 1 && _Calendar.isLeapYear(year))
  {
    return (days + 1);
  }
  else
  {
    return days;  
  }
};

_Calendar._monthValues = [0,3,3,6,1,4,6,2,5,0,3,5];

_Calendar.getWeekdayValue = function(month, day, year) 
{
  var total = Math.floor(year / 4) + year;
  total -= Math.floor(year / 100);
  total += Math.floor(year / 400);
  total += day;
  total += _Calendar._monthValues[month];
  total -= 1;

  return (total % 7);
};

_Calendar._monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"];
  
_Calendar.getMonthName = function(month)
{
  return _Calendar._monthNames[month];
};

_Calendar._weekdayNames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
  
_Calendar.getWeekdayName = function(weekday)
{
  return _Calendar._weekdayNames[weekday];
};

/////////////////////////////////////////////////////////////
// calendar element functions
/////////////////////////////////////////////////////////////

_Calendar.createElement = function(type, parent) 
{
	var el = null;
	if (document.createElementNS) 
	{
		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
	}
	else 
	{
		el = document.createElement(type);
	}
	if (typeof parent != "undefined") 
	{
		parent.appendChild(el);
	}

	return el;
};

_Calendar.prototype.removeClass = function(el, className) 
{
	if (!(el && el.className))
	{
		return;
	}

	className = this.tagRoot + className;

	var cls = el.className.split(" ");
	var ar = [];
	for (var i = cls.length; i > 0;)
	{
		if (cls[--i] != className)
		{
			ar[ar.length] = cls[i];
		}
	}
	el.className = ar.join(" ");
};

_Calendar.prototype.addClass = function(el, className) 
{
	this.removeClass(el, className);
	className = this.tagRoot + className;
	el.className += " " + className;
};

_Calendar.findPosX = function(obj)
{
	var curleft = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft;
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
	{
		curleft += obj.x;
	}
	return curleft;
};

_Calendar.findPosY = function(obj)
{
	var curtop = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop;
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
	{
		curtop += obj.y;
	}
	return curtop;
};