
var _MN = new Array(
"Январь",
"Февраль",
"Март",
"Апрель",
"Май",
"Июнь",
"Июль",
"Август",
"Сентябрь",
"Октябрь",
"Ноябрь",
"Декабрь"
);
var _DN = new Array(
"Воскресенье",
"Понедельник",
"Вторник",
"Среда",
"Четверг",
"Пятница",
"Суббота",
"Воскресенье"
);
var _MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

var agt = navigator.userAgent.toLowerCase();
var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));

var _C = null;

function isRelated(el, evt) {
  var related = evt.relatedTarget;
  if (!related) {
    var type = evt.type;
    if (type == "mouseover") related = evt.fromElement;
    else if (type == "mouseout") related = evt.toElement;
  }
  while (related) {
    if (related == el) return true;
    related = related.parentNode;
  }
  return false;
}

function getMonthDays(year, month) {
  if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) return 29;
  else return _MD[month];
}

function removeClass(el, className) {
  if (!(el && el.className)) return;
  var classes = el.className.split(" ");
  var newClasses = new Array;
  for (i = 0; i < classes.length; ++i)
    if (classes[i] != className) newClasses[newClasses.length] = classes[i];
  el.className = newClasses.join(" ");
}

function addClass(el, className) {
  el.className += " " + className;
}

function getElement(ev) {
  if (is_ie) return window.event.srcElement;
  else return ev.currentTarget;
}

function getTargetElement(ev) {
  if (is_ie) return window.event.srcElement;
  else return ev.target;
}

function stopEvent(ev) {
  if (is_ie) {
    window.event.cancelBubble = true;
    window.event.returnValue = false;
  } else {
    ev.preventDefault();
    ev.stopPropagation();
  }
}

function addEvent(el, evname, func) {
  if (is_ie) el.attachEvent("on" + evname, func);
  else el.addEventListener(evname, func, true);
}

function removeEvent(el, evname, func) {
  if (is_ie) el.detachEvent("on" + evname, func);
  else el.removeEventListener(evname, func, true);
}

function findMonth(el) {
  if (typeof el.month != 'undefined') return el;
  else if (typeof el.parentNode.month != 'undefined') return el.parentNode;
  return null;
}

function findYear(el) {
  if (typeof el.year != 'undefined') return el;
  else if (typeof el.parentNode.year != 'undefined') return el.parentNode;
  return null;
}

function tableMouseUp(ev) {
  if (!_C) return;
  var cal = _C;
  if (cal.timeout) clearTimeout(cal.timeout);
  var el = cal.activeDateEl;
  if (!el) return;
  var target = getTargetElement(ev);
  removeClass(el, "active");
  if (target == el || target.parentNode == el) cellClick(el);
  var mon = findMonth(target);
  if (mon) {
    var date = new Date(cal.date);
    if (mon.month != date.getMonth()) {
      date.setMonth(mon.month);
      cal.setDate(date);
    }
  } else {
    var year = findYear(target);
    if (year) {
      var date = new Date(cal.date);
      if (year.year != date.getFullYear()) {
	date.setFullYear(year.year);
	cal.setDate(Date);
      }
    }
  }
  removeEvent(document, "mouseup", tableMouseUp);
  removeEvent(document, "mouseover", tableMouseOver);
  removeEvent(document, "mousemove", tableMouseOver);
  el = null;
  _C.hideCombos();
  _C = null;
  stopEvent(ev);
}

function tableMouseOver(ev) {
  if (!_C) return;
  var cal = _C;
  var el = cal.activeDateEl;
  var target = getTargetElement(ev);
  if (target == el || target.parentNode == el) addClass(el, "hilite active");
  else {
    removeClass(el, "active");
    removeClass(el, "hilite");
  }
  var mon = findMonth(target);
  if (mon) {
    var date = new Date(cal.date);
    if (mon.month != date.getMonth()) {
      if (cal.activeMonEl)
	removeClass(cal.activeMonEl, "hilite");
      addClass(mon, "hilite");
      cal.activeMonEl = mon;
    }
  } else {
    var year = findYear(target);
    if (year) {
      var date = new Date(cal.date);
      if (year.year != date.getFullYear()) {
	if (cal.activeYearEl)
	  removeClass(cal.activeYearEl, "hilite");
	addClass(year, "hilite");
	cal.activeYearEl = year;
      }
    }
  }
  stopEvent(ev);
}

function tableMouseDown(ev) { if (getTargetElement(ev) == getElement(ev)) stopEvent(ev); }

function showMonthsCombo() {
  if (!_C) return;
  var cal = _C;
  var cd = cal.activeDateEl;
  var mc = cal.monthsCombo;
  if (cal.activeMonEl)
    removeClass(cal.activeMonEl, "hilite");
  var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
  addClass(mon, "hilite");
  cal.activeMonEl = mon;
  mc.style.left = cd.offsetLeft;
  mc.style.top = cd.offsetTop + cd.offsetHeight;
  mc.style.visibility = "visible";
}

function showYearsCombo(fwd) {
  if (!_C) return;
  var cal = _C;
  var cd = cal.activeDateEl;
  var yc = cal.yearsCombo;
  if (cal.activeYearEl)
    removeClass(cal.activeYearEl, "hilite");
  cal.activeYearEl = null;
  var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
  var yr = yc.firstChild;
  var show = false;
  for (var i = 0; i < 12; ++i) {
    if (Y >= cal.minYear && Y <= cal.maxYear) {
      yr.firstChild.data = Y;
      yr.year = Y;
      yr.style.display = "block";
      show = true;
    } else yr.style.display = "none";
    yr = yr.nextSibling;
    Y += fwd ? 2 : -2;
  }
  if (show) {
    yc.style.left = cd.offsetLeft;
    yc.style.top = cd.offsetTop + cd.offsetHeight;
    yc.style.visibility = "visible";
  }
}

function dayMouseDown(ev) {
  var el = getElement(ev);
  var cal = el.calendar;
  addClass(el, "hilite active");
  cal.activeDateEl = el;
  _C = cal;
  addEvent(document, "mouseup", tableMouseUp);
  addEvent(document, "mouseover", tableMouseOver);
  addEvent(document, "mousemove", tableMouseOver);
  stopEvent(ev);
  if (el.navtype == -1 || el.navtype == 1)
    cal.timeout = setTimeout("showMonthsCombo()", 250);
  else if (el.navtype == -2 || el.navtype == 2)
    cal.timeout = setTimeout((el.navtype > 0) ? "showYearsCombo(true)" : "showYearsCombo(false)", 250);
  else cal.timeout = null;
}

function dayMouseDblClick(ev) { cellClick(getElement(ev)); if (is_ie) { document.selection.empty(); } }

function dayMouseOver(ev) {
  var el = getElement(ev);
  if (isRelated(el, ev) || _C) return;
  if (el.ttip) el.calendar.tooltips.firstChild.data = el.ttip;
  addClass(el, "hilite");
  stopEvent(ev);
}

function dayMouseOut(ev) {
  var el = getElement(ev);
  if (isRelated(el, ev) || _C) return;
  removeClass(el, "hilite");
  el.calendar.tooltips.firstChild.data = "";
  stopEvent(ev);
}

function datesEqual(a, b) {
  return a.getFullYear() == b.getFullYear() && a.getMonth() == b.getMonth() && a.getDate() == b.getDate();
}

function cellClick(el) {
  var cal = el.calendar;
  var closing = false;
  var newdate = false;
  if (typeof el.navtype == 'undefined') {
    removeClass(cal.currentDateEl, "today");
    addClass(el, "today");
    closing = cal.currentDateEl == el;
    if (!closing) cal.currentDateEl = el;
    var date = el.caldate;
    cal.date = date;
    newdate = true;
  } else {
    if (el.navtype == 200) {
      cal.callCloseHandler();
      return;
    }
    var date = (el.navtype == 0) ? new Date() : new Date(cal.date);
    var year = date.getFullYear();
    var mon = date.getMonth();
    var setMonth = function (mon) {
      var day = date.getDate();
      var max = getMonthDays(year, mon);
      if (day > max) date.setDate(max);
      date.setMonth(mon);
    }
    switch (el.navtype) {
    case -2:
      if (year > cal.minYear) date.setFullYear(year - 1);
      break;
    case -1:
      if (mon > 0) setMonth(mon - 1);
      else if (year-- > cal.minYear) {
	date.setFullYear(year);
	setMonth(11);
      }
      break;
    case 1:
      if (mon < 11) setMonth(mon + 1);
      else if (year < cal.maxYear) {
	date.setFullYear(year + 1);
	setMonth(0);
      }
      break;
    case 2:
      if (year < cal.maxYear) date.setFullYear(year + 1);
      break;
    case 100:
      cal.setMondayFirst(!cal.mondayFirst);
      return;
    }
    if (!datesEqual(date, cal.date)) {
      cal.setDate(date);
      newdate = el.navtype == 0;
    } else newdate = closing = el.navtype == 0;
  }
  if (newdate) cal.callHandler();
  if (closing) cal.callCloseHandler();
}

function Calendar(mondayFirst, dateStr, yourHandler, yourCloseHandler) {
  var parent = document.getElementsByTagName("body")[0];
  this.init = function (mondayFirst, date) {
    this.mondayFirst = mondayFirst;
    this.date = new Date(date);
    var month = date.getMonth();
    var mday = date.getDate();
    var year = date.getFullYear();
    var no_days = getMonthDays(year, month);
    date.setDate(1);
    var wday = date.getDay();
    var MON = mondayFirst ? 1 : 0;
    var SAT = mondayFirst ? 5 : 6;
    var SUN = mondayFirst ? 6 : 0;
    if (mondayFirst) wday = (wday > 0) ? (wday - 1) : 6;
    var iday = 1;
    var row = this.table.getElementsByTagName("tbody")[0].firstChild;
    for (var i = 0; i < 6; ++i, row = row.nextSibling) {
      var cell = row.firstChild;
      if (iday > no_days) { row.className = "emptyrow"; continue; }
      else row.className = "daysrow";
      for (var j = 0; j < 7; ++j, cell = cell.nextSibling) {
	if ((!i && j < wday) || iday > no_days)
	  cell.className = "emptycell";
	else {
	  cell.firstChild.data = iday;
	  cell.className = "day";
	  date.setDate(iday);
	  cell.caldate = new Date(date);
	  cell.ttip = _DN[wday + MON].substr(0, 3) + ", " + _MN[month].substr(0, 3) + " " + iday + ", " + year;
	  if (iday == mday) { addClass(cell, "today"); this.currentDateEl = cell; }
	  if (wday == SAT || wday == SUN) addClass(cell, "weekend");
	  ++wday; ++iday;
	  if (wday == 7) wday = 0;
	}
      }
    }
    this.title.firstChild.data = _MN[month].substr(0, 3) + ", " + year;
  }
  this.setDate = function (date) {
    if (!datesEqual(date, this.date)) this.init(this.mondayFirst, date);
  }
  this.setMondayFirst = function (mondayFirst) {
    this.init(mondayFirst, this.date);
    this.displayWeekdays();
  }
  this.displayWeekdays = function () {
    var thead = this.table.getElementsByTagName("thead")[0];
    var MON = this.mondayFirst ? 0 : 1;
    var SUN = this.mondayFirst ? 6 : 0;
    var SAT = this.mondayFirst ? 5 : 6;
    var cell = thead.getElementsByTagName("tr")[1].firstChild;
    for (var i = 0; i < 7; ++i, cell = cell.nextSibling) {
      if (!i)
	if (this.mondayFirst) cell.ttip = "";
	else cell.ttip = "";
      cell.className = "day name";
      if (i == SUN || i == SAT) addClass(cell, "weekend");
      cell.firstChild.data = _DN[i + 1 - MON].substr(0,2);
    }
  }
  this.setRange = function (a, z) {
    this.minYear = a;
    this.maxYear = z;
  }
  this.callHandler = function () {
    if (this.yourHandler) this.yourHandler(this, this.date.getDate()+"."+(1+this.date.getMonth())+"."+this.date.getFullYear());
  }
  this.callCloseHandler = function () {
    if (this.yourCloseHandler) this.yourCloseHandler(this);
  }
  this.destroy = function () {
    var el = this.element.parentNode;
    el.removeChild(this.element);
  }
  this.show = function () { this.element.style.visibility = "visible"; }
  this.hide = function () { this.element.style.visibility = "hidden"; }
  this.showAt = function (x, y) {
    var s = this.element.style;
    s.left = x + "px";
    s.top = y + "px";
    this.show();
  }
  this.showAtElement = function (el) {
    function Pos (x, y) {
      this.x = x;
      this.y = y;
    }
    function getPos(el) {
      var r = new Pos(el.offsetLeft, el.offsetTop);
      if (el.offsetParent) {
	var tmp = getPos(el.offsetParent);
	r.x += tmp.x;
	r.y += tmp.y;
      }
      return r;
    }
    var p = getPos(el);
    /* calendar position*/
    this.showAt(p.x + 5, p.y + el.offsetHeight + 5);
  }
  this.hideCombos = function () {
    this.monthsCombo.style.visibility = "hidden";
    this.yearsCombo.style.visibility = "hidden";
  }

  if (yourHandler) this.yourHandler = yourHandler;
  if (yourCloseHandler) this.yourCloseHandler = yourCloseHandler;

  this.setRange(1970, 2050);

  var date = null;
  if (dateStr) date = new Date(dateStr);
  else date = new Date();

  var table = document.createElement("table");
  this.table = table;
  table.cellSpacing = 0;
  table.cellPadding = 0;
  table.calendar = this;
  addEvent(table, "mousedown", tableMouseDown);

  var div = document.createElement("div");
  this.element = div;
  div.className = "calendar";
  div.style.position = "absolute";
  div.style.visibility = "hidden";
  div.appendChild(table);

  var thead = document.createElement("thead");
  table.appendChild(thead);
  var row = document.createElement("tr");
  thead.appendChild(row);
  row.className = "headrow";
  var cal = this;
  var cell = null;

  var add_evs = function (el) {
    addEvent(el, "mouseover", dayMouseOver);
    addEvent(el, "mousedown", dayMouseDown);
    addEvent(el, "mouseout", dayMouseOut);
    if (is_ie) {
      addEvent(el, "dblclick", dayMouseDblClick);
      el.setAttribute("unselectable", true);
    }
  }

  var hh = function (text, cs, navtype) {
    cell = document.createElement("td");
    row.appendChild(cell);
    if (cs != 1) { cell.colSpan = cs; cell.className = "title"; }
    else cell.className = "head";
    add_evs(cell);
    cell.calendar = cal;
    cell.navtype = navtype;
    if (text.substr(0, 1) != '&') cell.appendChild(document.createTextNode(text));
    else cell.innerHTML = text;
    return cell;
  }

  hh("&#x00ab;", 1, -2).ttip="Пред. год";
  hh("&#x2039;", 1, -1).ttip="Пред. месяц";
  this.title = hh("", 3, 0);
  this.title.ttip="Сегодня";
  hh("&#x203a;", 1, 1).ttip="След. месяц";
  hh("&#x00bb;", 1, 2).ttip="След. год";

  row = document.createElement("tr");
  thead.appendChild(row);
  row.className = "daynames";
  for (var i = 0; i < 7; ++i) {
    cell = document.createElement("td");
    row.appendChild(cell);
    cell.appendChild(document.createTextNode(""));
    if (!i) {
      cell.navtype = 100;
      cell.calendar = this;
      add_evs(cell);
    }
  }
  this.mondayFirst = mondayFirst;
  this.displayWeekdays();

  var tbody = document.createElement("tbody");
  table.appendChild(tbody);

  for (var i = 0; i < 6; ++i) {
    row = document.createElement("tr");
    tbody.appendChild(row);
    for (var j = 0; j < 7; ++j) {
      cell = document.createElement("td");
      row.appendChild(cell);
      cell.appendChild(document.createTextNode(""));
      cell.calendar = this;
      add_evs(cell);
    }
  }

  var tfoot = document.createElement("tfoot");
  table.appendChild(tfoot);
  row = document.createElement("tr");
  tfoot.appendChild(row);
  row.className = "footrow";
  for (var i = 0; i < 2; ++i) {
    cell = document.createElement("td");
    row.appendChild(cell);
    cell.calendar = this;
    if (!i) {
      this.tooltips = cell;
      cell.className = "ttip";
      cell.colSpan = 5;
      cell.appendChild(document.createTextNode(""));
    } else {
      cell.colSpan = 2;
      add_evs(cell);
      cell.appendChild(document.createTextNode("Закрыть"));
      cell.className = "button";
      cell.navtype = 200;
      cell.ttip = "";
    }
  }

  var div = document.createElement("div");
  this.monthsCombo = div;
  div.className = "combo";
  div.style.position = "absolute";
  div.style.visibility = "hidden";
  for (var i = 0; i < _MN.length; ++i) {
    var mn = document.createElement("div");
    mn.className = "label";
    mn.month = i;
    mn.appendChild(document.createTextNode(_MN[i].substr(0, 3)));
    div.appendChild(mn);
  }
  this.element.appendChild(div);

  div = document.createElement("div");
  this.yearsCombo = div;
  div.className = "combo";
  div.style.position = "absolute";
  div.style.visibility = "hidden";
  for (var i = 0; i < 12; ++i) {
    var yr = document.createElement("div");
    yr.className = "label";
    yr.appendChild(document.createTextNode(""));
    div.appendChild(yr);
  }
  this.element.appendChild(div);

  this.init(mondayFirst, date);
  parent.appendChild(this.element);
}
