function Calendar(parentId,inputId,offsetX,offsetY,numMonths,startDay,maxPastDays,maxFutureDays,Manager) {
  this.Manager = Manager;
  this.calDiv = new Dynamic(parentId,"div",document.body,true);
  this.calDiv.addClass("cal");
  this.input = new Dynamic(inputId);

  this.top = this.input.findY() + offsetY;
  this.bottom = this.top + this.calDiv.el.offsetHeight;
  this.left = this.input.findX() + offsetX;
  this.right = this.left + this.calDiv.el.offsetWidth;
  this.calDiv.setPos(this.top + "px",this.left + "px");
  this.input.el.setAttribute("autocomplete","off");
  this.now = new Date();

  var startDay = (startDay ? startDay : 0);
  if(typeof startDay == "number") {
    this.startDay = new Date(this.now.getFullYear(), this.now.getMonth(), this.now.getDate() + startDay);
  } else if(this.dateFormat.test(startDay)) {
    var startArray = startDay.split("/");
    this.startDay = new Date(startArray[2], startArray[0] - 1, startArray[1]);
    var startArray = null;
  }
  this.numMonths = (numMonths ? numMonths : 2);

  this.thisMonth = new Date(this.startDay.getFullYear(), this.startDay.getMonth(), 1);
  if(typeof maxPastDays == "boolean" && !maxPastDays) {
    this.minTime = new Date(Number.MIN_VALUE);
  } else if(typeof maxPastDays == "string") {
    var minArray = maxPastDays.split("/");
    this.minTime = new Date(minArray[2], minArray[0] - 1, minArray[1]);
    var minArray = null;
  } else {
    this.minTime = new Date(this.now.getFullYear(), this.now.getMonth(), this.now.getDate() - maxPastDays);
  }
  if(typeof maxFutureDays == "boolean" && !maxFutureDays) {
    this.maxTime = new Date(Number.MAX_VALUE);
  } else if(typeof maxFutureDays == "string") {
    var maxArray = maxFutureDays.split("/");
    this.maxTime = new Date(maxArray[2], maxArray[0] - 1, maxArray[1]);
    var maxArray = null;
  } else {
    this.maxTime = new Date(this.now.getFullYear(), this.now.getMonth(), this.now.getDate() + maxFutureDays + 1);
  }
  this.minMonth = new Date(this.minTime.getFullYear(), this.minTime.getMonth(), 1);
  this.maxMonth = new Date(this.maxTime.getFullYear(), this.maxTime.getMonth() + 1, 0);

  this.inputListener();
  this.highlight = [];
}

Calendar.prototype.monthNames = new Array('January','February','March','April','May','June','July','August','September','October','November','December');

Calendar.prototype.longDayNames = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');

Calendar.prototype.shortDayNames = new Array('S','M','T','W','Th','F','S');

Calendar.prototype.dateFormat = /^(1[0-2]|0?\d{1})\/[0-3]?\d{1}\/\d{4,}$/;

Calendar.prototype.inputListener = function() {
	var obj = this;
  obj.input.listen("keyup",function(){if(obj.dateFormat.test(obj.input.el.value)) { obj.hide(); } });
  obj.input.listen("focus",function(){obj.draw(true);});
}

Calendar.prototype.inputIgnorer = function() {
	var obj = this;
  obj.input.ignore("keyup",function(){if(obj.dateFormat.test(obj.input.el.value)) { obj.hide(); } });
  obj.input.ignore("focus",function(){obj.draw(true);});
}

Calendar.prototype.startWeek = function(stamp){
  var weekDate = new Date(stamp);
  var weekDay = weekDate.getDay();
  var offSet = weekDay * 24 * 60 * 60 * 1000;
  return stamp-offSet;
}

Calendar.prototype.hide = function() {
  this.calDiv.hide();
  this.calDiv.empty();
  this.calDiv.toggleSelects(true);
}

Calendar.prototype.makeDateClickable = function(anch) {
  var obj = this;
  anch.listen("click",function(){obj.printDate(anch);});
}

Calendar.prototype.makeNavClickable = function(anch,adv) {
  var obj = this;
  anch.listen("click",function(){obj.draw(false,adv);});
}

Calendar.prototype.printDate = function(anch) {
  var currDate = anch.currDate;
  var month = currDate.getMonth() + 1;
  var date = currDate.getDate();
  var year = currDate.getFullYear();
  this.input.el.value = month.toString() + "/" + date.toString() + "/" + year.toString();
  this.hide();
}

Calendar.prototype.draw = function(chkInput,adv) {
  adv = (adv ? adv : 0);
  if(this.lowerConstraint && adv == 0) {
    this.setMin(this.lowerConstraint.value);
  }
  if(this.upperConstraint) {
    this.setMax(this.upperConstraint.value);
  }
  this.Manager.hideAll();
  this.calDiv.empty();
  this.figureMonth(chkInput,adv);
  this.docFrag = document.createDocumentFragment();
  for(var n=0; n<this.numMonths; n++) {
    this.drawCal(n);
  }
  var closeAnchor = document.createElement("a");
  closeAnchor.appendChild(document.createTextNode("x"));
  closeAnchor.setAttribute("href","#");
  var obj = this;
  closeAnchor.onclick = function() {
    obj.hide();
    return false;
  }
  closeAnchor.className = "closeAnchor";
  this.calDiv.appendChildren(closeAnchor,this.docFrag);
  this.calDiv.show();
  this.calDiv.toggleSelects();
  this.docFrag = null;
}

Calendar.prototype.figureMonth = function(chkInput,adv) {
  var chkAdv = true;
  if(chkInput) {
    if(this.dateFormat.test(this.input.el.value)) {
      var startArray = this.input.el.value.split("/");
      var newMonth = new Date(startArray[2], startArray[0] - 1, 1);
      if(this.lowerConstraint) {
          var lowConstraintArray = this.lowerConstraint.value.split("/");
          if(lowConstraintArray[0] > newMonth.getMonth()) {
              newMonth = new Date(lowConstraintArray[2], lowConstraintArray[0] - 1, 1);
          }
      }
      chkAdv = false;
    }
  }
  if(chkAdv) {
    if(adv != 0) {
      var newMonth = new Date(this.thisMonth.getFullYear(),this.thisMonth.getMonth() + adv, 1);
    } else {
      var newMonth = new Date(this.startDay.getFullYear(), this.startDay.getMonth(), 1);
    }
  }
  if(newMonth.getTime() >= this.minMonth.getTime() && newMonth.getTime() <= this.maxMonth.getTime()) {
    this.thisMonth = newMonth;
  }
  this.prevMonth = new Date(this.thisMonth.getFullYear(), this.thisMonth.getMonth() - 1, 1);
  this.nextMonth = new Date(this.thisMonth.getFullYear(), this.thisMonth.getMonth() + 1, 1);
  this.prev = true;
  this.next = true;
  if(this.prevMonth.getTime() < this.minMonth.getTime()) {
    this.prev = false;
  } else if(this.nextMonth.getTime() > this.maxMonth.getTime()) {
    this.next = false;
  }
}

Calendar.prototype.drawCal = function(n) {
  this.makeCalObjs(n);
  this.drawNav();

  for(var i=0; i<7; i++) {
    var dayHead = new Dynamic("","td",this.dayRow.el);
    dayHead.appendChildren(this.shortDayNames[i]);
  }

  var curr = this.currMonth.getTime();
  var end = this.endMonth.getTime();
  var min = this.minTime.getTime();
  var max = this.maxTime.getTime();
  for(var z=0; z<6; z++) {
    var e = this.startWk + (z * 24 * 3600 * 7 * 1000);
    var row = new Dynamic("","tr",this.tBody.el);
    for(var g=0; g<7; g++) {
			var dayOfMonth = e + (g * 24 * 3600 * 1000 + (2 * 3600000));
      var currDate = new Date(dayOfMonth);
      var cell = new Dynamic("","td",row.el);
      if(z == 0) {
        cell.addClass("firstRow");
      }
      if(z == 5) {
        cell.addClass("lastRow");
      }
      if(g == 0 || g == 6) {
        cell.addClass("weekend");
        if(g == 0) {
          cell.addClass("sun");
        } else {
          cell.addClass("sat");
        }
      } else {
        cell.addClass("weekday");
      }
      if(dayOfMonth >= curr && dayOfMonth <= end) {
				for(var r=0, len=this.highlight.length; r<len; r++) {
          var emphstring = this.highlight[r].value;
          if(this.dateFormat.test(emphstring)) {
            var emphArray = emphstring.split("/");
            var year = parseInt(emphArray[2]);
            var month = parseInt(emphArray[0]);
            var date = parseInt(emphArray[1]);
            var emphDate = new Date(year, month - 1, date);
            if(emphDate.getDate() == currDate.getDate() && emphDate.getMonth() == currDate.getMonth()) {
              cell.addClass("picked");
            }
            if(r != len-1) {
              var emphstring2 = this.highlight[r+1].value;
              if(this.dateFormat.test(emphstring2)) {
                var emphArray2 = emphstring2.split("/");
                var year2 = parseInt(emphArray2[2]);
                var month2 = parseInt(emphArray2[0]);
                var date2 = parseInt(emphArray2[1]);
                var emphDate2 = new Date(year2, month2 - 1, date2);
                if(currDate.getTime() > emphDate.getTime() && currDate.getTime() < emphDate2.getTime()) {
                  cell.addClass("dateRange");
                }
              }
            }
          }
        }
        if(dayOfMonth > min && dayOfMonth < max) {
          cell.addClass("openDates");
          var anch = new Dynamic("","a",cell.el);
          anch.el.setAttribute("href","#");
          anch.el.onclick = function() {
            return false;
          }
          anch.currDate = currDate;
          this.makeDateClickable(anch);
          anch.appendChildren(currDate.getDate().toString());
        } else {
          cell.appendChildren(currDate.getDate().toString());
          cell.addClass("closedDates");
        }
      } else {
        cell.addClass("outsideMonth");
      }
    }
  }
  this.navRow = null;
}

Calendar.prototype.makeCalObjs = function(n) {
  this.currMonth = new Date(this.thisMonth.getFullYear(), this.thisMonth.getMonth() + n, 1);
  this.startWk = this.startWeek(this.currMonth.getTime());
  this.endMonth = new Date(this.currMonth.getFullYear(), this.currMonth.getMonth() + 1, 0, 3);
  var table = new Dynamic("","table",this.docFrag);
  var tHead = new Dynamic("","thead",table.el);
  this.tBody = new Dynamic("","tbody",table.el);
  this.navRow = new Dynamic("","tr",tHead.el);
  this.dayRow = new Dynamic("","tr",tHead.el);
  var caption = new Dynamic("","caption",table.el);
  caption.appendChildren(this.monthNames[this.currMonth.getMonth()] + ' ' + this.currMonth.getFullYear());
}

Calendar.prototype.drawNav = function() {
  var prevNav = new Dynamic("","th",this.navRow.el);
  prevNav.el.colSpan = 3;
  prevNav.el.setAttribute("id","prevNav");
  var spaceNav = new Dynamic("","th",this.navRow.el);
  var nextNav = new Dynamic("","th",this.navRow.el);
  nextNav.el.colSpan = 3;
  nextNav.el.setAttribute("id","nextNav");
  if(this.prev) {
    var prevNavA = new Dynamic("","a",prevNav.el);
    prevNavA.el.onclick = function() {
      return false;
    }
    prevNavA.el.setAttribute("href","#");
    prevNavA.appendChildren("Prev");
    this.makeNavClickable(prevNavA,-1);
  }
  if(this.next) {
    var nextNavA = new Dynamic("","a",nextNav.el);
    nextNavA.el.onclick = function() {
      return false;
    }
    nextNavA.el.setAttribute("href","#");
    nextNavA.appendChildren("Next");
    this.makeNavClickable(nextNavA,1);
  }
}


Calendar.prototype.constrain = function(input,type) {
  if(type == "lower") {
    this.lowerConstraint = input;
  } else if(type == "upper") {
    this.upperConstraint = input;
  }
}

Calendar.prototype.setMin = function(dateString) {
  if(this.dateFormat.test(dateString)) {
    var minArray = dateString.split("/");
    var year = parseInt(minArray[2]);
    var month = parseInt(minArray[0]);
    var day = parseInt(minArray[1]);
    this.minTime = new Date(year, month - 1, day);
    this.minMonth = new Date(this.minTime.getFullYear(), this.minTime.getMonth(), 1);
    if(!this.dateFormat.test(this.input.el.value)) {
      this.thisMonth = this.minMonth;
    }
  }
}

Calendar.prototype.setMax = function(dateString) {
  if(this.dateFormat.test(dateString)) {
    var maxArray = dateString.split("/");
    var year = parseInt(maxArray[2]);
    var month = parseInt(maxArray[0]);
    var day = parseInt(maxArray[1]);
    this.maxTime = new Date(year, month - 1, day + 1);
    this.maxMonth = new Date(this.maxTime.getFullYear(), this.maxTime.getMonth() + 1, 0);
  }
}

function CalendarManager() {
		this.calendars = [];
}

CalendarManager.prototype.addCal = function() {
	for(var i=0, len=arguments.length; i<len; i++) {
    this.calendars.push(arguments[i]);
  }
}

CalendarManager.prototype.resetMonitor = function() {
		this.calendars = [];
}

CalendarManager.prototype.monitor = function(lower,upper) {
		this.resetMonitor();
		for(var i=2, len=arguments.length; i<len; i++) {
				this.calendars.push(arguments[i]);
		}
		for(var j=0, len=this.calendars.length; j<len; j++) {
				this.calendars[j].highlight = new Array();
				if(j != 0) {
						this.calendars[j].highlight.push(this.calendars[j-1].input.el);
						this.calendars[j].highlight.push(this.calendars[j].input.el);
						if(lower) {
								this.calendars[j].constrain(this.calendars[j-1].input.el,"lower");
						}
				}
				if(j != (len - 1)) {
						this.calendars[j].highlight.push(this.calendars[j].input.el);
						this.calendars[j].highlight.push(this.calendars[j+1].input.el);
						if(upper) {
								this.calendars[j].constrain(this.calendars[j+1].input.el,"upper");
						}
				}
		}
}

CalendarManager.prototype.hideAll = function() {
		for(var i=0, len=this.calendars.length; i<len; i++) {
				this.calendars[i].hide();
		}
}
