function CalendarWidget(parentNode,ib_ob) {
/* tag the parentnode, but do not create calendar until absolutely required! */
  this.startOfYear = {};
  this.numDaysInMonth = {};
  if(typeof(parentNode) == 'string') {
    this.parentNode = document.getElementById(parentNode);
  } else { this.parentNode = parentNode; }
  this.days = {};
  this.boxes = [];
  this.rendered = 0;
  this.ibOb = ib_ob;
  this.firstYear = 1900; this.firstMonth = 1; this.firstDay = 1;
  this.lastYear = 2100; this.lastMonth = 1;
  this.selectedYear = 2007; this.selectedMonth = 4; this.selectedDay = 12;
  this.monthNames = ['','January','February','March','April','May','June','July','August','September','October','November','December'];
  this.onSelChange = null;
}

CalendarWidget.prototype.setLastMonth = function(year,month) {
  this.lastYear = year;
  this.lastMonth = month;
}
CalendarWidget.prototype.hide = function() {
  this.parentNode.className = 'hidden';
  if(this.ibOb=='outbound') {
    document.body.className = document.body.className.replace(/hide_selects_cal_o[^ ]*/,'');
  } else {
    document.body.className = document.body.className.replace(/hide_selects_cal_i[^ ]*/,'');
  }
  document.onmouseup = this.oldMouseUp;
}
CalendarWidget.prototype.setFirstDate = function(year,month,day,offset) {
  if(offset) {
    var d = new Date(year,parseInt(month,10)-1,parseInt(day,10));
    d.setDate(d.getDate()+1);
    this.firstYear = d.getFullYear();
    this.firstMonth = d.getMonth()+1;
    this.firstDay = d.getDate();
  } else {
    this.firstYear = parseInt(year);
    this.firstMonth = parseInt(month,10);
    this.firstDay = parseInt(day,10);
  }
}
CalendarWidget.prototype.selectDate = function(year,month,day) {
  this.selectedYear = parseInt(year);
  this.selectedMonth = parseInt(month,10);
  this.selectedDay = parseInt(day,10);
}

CalendarWidget.prototype.showMonth = function(year,month) {
  if(!year) { year = this.selectedYear; }
  if(!month) { month = this.selectedMonth; }
  if(!this.numDaysInMonth[year]) {
    var days = [31,28,31,30,31,30,31,31,30,31,30,31];
    // deal with leap years... [400 = yes, 100=no, 4=yes]
    if(!(year%400) || (year%100 && !(year%4))) { days[1]+=1; }
    this.numDaysInMonth[year] = days;
    // also calculate first day of year
    var t = new Date(Date.UTC(year,0,1));
    var startOfYear = t.getUTCDay();
    this.startOfYear[year] = startOfYear;
  } else { var days = this.numDaysInMonth[year]; var startOfYear = this.startOfYear[year]; }
  var startOfMonth = startOfYear;
  for(var i=0;i<month-1;i++) {
    startOfMonth += days[i];
  }
  this.displayedMonthStart = startOfMonth%7;
  this.renderMonth(year,month,startOfMonth%7,days);
  this.displayedYear = year;
  this.displayedMonth = month;
  this.parentNode.className = '';
  this.oldMouseUp = document.onmouseup;
  var c = 'hide_selects_cal_'+(this.ibOb=='outbound'?'o':'i');
  if(document.body.className.indexOf(c)<0) {
    document.body.className = c+' '+document.body.className;
  }
  document.addEvent('mouseup',this.checkClick.bind(this));
}

CalendarWidget.prototype.checkClick = function(e) {
  var p = e.srcElement || e.target;
  while(p && p != document && p != this.parentNode) {
    p = p.parentNode;
  }
  if(!p) { return; }
  if(p == document) { this.hide(); }
  return;
}

CalendarWidget.prototype.dayClick = function(box_id) {
  if(this.boxFirst>0 && this.boxFirst>box_id) return;
  if(this.boxLast>0 && this.boxLast<box_id) return;
  var d = 1+box_id-this.displayedMonthStart;
  var month = this.displayedMonth;
  var year = this.displayedYear;
  var dMonth = this.numDaysInMonth[year][month-1];
  if(d<=dMonth) {
    if(d<=0) {
      month--;
      if(month<1) { month=12;year--; }
      d = this.numDaysInMonth[year][month-1]+(d);
    }
  } else {
    d = (d-dMonth);
    month++;
    if(month>12) { month=1; year++; }
  }
  this.selectDate(year,month,d);
  this.hide();
  if(this.onSelChange) {
    this.onSelChange(year,parseInt(month,10),parseInt(d,10),0);
  }
  return false;
}

CalendarWidget.prototype.monthClick = function(relative) {
  var month = this.displayedMonth+relative;
  var year = this.displayedYear;
  if(month<1) {
    month = 12; year--;
  }
  if(month>12) {
    month = month-12; year++;
  }
  unselect_selection();
  if(relative < 1) {
    if(year < this.firstYear || (year == this.firstYear && month < this.firstMonth)) { return false; }
  } else {
    if(year > this.lastYear || (year == this.lastYear && month > this.lastMonth)) { return false; }
  }
  this.showMonth(year,month);
  return false;
}

CalendarWidget.prototype.render = function(widget) {
  /* do this with DHTML, can then update fairly easily... */
  var div = document.createElement('DIV');
  div.className = 'calendarWidget';
  div.onselectstart = function() { return(false); };
  var tab = document.createElement('TABLE');
  var el = document.createElement('TBODY');
  tab.appendChild(el);
  var cap = document.createElement('div');
  cap.className = 'header';
  var left = document.createElement('a');
  left.className = 'leftMonth';
  left.innerHTML = '&lt;&lt;';
  left.onclick = this.monthClick.bind(this,-1);
  this.leftMonth = left;
  var right = document.createElement('a');
  right.className = 'rightMonth';
  right.innerHTML = '&gt;&gt;';
  right.onclick = this.monthClick.bind(this,1);
  this.rightMonth = right;
  this.monthHeader = document.createElement('span');
  this.monthHeader.className = 'month_header';
  cap.appendChild(left);cap.appendChild(right);cap.appendChild(this.monthHeader);
  div.appendChild(cap);

  var tr = document.createElement('TR'); el.appendChild(tr);
  tr.className = 'days_of_week';
  dayOfWeek = ['S','M','T','W','T','F','S'];
  for(var i=0;i<7;i++) {
    var d = document.createElement('td');
    d.innerHTML = dayOfWeek[i];
    tr.appendChild(d);
  }

  for(var i=0;i<42;i++) {
    if(!(i%7)) { var tr = document.createElement('TR'); el.appendChild(tr); }
    var td = document.createElement('TD');
    var a = document.createElement('A');
    a.onclick = this.dayClick.bind(this,i);
    a.href = '#';
    this.boxes[i] = a;
    td.appendChild(a);
    tr.appendChild(td);
  }
  div.appendChild(tab);
  widget.appendChild(div);
  this.rendered = 1;
}

CalendarWidget.prototype.renderMonth = function(year,month,firstDay,days) {
  /* requires days rather than daysofmonth since this may affect previous/next month
    months 12/1 are 'non variant' so we can wrap forward/back on years without trouble */
  var str = '';
  if(!this.rendered) { this.render(this.parentNode); }
  // render last month first
  var boxId=0;
  var dMonth = days[month-1];
  if(month>1) {
    var lastMonth = month-1; var lastYear = year;
  } else { var lastMonth = 12; var lastYear = year-1; }
  var dLastMonth = days[lastMonth-1];
  if(month<12) {
    var nextMonth = month+1;  var nextYear = year;
  } else { var nextMonth = 1;  var nextYear = year+1; }

  var firstCell = 0;

  if(lastYear < this.firstYear || (lastYear == this.firstYear && lastMonth < this.firstMonth)) {
    this.leftMonth.innerHTML = '&nbsp;&nbsp;&nbsp;'; firstCell = (dLastMonth-this.firstDay)+firstDay;
  } else { this.leftMonth.innerHTML = '&lt;&lt;'; }

  if(nextYear > this.lastYear || (nextYear == this.lastYear && nextMonth > this.lastMonth)) {
    this.rightMonth.innerHTML = '&nbsp;&nbsp;&nbsp;';
  } else { this.rightMonth.innerHTML = '&gt;&gt;'; }
  this.monthHeader.innerHTML = this.monthNames[month]+' '+year;
  if(year == this.firstYear && month == this.firstMonth) {
    firstCell = firstDay+this.firstDay-1;
  } else {
    if((year == this.firstYear && month == this.firstMonth+1) || (month==1 && this.firstMonth==12 && this.firstYear == year-1)) {
      // the first date is last month, so firstCell should be set..
      firstCell = (this.firstDay-dLastMonth)+firstDay-1;
      if(firstCell<0) { firstCell = 0; }
    }
  }
  // is the selected date within the various months?
  var selectedCell = -3;
  if((this.selectedYear == year) && this.selectedMonth == month) {
    selectedCell = firstDay+this.selectedDay-1;
  }
  if(selectedCell<0 && this.selectedYear == lastYear && this.selectedMonth == lastMonth) {
    selectedCell = (this.selectedDay-dLastMonth)+firstDay-1;
  }
  if(selectedCell<0 && this.selectedYear == nextYear && this.selectedMonth == nextMonth) {
    selectedCell = (firstDay+dMonth+this.selectedDay)-1;
  }
  if(firstDay) {
    for(boxId=0;boxId<firstDay;boxId++) {
      box = this.boxes[boxId];
      box.innerHTML = dLastMonth-(firstDay-(boxId+1));
      if(boxId < firstCell) { box.className = 'otherMonth disabled'; continue; }
      box.className = 'otherMonth';
    }
  }
  if(firstCell) {
    this.boxFirst = firstCell;
  } else { this.boxFirst = -1; }
  // 'this' month
  for(var day=1;day<=dMonth;day++) {
    var box = this.boxes[boxId];
    box.innerHTML = day;
    if(boxId < firstCell) { box.className = 'currentMonth disabled'; boxId++; continue; }
    box.className = 'currentMonth';
    boxId++;
  }
  // 'next' month
  if(nextYear > this.lastYear || (nextYear == this.lastYear && nextMonth > this.lastMonth)) {
     this.boxLast = boxId-1;
     for(var day=1;boxId<42;boxId++) {
      box = this.boxes[boxId];
      box.innerHTML = day++;
      box.className = 'otherMonth disabled';
    }
  } else {
    this.boxLast = -1;
    for(var day=1;boxId<42;boxId++) {
      box = this.boxes[boxId];
      box.innerHTML = day++;
      box.className = 'otherMonth';
    }
  }
  if(selectedCell>=0 && selectedCell<42) { this.boxes[selectedCell].className += ' selected'; }
}

moz=document.getElementById&&!document.all
function unselect_selection(){
  if(!moz){
    oTextRange = document.selection.createRange()
    oTextRange.expand("word")
    oTextRange.execCommand("unselect")
  }
}

