
/* This script and many more are available free online at
The JavaScript Source :: http://javascript.internet.com
Created by: Michael O'Connell :: http://wunder-ful.com */ /**
* Invocation represents a function call in a specific object context with a list of parameters
* This is a very powerful object that allows code to be executed in it's intended context
* from any other context (context = the 'this' for that function).
* There is a perfomance hit to use this (takes about 5x more time)
* WARNING in IE if you try to use invoke on an object that is in a window that is no longer loaded it will throw "can't execute code in freed script"
* &#64;constructor
* &#64;param targetObj object to invoke a method on (functions not on an object are attached to the window object)
* &#64;param functionName string containing the name of the function to invoke on targetObj
* &#64;param arguments an Array of arguments to invoke the function with (optional)
* &#64;author Mike O'Connell
*/
function Invocation(targetObj, functionName, arguments) {
  this.targetObj = targetObj;
  this.functionName = functionName;
  this.arguments = arguments;
} /**
* Actually invoke the function call defined by this instance of Invocation.
* If the object doesn't exist, or the function does not exist it will not try to execute
* &#64;param arguments an Array of arguments to invoke the function with (will be appended to the end of the predefined argument Array)
* &#64;return The return value of the function invoked.
*/
Invocation.prototype.invoke = function(arguments) {
  if(this.targetObj && typeof this.targetObj[this.functionName] == "function") { var args = this.arguments; if(!args)   args = new Array(); if(arguments)   args = args.concat(arguments); return this.targetObj[this.functionName].apply(this.targetObj, args);
  }
} /**
* &#64;return the sum of all the offsetTop values from this element to the body
*/
function getTotalOffsetTop(element) {
  var offset = 0;
  for(var i = element; i && i.tagName != "BODY"; i = i.offsetParent) offset+=i.offsetTop;
  return offset;
} /**
* &#64;return the sum of all the offsetLeft values from this element to the body
*/
function getTotalOffsetLeft(element) {
  var offset = 0;
  for(var i = element; i && i.tagName != "BODY"; i = i.offsetParent) offset+=i.offsetLeft;
  return offset;
} /**
* Positions a absolutely positioned element over the anchor.  It will detect body clipping
* and move the right side of the element to the right of the anchor.  The element cannot
* have
* &#64;param element the element to position
* &#64;param anchor the anchor to position element over
* &#64;param positionVertical position on "top" or "bottom" edge of the anchor
* &#64;param positionHorizontal position on "left" or "right" edge of the anchor
*/
function positionAtAnchor(element, anchor, positionVertical, positionHorizontal) {
  var left = getTotalOffsetLeft(anchor);   if(positionHorizontal == "right") left = left + anchor.offsetWidth + 1;   //horizontal clip
  if(left + element.offsetWidth > document.body.scrollWidth && left - element.offsetWidth > 0) { left = left - element.offsetWidth; if(positionHorizontal == "right")   left = left - anchor.offsetWidth; else   left = left + anchor.offsetWidth + 1;
  }   var top = getTotalOffsetTop(anchor);
  if(positionVertical == "bottom") top = top + anchor.offsetHeight + 1;   //vertical clip
  if(top + element.offsetHeight > document.body.scrollHeight && top - element.offsetHeight > 0) { top = top - element.offsetHeight; if(positionVertical == "bottom")   top = top - anchor.offsetHeight; else   top = top + anchor.offsetHeight + 1;
  }   element.style.top = top+"px";
  element.style.left = left+"px";
} 
// =========================== //map of dynaCalendars by name
var dynaCalendars = {}; /**
* Opens / closes the dynacalendar at the specified anchor
* &#64;param name The unique name for the calendar to open.
* &#64;param anchor A element to open the calendar under
**/
function openDynaCalendar(name, anchor) {
  var calendar = dynaCalendars[name];
  if(!calendar.isOpen) calendar.open(anchor);
  else calendar.close();
} /**
* Constructor for the DynaCalendar
* &#64;param name A unique name for this calendar
* &#64;param invocation The function to pass the date when a date is selected.
* &#64;param year Year to start calendar.
* &#64;param month Month to start calendar.
* &#64;param day Day to start calendar.
* &#64;see Invocation
**/
function DynaCalendar(name, invocation, year, month, day) {
  //populate day of week and month name constants if not previously created...
  if(!DynaCalendar.MONTHS) { var MONTHS = []; var DOW = []; DynaCalendar.MONTHS = MONTHS; DynaCalendar.DOW = DOW;
 var tmpDate = new Date(2000, 0, 1); for(var i = 0; i < 12; i++) {   tmpDate.setMonth(i);   var dateStr = tmpDate.toLocaleString();   var monthname = dateStr.match(/\s[a-zA-Z]+\s/)[0];   monthname = monthname.substring(1,monthname.length-1);   MONTHS[i] = monthname; }
 for(var i = 1; i < 8; i++) {   tmpDate.setDate(i);   var dateStr = tmpDate.toLocaleString();   var dow = dateStr.substring(0,3);   DOW[tmpDate.getDay()] = dow; }
  }   if(year && month && day) this.date = new Date(year, month, day);
  else this.date = new Date();   this.invocation = invocation;
  this.name = name;
  this.isOpen = false;
  this.rendered = false;
  this.bodyRendern = false;   dynaCalendars[name] = this;
} /**
* Displays the dynacalendar at the specified anchor.
* &#64;param anchor A element to open the calendar under
*/
DynaCalendar.prototype.open = function(anchor) {
  if(!this.rendered) this.render();
  if(!this.bodyRendern) this.renderBody();
  this.calendarDiv.style.display = "block";
  positionAtAnchor(this.calendarDiv, anchor, "bottom", "left");
  this.isOpen = true;
} /**
* Closes the dynaCalendar
*/
DynaCalendar.prototype.close = function() {
  this.calendarDiv.style.display = "none";
  this.isOpen = false;
} /**
* Fills the calendarDiv with actual date spans
**/
DynaCalendar.prototype.render = function() {
  var calendarDiv = document.createElement("DIV");
  var calendarTable = document.createElement("TABLE");
  var calendarHead = document.createElement("THEAD");
  var calendarHeadTR = document.createElement("TR");
  var calendarHeadWeekTR = document.createElement("TR");
  var calendarBY = document.createElement("TH");
  var calendarBM = document.createElement("TH");
  var calendarTitle = document.createElement("TH");
  var calendarFM = document.createElement("TH");
  var calendarFY = document.createElement("TH");
  var calendarBody = document.createElement("TBODY");   calendarDiv.className = "dynaCalendarDiv";
  calendarTable.className = "dynaCalendarTable";
  calendarTitle.colSpan = 3;   calendarBY.className = "BF";
  calendarBM.className = "BF";
  calendarFM.className = "BF";
  calendarFY.className = "BF";
  calendarBY.innerHTML = "&lt;&lt;";
  calendarBM.innerHTML = "&lt;";
  calendarFM.innerHTML = "&gt;";
  calendarFY.innerHTML = "&gt;&gt;";
  calendarBY.onclick = dynaCalendarBackYearOnClick;
  calendarBM.onclick = dynaCalendarBackMonthOnClick;
  calendarFM.onclick = dynaCalendarForwardMonthOnClick;
  calendarFY.onclick = dynaCalendarForwardYearOnClick;   calendarDiv.dynaCalendar = this;
  calendarHead.dynaCalendar = this;
  calendarBody.dynaCalendar = this;
  calendarBY.dynaCalendar = this;
  calendarBM.dynaCalendar = this;
  calendarTitle.dynaCalendar = this;
  calendarFM.dynaCalendar = this;
  calendarFY.dynaCalendar = this;   this.calendarTitle = calendarTitle;
  this.calendarBody = calendarBody;
  this.calendarDiv = calendarDiv;   //populate week header
  for(var i = 0; i < 7; i++) { var dowTH = document.createElement("TH"); dowTH.innerHTML = DynaCalendar.DOW[i]; calendarHeadWeekTR.appendChild(dowTH);
  }   calendarHeadTR.appendChild(calendarBY);
  calendarHeadTR.appendChild(calendarBM);
  calendarHeadTR.appendChild(calendarTitle);
  calendarHeadTR.appendChild(calendarFM);
  calendarHeadTR.appendChild(calendarFY);
  calendarHead.appendChild(calendarHeadTR);
  calendarHead.appendChild(calendarHeadWeekTR);
  calendarTable.appendChild(calendarHead);
  calendarTable.appendChild(calendarBody);
  calendarDiv.appendChild(calendarTable);
  document.body.appendChild(calendarDiv);   this.rendered = true;
} /**
* Renders the actual month.
**/
DynaCalendar.prototype.renderBody = function() {
  //title
  var title = DynaCalendar.MONTHS[this.date.getMonth()]+ " " + this.date.getFullYear();
  this.calendarTitle.innerHTML = title;   //clear table body
  while(this.calendarBody.hasChildNodes()) this.calendarBody.removeChild(this.calendarBody.lastChild);   //generate new table body
  var tmpDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1);   var row = document.createElement("TR");
  this.calendarBody.appendChild(row);   //blanks
  for(var i = 0; i < tmpDate.getDay(); i++) { var td = document.createElement("TD"); row.appendChild(td);
  }   while(tmpDate.getMonth() == this.date.getMonth()) { var td = document.createElement("TD"); var date = tmpDate.getDate(); if(date == this.date.getDate())   td.className = "daySelected"; else   td.className = "day";
 td.innerHTML = date; td.dynaCalendar = this; td.onclick = dynaCalendarSelectDayOnClick; row.appendChild(td);
 if(tmpDate.getDay() == 6) {   row = document.createElement("TR");   this.calendarBody.appendChild(row); }
 tmpDate.setDate(date + 1);
  }   //blanks
  for(var i = tmpDate.getDay(); i < 7; i++) { var td = document.createElement("TD"); row.appendChild(td);
  }   this.bodyRendern = true;
} /**
* Set the day of the month
* &#64;param date The date of the month to set the calandar to.
**/
DynaCalendar.prototype.setDate = function(date) {
  this.date.setDate(date);
  this.bodyRendern = false;
} /**
* Moves back a year
**/
DynaCalendar.prototype.backYear = function() {
  this.setDate(1);
  this.date.setFullYear(this.date.getFullYear() - 1);
  this.renderBody();
} /**
* Moves back a month
**/
DynaCalendar.prototype.backMonth = function() {
  this.setDate(1);
  this.date.setMonth(this.date.getMonth() - 1);
  this.renderBody();
} /**
* Moves forward a month
**/
DynaCalendar.prototype.forwardMonth = function() {
  this.setDate(1);
  this.date.setMonth(this.date.getMonth() + 1);
  this.renderBody();
} /**
* Moves forward a year
**/
DynaCalendar.prototype.forwardYear = function() {
  this.setDate(1);
  this.date.setFullYear(this.date.getFullYear() + 1);
  this.renderBody();
} // Event handlers -------------------------------------------------------------
/**
* onclick to select a day
*/
function dynaCalendarSelectDayOnClick(event) {
  //IE
  if(window.event) { var srcElement = window.event.srcElement;
  }
  //MOZ
  else { var srcElement = event.currentTarget;
  }   var calendar = srcElement.dynaCalendar;
  calendar.setDate(srcElement.innerHTML);
  if(calendar.invocation) calendar.invocation.invoke([calendar.date]);
  srcElement.dynaCalendar.close();   return false;
} /**
* onclick to go back one year
*/
function dynaCalendarBackYearOnClick(event) {
  //IE
  if(window.event) { var srcElement = window.event.srcElement;
  }
  //MOZ
  else { var srcElement = event.currentTarget;
  }   srcElement.dynaCalendar.backYear();
  return false;
} /**
* onclick to go back one month
*/
function dynaCalendarBackMonthOnClick(event) {
  //IE
  if(window.event) { var srcElement = window.event.srcElement;
  }
  //MOZ
  else { var srcElement = event.currentTarget;
  }   srcElement.dynaCalendar.backMonth();
  return false;
} /**
* onclick to go forward one month
*/
function dynaCalendarForwardMonthOnClick(event) {
  //IE
  if(window.event) { var srcElement = window.event.srcElement;
  }
  //MOZ
  else { var srcElement = event.currentTarget;
  }   srcElement.dynaCalendar.forwardMonth();
  return false;
} /**
* onclick to go forward one year
*/
function dynaCalendarForwardYearOnClick(event) {
  //IE
  if(window.event) { var srcElement = window.event.srcElement;
  }
  //MOZ
  else { var srcElement = event.currentTarget;
  }   srcElement.dynaCalendar.forwardYear();
  return false;
} // =================================== function setup_cal() {
  var cal = new DynaCalendar("test",new Invocation(this,"change_link"));
} function change_link(date) {
  document.getElementById("link1").innerHTML = date.toLocaleString();
} // ================================= window.onload = setup_cal();  

