[ Index ]

PHP Cross Reference of Limb3

title

Body

[close]

/calendar/shared/js/ -> calendar.js (source)

   1  /*  Copyright Mihai Bazon, 2002-2005  |  www.bazon.net/mishoo
   2   * -----------------------------------------------------------
   3   *
   4   * The DHTML Calendar, version 1.0 "It is happening again"
   5   *
   6   * Details and latest version at:
   7   * www.dynarch.com/projects/calendar
   8   *
   9   * This script is developed by Dynarch.com.  Visit us at www.dynarch.com.
  10   *
  11   * This script is distributed under the GNU Lesser General Public License.
  12   * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
  13   */
  14  
  15  // $Id: calendar.js,v 1.51 2005/03/07 16:44:31 mishoo Exp $
  16  
  17  /** The Calendar object constructor. */
  18  Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
  19      // member variables
  20      this.activeDiv = null;
  21      this.currentDateEl = null;
  22      this.getDateStatus = null;
  23      this.getDateToolTip = null;
  24      this.getDateText = null;
  25      this.timeout = null;
  26      this.onSelected = onSelected || null;
  27      this.onClose = onClose || null;
  28      this.dragging = false;
  29      this.hidden = false;
  30      this.minYear = 1970;
  31      this.maxYear = 2050;
  32      this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
  33      this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
  34      this.isPopup = true;
  35      this.weekNumbers = true;
  36      this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
  37      this.showsOtherMonths = false;
  38      this.dateStr = dateStr;
  39      this.ar_days = null;
  40      this.showsTime = false;
  41      this.time24 = true;
  42      this.yearStep = 2;
  43      this.hiliteToday = true;
  44      this.multiple = null;
  45      // HTML elements
  46      this.table = null;
  47      this.element = null;
  48      this.tbody = null;
  49      this.firstdayname = null;
  50      // Combo boxes
  51      this.monthsCombo = null;
  52      this.yearsCombo = null;
  53      this.hilitedMonth = null;
  54      this.activeMonth = null;
  55      this.hilitedYear = null;
  56      this.activeYear = null;
  57      // Information
  58      this.dateClicked = false;
  59  
  60      // one-time initializations
  61      if (typeof Calendar._SDN == "undefined") {
  62          // table of short day names
  63          if (typeof Calendar._SDN_len == "undefined")
  64              Calendar._SDN_len = 3;
  65          var ar = new Array();
  66          for (var i = 8; i > 0;) {
  67              ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
  68          }
  69          Calendar._SDN = ar;
  70          // table of short month names
  71          if (typeof Calendar._SMN_len == "undefined")
  72              Calendar._SMN_len = 3;
  73          ar = new Array();
  74          for (var i = 12; i > 0;) {
  75              ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
  76          }
  77          Calendar._SMN = ar;
  78      }
  79  };
  80  
  81  // ** constants
  82  
  83  /// "static", needed for event handlers.
  84  Calendar._C = null;
  85  
  86  /// detect a special case of "web browser"
  87  Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
  88             !/opera/i.test(navigator.userAgent) );
  89  
  90  Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
  91  
  92  /// detect Opera browser
  93  Calendar.is_opera = /opera/i.test(navigator.userAgent);
  94  
  95  /// detect KHTML-based browsers
  96  Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
  97  
  98  // BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
  99  //        library, at some point.
 100  
 101  Calendar.getAbsolutePos = function(el) {
 102      var SL = 0, ST = 0;
 103      var is_div = /^div$/i.test(el.tagName);
 104      if (is_div && el.scrollLeft)
 105          SL = el.scrollLeft;
 106      if (is_div && el.scrollTop)
 107          ST = el.scrollTop;
 108      var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
 109      if (el.offsetParent) {
 110          var tmp = this.getAbsolutePos(el.offsetParent);
 111          r.x += tmp.x;
 112          r.y += tmp.y;
 113      }
 114      return r;
 115  };
 116  
 117  Calendar.isRelated = function (el, evt) {
 118      var related = evt.relatedTarget;
 119      if (!related) {
 120          var type = evt.type;
 121          if (type == "mouseover") {
 122              related = evt.fromElement;
 123          } else if (type == "mouseout") {
 124              related = evt.toElement;
 125          }
 126      }
 127      while (related) {
 128          if (related == el) {
 129              return true;
 130          }
 131          related = related.parentNode;
 132      }
 133      return false;
 134  };
 135  
 136  Calendar.removeClass = function(el, className) {
 137      if (!(el && el.className)) {
 138          return;
 139      }
 140      var cls = el.className.split(" ");
 141      var ar = new Array();
 142      for (var i = cls.length; i > 0;) {
 143          if (cls[--i] != className) {
 144              ar[ar.length] = cls[i];
 145          }
 146      }
 147      el.className = ar.join(" ");
 148  };
 149  
 150  Calendar.addClass = function(el, className) {
 151      Calendar.removeClass(el, className);
 152      el.className += " " + className;
 153  };
 154  
 155  // FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
 156  Calendar.getElement = function(ev) {
 157      var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
 158      while (f.nodeType != 1 || /^div$/i.test(f.tagName))
 159          f = f.parentNode;
 160      return f;
 161  };
 162  
 163  Calendar.getTargetElement = function(ev) {
 164      var f = Calendar.is_ie ? window.event.srcElement : ev.target;
 165      while (f.nodeType != 1)
 166          f = f.parentNode;
 167      return f;
 168  };
 169  
 170  Calendar.stopEvent = function(ev) {
 171      ev || (ev = window.event);
 172      if (Calendar.is_ie) {
 173          ev.cancelBubble = true;
 174          ev.returnValue = false;
 175      } else {
 176          ev.preventDefault();
 177          ev.stopPropagation();
 178      }
 179      return false;
 180  };
 181  
 182  Calendar.addEvent = function(el, evname, func) {
 183      if (el.attachEvent) { // IE
 184          el.attachEvent("on" + evname, func);
 185      } else if (el.addEventListener) { // Gecko / W3C
 186          el.addEventListener(evname, func, true);
 187      } else {
 188          el["on" + evname] = func;
 189      }
 190  };
 191  
 192  Calendar.removeEvent = function(el, evname, func) {
 193      if (el.detachEvent) { // IE
 194          el.detachEvent("on" + evname, func);
 195      } else if (el.removeEventListener) { // Gecko / W3C
 196          el.removeEventListener(evname, func, true);
 197      } else {
 198          el["on" + evname] = null;
 199      }
 200  };
 201  
 202  Calendar.createElement = function(type, parent) {
 203      var el = null;
 204      if (document.createElementNS) {
 205          // use the XHTML namespace; IE won't normally get here unless
 206          // _they_ "fix" the DOM2 implementation.
 207          el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
 208      } else {
 209          el = document.createElement(type);
 210      }
 211      if (typeof parent != "undefined") {
 212          parent.appendChild(el);
 213      }
 214      return el;
 215  };
 216  
 217  // END: UTILITY FUNCTIONS
 218  
 219  // BEGIN: CALENDAR STATIC FUNCTIONS
 220  
 221  /** Internal -- adds a set of events to make some element behave like a button. */
 222  Calendar._add_evs = function(el) {
 223      with (Calendar) {
 224          addEvent(el, "mouseover", dayMouseOver);
 225          addEvent(el, "mousedown", dayMouseDown);
 226          addEvent(el, "mouseout", dayMouseOut);
 227          if (is_ie) {
 228              addEvent(el, "dblclick", dayMouseDblClick);
 229              el.setAttribute("unselectable", true);
 230          }
 231      }
 232  };
 233  
 234  Calendar.findMonth = function(el) {
 235      if (typeof el.month != "undefined") {
 236          return el;
 237      } else if (typeof el.parentNode.month != "undefined") {
 238          return el.parentNode;
 239      }
 240      return null;
 241  };
 242  
 243  Calendar.findYear = function(el) {
 244      if (typeof el.year != "undefined") {
 245          return el;
 246      } else if (typeof el.parentNode.year != "undefined") {
 247          return el.parentNode;
 248      }
 249      return null;
 250  };
 251  
 252  Calendar.showMonthsCombo = function () {
 253      var cal = Calendar._C;
 254      if (!cal) {
 255          return false;
 256      }
 257      var cal = cal;
 258      var cd = cal.activeDiv;
 259      var mc = cal.monthsCombo;
 260      if (cal.hilitedMonth) {
 261          Calendar.removeClass(cal.hilitedMonth, "hilite");
 262      }
 263      if (cal.activeMonth) {
 264          Calendar.removeClass(cal.activeMonth, "active");
 265      }
 266      var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
 267      Calendar.addClass(mon, "active");
 268      cal.activeMonth = mon;
 269      var s = mc.style;
 270      s.display = "block";
 271      if (cd.navtype < 0)
 272          s.left = cd.offsetLeft + "px";
 273      else {
 274          var mcw = mc.offsetWidth;
 275          if (typeof mcw == "undefined")
 276              // Konqueror brain-dead techniques
 277              mcw = 50;
 278          s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
 279      }
 280      s.top = (cd.offsetTop + cd.offsetHeight) + "px";
 281  };
 282  
 283  Calendar.showYearsCombo = function (fwd) {
 284      var cal = Calendar._C;
 285      if (!cal) {
 286          return false;
 287      }
 288      var cal = cal;
 289      var cd = cal.activeDiv;
 290      var yc = cal.yearsCombo;
 291      if (cal.hilitedYear) {
 292          Calendar.removeClass(cal.hilitedYear, "hilite");
 293      }
 294      if (cal.activeYear) {
 295          Calendar.removeClass(cal.activeYear, "active");
 296      }
 297      cal.activeYear = null;
 298      var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
 299      var yr = yc.firstChild;
 300      var show = false;
 301      for (var i = 12; i > 0; --i) {
 302          if (Y >= cal.minYear && Y <= cal.maxYear) {
 303              yr.innerHTML = Y;
 304              yr.year = Y;
 305              yr.style.display = "block";
 306              show = true;
 307          } else {
 308              yr.style.display = "none";
 309          }
 310          yr = yr.nextSibling;
 311          Y += fwd ? cal.yearStep : -cal.yearStep;
 312      }
 313      if (show) {
 314          var s = yc.style;
 315          s.display = "block";
 316          if (cd.navtype < 0)
 317              s.left = cd.offsetLeft + "px";
 318          else {
 319              var ycw = yc.offsetWidth;
 320              if (typeof ycw == "undefined")
 321                  // Konqueror brain-dead techniques
 322                  ycw = 50;
 323              s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
 324          }
 325          s.top = (cd.offsetTop + cd.offsetHeight) + "px";
 326      }
 327  };
 328  
 329  // event handlers
 330  
 331  Calendar.tableMouseUp = function(ev) {
 332      var cal = Calendar._C;
 333      if (!cal) {
 334          return false;
 335      }
 336      if (cal.timeout) {
 337          clearTimeout(cal.timeout);
 338      }
 339      var el = cal.activeDiv;
 340      if (!el) {
 341          return false;
 342      }
 343      var target = Calendar.getTargetElement(ev);
 344      ev || (ev = window.event);
 345      Calendar.removeClass(el, "active");
 346      if (target == el || target.parentNode == el) {
 347          Calendar.cellClick(el, ev);
 348      }
 349      var mon = Calendar.findMonth(target);
 350      var date = null;
 351      if (mon) {
 352          date = new Date(cal.date);
 353          if (mon.month != date.getMonth()) {
 354              date.setMonth(mon.month);
 355              cal.setDate(date);
 356              cal.dateClicked = false;
 357              cal.callHandler();
 358          }
 359      } else {
 360          var year = Calendar.findYear(target);
 361          if (year) {
 362              date = new Date(cal.date);
 363              if (year.year != date.getFullYear()) {
 364                  date.setFullYear(year.year);
 365                  cal.setDate(date);
 366                  cal.dateClicked = false;
 367                  cal.callHandler();
 368              }
 369          }
 370      }
 371      with (Calendar) {
 372          removeEvent(document, "mouseup", tableMouseUp);
 373          removeEvent(document, "mouseover", tableMouseOver);
 374          removeEvent(document, "mousemove", tableMouseOver);
 375          cal._hideCombos();
 376          _C = null;
 377          return stopEvent(ev);
 378      }
 379  };
 380  
 381  Calendar.tableMouseOver = function (ev) {
 382      var cal = Calendar._C;
 383      if (!cal) {
 384          return;
 385      }
 386      var el = cal.activeDiv;
 387      var target = Calendar.getTargetElement(ev);
 388      if (target == el || target.parentNode == el) {
 389          Calendar.addClass(el, "hilite active");
 390          Calendar.addClass(el.parentNode, "rowhilite");
 391      } else {
 392          if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
 393              Calendar.removeClass(el, "active");
 394          Calendar.removeClass(el, "hilite");
 395          Calendar.removeClass(el.parentNode, "rowhilite");
 396      }
 397      ev || (ev = window.event);
 398      if (el.navtype == 50 && target != el) {
 399          var pos = Calendar.getAbsolutePos(el);
 400          var w = el.offsetWidth;
 401          var x = ev.clientX;
 402          var dx;
 403          var decrease = true;
 404          if (x > pos.x + w) {
 405              dx = x - pos.x - w;
 406              decrease = false;
 407          } else
 408              dx = pos.x - x;
 409  
 410          if (dx < 0) dx = 0;
 411          var range = el._range;
 412          var current = el._current;
 413          var count = Math.floor(dx / 10) % range.length;
 414          for (var i = range.length; --i >= 0;)
 415              if (range[i] == current)
 416                  break;
 417          while (count-- > 0)
 418              if (decrease) {
 419                  if (--i < 0)
 420                      i = range.length - 1;
 421              } else if ( ++i >= range.length )
 422                  i = 0;
 423          var newval = range[i];
 424          el.innerHTML = newval;
 425  
 426          cal.onUpdateTime();
 427      }
 428      var mon = Calendar.findMonth(target);
 429      if (mon) {
 430          if (mon.month != cal.date.getMonth()) {
 431              if (cal.hilitedMonth) {
 432                  Calendar.removeClass(cal.hilitedMonth, "hilite");
 433              }
 434              Calendar.addClass(mon, "hilite");
 435              cal.hilitedMonth = mon;
 436          } else if (cal.hilitedMonth) {
 437              Calendar.removeClass(cal.hilitedMonth, "hilite");
 438          }
 439      } else {
 440          if (cal.hilitedMonth) {
 441              Calendar.removeClass(cal.hilitedMonth, "hilite");
 442          }
 443          var year = Calendar.findYear(target);
 444          if (year) {
 445              if (year.year != cal.date.getFullYear()) {
 446                  if (cal.hilitedYear) {
 447                      Calendar.removeClass(cal.hilitedYear, "hilite");
 448                  }
 449                  Calendar.addClass(year, "hilite");
 450                  cal.hilitedYear = year;
 451              } else if (cal.hilitedYear) {
 452                  Calendar.removeClass(cal.hilitedYear, "hilite");
 453              }
 454          } else if (cal.hilitedYear) {
 455              Calendar.removeClass(cal.hilitedYear, "hilite");
 456          }
 457      }
 458      return Calendar.stopEvent(ev);
 459  };
 460  
 461  Calendar.tableMouseDown = function (ev) {
 462      if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
 463          return Calendar.stopEvent(ev);
 464      }
 465  };
 466  
 467  Calendar.calDragIt = function (ev) {
 468      var cal = Calendar._C;
 469      if (!(cal && cal.dragging)) {
 470          return false;
 471      }
 472      var posX;
 473      var posY;
 474      if (Calendar.is_ie) {
 475          posY = window.event.clientY + document.body.scrollTop;
 476          posX = window.event.clientX + document.body.scrollLeft;
 477      } else {
 478          posX = ev.pageX;
 479          posY = ev.pageY;
 480      }
 481      cal.hideShowCovered();
 482      var st = cal.element.style;
 483      st.left = (posX - cal.xOffs) + "px";
 484      st.top = (posY - cal.yOffs) + "px";
 485      return Calendar.stopEvent(ev);
 486  };
 487  
 488  Calendar.calDragEnd = function (ev) {
 489      var cal = Calendar._C;
 490      if (!cal) {
 491          return false;
 492      }
 493      cal.dragging = false;
 494      with (Calendar) {
 495          removeEvent(document, "mousemove", calDragIt);
 496          removeEvent(document, "mouseup", calDragEnd);
 497          tableMouseUp(ev);
 498      }
 499      cal.hideShowCovered();
 500  };
 501  
 502  Calendar.dayMouseDown = function(ev) {
 503      var el = Calendar.getElement(ev);
 504      if (el.disabled) {
 505          return false;
 506      }
 507      var cal = el.calendar;
 508      cal.activeDiv = el;
 509      Calendar._C = cal;
 510      if (el.navtype != 300) with (Calendar) {
 511          if (el.navtype == 50) {
 512              el._current = el.innerHTML;
 513              addEvent(document, "mousemove", tableMouseOver);
 514          } else
 515              addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
 516          addClass(el, "hilite active");
 517          addEvent(document, "mouseup", tableMouseUp);
 518      } else if (cal.isPopup) {
 519          cal._dragStart(ev);
 520      }
 521      if (el.navtype == -1 || el.navtype == 1) {
 522          if (cal.timeout) clearTimeout(cal.timeout);
 523          cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
 524      } else if (el.navtype == -2 || el.navtype == 2) {
 525          if (cal.timeout) clearTimeout(cal.timeout);
 526          cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
 527      } else {
 528          cal.timeout = null;
 529      }
 530      return Calendar.stopEvent(ev);
 531  };
 532  
 533  Calendar.dayMouseDblClick = function(ev) {
 534      Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
 535      if (Calendar.is_ie) {
 536          document.selection.empty();
 537      }
 538  };
 539  
 540  Calendar.dayMouseOver = function(ev) {
 541      var el = Calendar.getElement(ev);
 542      if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
 543          return false;
 544      }
 545      if (el.ttip) {
 546          if (el.ttip.substr(0, 1) == "_") {
 547              el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
 548          }
 549          el.calendar.tooltips.innerHTML = el.ttip;
 550      }
 551      if (el.navtype != 300) {
 552          Calendar.addClass(el, "hilite");
 553          if (el.caldate) {
 554              Calendar.addClass(el.parentNode, "rowhilite");
 555          }
 556      }
 557      return Calendar.stopEvent(ev);
 558  };
 559  
 560  Calendar.dayMouseOut = function(ev) {
 561      with (Calendar) {
 562          var el = getElement(ev);
 563          if (isRelated(el, ev) || _C || el.disabled)
 564              return false;
 565          removeClass(el, "hilite");
 566          if (el.caldate)
 567              removeClass(el.parentNode, "rowhilite");
 568          if (el.calendar)
 569              el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
 570          return stopEvent(ev);
 571      }
 572  };
 573  
 574  /**
 575   *  A generic "click" handler :) handles all types of buttons defined in this
 576   *  calendar.
 577   */
 578  Calendar.cellClick = function(el, ev) {
 579      var cal = el.calendar;
 580      var closing = false;
 581      var newdate = false;
 582      var date = null;
 583      if (typeof el.navtype == "undefined") {
 584          if (cal.currentDateEl) {
 585              Calendar.removeClass(cal.currentDateEl, "selected");
 586              Calendar.addClass(el, "selected");
 587              closing = (cal.currentDateEl == el);
 588              if (!closing) {
 589                  cal.currentDateEl = el;
 590              }
 591          }
 592          cal.date.setDateOnly(el.caldate);
 593          date = cal.date;
 594          var other_month = !(cal.dateClicked = !el.otherMonth);
 595          if (!other_month && !cal.currentDateEl)
 596              cal._toggleMultipleDate(new Date(date));
 597          else
 598              newdate = !el.disabled;
 599          // a date was clicked
 600          if (other_month)
 601              cal._init(cal.firstDayOfWeek, date);
 602      } else {
 603          if (el.navtype == 200) {
 604              Calendar.removeClass(el, "hilite");
 605              cal.callCloseHandler();
 606              return;
 607          }
 608          date = new Date(cal.date);
 609          if (el.navtype == 0)
 610              date.setDateOnly(new Date()); // TODAY
 611          // unless "today" was clicked, we assume no date was clicked so
 612          // the selected handler will know not to close the calenar when
 613          // in single-click mode.
 614          // cal.dateClicked = (el.navtype == 0);
 615          cal.dateClicked = false;
 616          var year = date.getFullYear();
 617          var mon = date.getMonth();
 618  		function setMonth(m) {
 619              var day = date.getDate();
 620              var max = date.getMonthDays(m);
 621              if (day > max) {
 622                  date.setDate(max);
 623              }
 624              date.setMonth(m);
 625          };
 626          switch (el.navtype) {
 627              case 400:
 628              Calendar.removeClass(el, "hilite");
 629              var text = Calendar._TT["ABOUT"];
 630              if (typeof text != "undefined") {
 631                  text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
 632              } else {
 633                  // FIXME: this should be removed as soon as lang files get updated!
 634                  text = "Help and about box text is not translated into this language.\n" +
 635                      "If you know this language and you feel generous please update\n" +
 636                      "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
 637                      "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n" +
 638                      "Thank you!\n" +
 639                      "http://dynarch.com/mishoo/calendar.epl\n";
 640              }
 641              alert(text);
 642              return;
 643              case -2:
 644              if (year > cal.minYear) {
 645                  date.setFullYear(year - 1);
 646              }
 647              break;
 648              case -1:
 649              if (mon > 0) {
 650                  setMonth(mon - 1);
 651              } else if (year-- > cal.minYear) {
 652                  date.setFullYear(year);
 653                  setMonth(11);
 654              }
 655              break;
 656              case 1:
 657              if (mon < 11) {
 658                  setMonth(mon + 1);
 659              } else if (year < cal.maxYear) {
 660                  date.setFullYear(year + 1);
 661                  setMonth(0);
 662              }
 663              break;
 664              case 2:
 665              if (year < cal.maxYear) {
 666                  date.setFullYear(year + 1);
 667              }
 668              break;
 669              case 100:
 670              cal.setFirstDayOfWeek(el.fdow);
 671              return;
 672              case 50:
 673              var range = el._range;
 674              var current = el.innerHTML;
 675              for (var i = range.length; --i >= 0;)
 676                  if (range[i] == current)
 677                      break;
 678              if (ev && ev.shiftKey) {
 679                  if (--i < 0)
 680                      i = range.length - 1;
 681              } else if ( ++i >= range.length )
 682                  i = 0;
 683              var newval = range[i];
 684              el.innerHTML = newval;
 685              cal.onUpdateTime();
 686              return;
 687              case 0:
 688              // TODAY will bring us here
 689              if ((typeof cal.getDateStatus == "function") &&
 690                  cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
 691                  return false;
 692              }
 693              break;
 694          }
 695          if (!date.equalsTo(cal.date)) {
 696              cal.setDate(date);
 697              newdate = true;
 698          } else if (el.navtype == 0)
 699              newdate = closing = true;
 700      }
 701      if (newdate) {
 702          ev && cal.callHandler();
 703      }
 704      if (closing) {
 705          Calendar.removeClass(el, "hilite");
 706          ev && cal.callCloseHandler();
 707      }
 708  };
 709  
 710  // END: CALENDAR STATIC FUNCTIONS
 711  
 712  // BEGIN: CALENDAR OBJECT FUNCTIONS
 713  
 714  /**
 715   *  This function creates the calendar inside the given parent.  If _par is
 716   *  null than it creates a popup calendar inside the BODY element.  If _par is
 717   *  an element, be it BODY, then it creates a non-popup calendar (still
 718   *  hidden).  Some properties need to be set before calling this function.
 719   */
 720  Calendar.prototype.create = function (_par) {
 721      var parent = null;
 722      if (! _par) {
 723          // default parent is the document body, in which case we create
 724          // a popup calendar.
 725          parent = document.getElementsByTagName("body")[0];
 726          this.isPopup = true;
 727      } else {
 728          parent = _par;
 729          this.isPopup = false;
 730      }
 731      this.date = this.dateStr ? new Date(this.dateStr) : new Date();
 732  
 733      var table = Calendar.createElement("table");
 734      this.table = table;
 735      table.cellSpacing = 0;
 736      table.cellPadding = 0;
 737      table.calendar = this;
 738      Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
 739  
 740      var div = Calendar.createElement("div");
 741      this.element = div;
 742      div.className = "calendar";
 743      if (this.isPopup) {
 744          div.style.position = "absolute";
 745          div.style.display = "none";
 746      }
 747      div.appendChild(table);
 748  
 749      var thead = Calendar.createElement("thead", table);
 750      var cell = null;
 751      var row = null;
 752  
 753      var cal = this;
 754      var hh = function (text, cs, navtype) {
 755          cell = Calendar.createElement("td", row);
 756          cell.colSpan = cs;
 757          cell.className = "button";
 758          if (navtype != 0 && Math.abs(navtype) <= 2)
 759              cell.className += " nav";
 760          Calendar._add_evs(cell);
 761          cell.calendar = cal;
 762          cell.navtype = navtype;
 763          cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
 764          return cell;
 765      };
 766  
 767      row = Calendar.createElement("tr", thead);
 768      var title_length = 6;
 769      (this.isPopup) && --title_length;
 770      (this.weekNumbers) && ++title_length;
 771  
 772      hh("?", 1, 400).ttip = Calendar._TT["INFO"];
 773      this.title = hh("", title_length, 300);
 774      this.title.className = "title";
 775      if (this.isPopup) {
 776          this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
 777          this.title.style.cursor = "move";
 778          hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
 779      }
 780  
 781      row = Calendar.createElement("tr", thead);
 782      row.className = "headrow";
 783  
 784      this._nav_py = hh("&#x00ab;", 1, -2);
 785      this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
 786  
 787      this._nav_pm = hh("&#x2039;", 1, -1);
 788      this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
 789  
 790      this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
 791      this._nav_now.ttip = Calendar._TT["GO_TODAY"];
 792  
 793      this._nav_nm = hh("&#x203a;", 1, 1);
 794      this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
 795  
 796      this._nav_ny = hh("&#x00bb;", 1, 2);
 797      this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
 798  
 799      // day names
 800      row = Calendar.createElement("tr", thead);
 801      row.className = "daynames";
 802      if (this.weekNumbers) {
 803          cell = Calendar.createElement("td", row);
 804          cell.className = "name wn";
 805          cell.innerHTML = Calendar._TT["WK"];
 806      }
 807      for (var i = 7; i > 0; --i) {
 808          cell = Calendar.createElement("td", row);
 809          if (!i) {
 810              cell.navtype = 100;
 811              cell.calendar = this;
 812              Calendar._add_evs(cell);
 813          }
 814      }
 815      this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
 816      this._displayWeekdays();
 817  
 818      var tbody = Calendar.createElement("tbody", table);
 819      this.tbody = tbody;
 820  
 821      for (i = 6; i > 0; --i) {
 822          row = Calendar.createElement("tr", tbody);
 823          if (this.weekNumbers) {
 824              cell = Calendar.createElement("td", row);
 825          }
 826          for (var j = 7; j > 0; --j) {
 827              cell = Calendar.createElement("td", row);
 828              cell.calendar = this;
 829              Calendar._add_evs(cell);
 830          }
 831      }
 832  
 833      if (this.showsTime) {
 834          row = Calendar.createElement("tr", tbody);
 835          row.className = "time";
 836  
 837          cell = Calendar.createElement("td", row);
 838          cell.className = "time";
 839          cell.colSpan = 2;
 840          cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";
 841  
 842          cell = Calendar.createElement("td", row);
 843          cell.className = "time";
 844          cell.colSpan = this.weekNumbers ? 4 : 3;
 845  
 846          (function(){
 847  			function makeTimePart(className, init, range_start, range_end) {
 848                  var part = Calendar.createElement("span", cell);
 849                  part.className = className;
 850                  part.innerHTML = init;
 851                  part.calendar = cal;
 852                  part.ttip = Calendar._TT["TIME_PART"];
 853                  part.navtype = 50;
 854                  part._range = [];
 855                  if (typeof range_start != "number")
 856                      part._range = range_start;
 857                  else {
 858                      for (var i = range_start; i <= range_end; ++i) {
 859                          var txt;
 860                          if (i < 10 && range_end >= 10) txt = '0' + i;
 861                          else txt = '' + i;
 862                          part._range[part._range.length] = txt;
 863                      }
 864                  }
 865                  Calendar._add_evs(part);
 866                  return part;
 867              };
 868              var hrs = cal.date.getHours();
 869              var mins = cal.date.getMinutes();
 870              var t12 = !cal.time24;
 871              var pm = (hrs > 12);
 872              if (t12 && pm) hrs -= 12;
 873              var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
 874              var span = Calendar.createElement("span", cell);
 875              span.innerHTML = ":";
 876              span.className = "colon";
 877              var M = makeTimePart("minute", mins, 0, 59);
 878              var AP = null;
 879              cell = Calendar.createElement("td", row);
 880              cell.className = "time";
 881              cell.colSpan = 2;
 882              if (t12)
 883                  AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
 884              else
 885                  cell.innerHTML = "&nbsp;";
 886  
 887              cal.onSetTime = function() {
 888                  var pm, hrs = this.date.getHours(),
 889                      mins = this.date.getMinutes();
 890                  if (t12) {
 891                      pm = (hrs >= 12);
 892                      if (pm) hrs -= 12;
 893                      if (hrs == 0) hrs = 12;
 894                      AP.innerHTML = pm ? "pm" : "am";
 895                  }
 896                  H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
 897                  M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
 898              };
 899  
 900              cal.onUpdateTime = function() {
 901                  var date = this.date;
 902                  var h = parseInt(H.innerHTML, 10);
 903                  if (t12) {
 904                      if (/pm/i.test(AP.innerHTML) && h < 12)
 905                          h += 12;
 906                      else if (/am/i.test(AP.innerHTML) && h == 12)
 907                          h = 0;
 908                  }
 909                  var d = date.getDate();
 910                  var m = date.getMonth();
 911                  var y = date.getFullYear();
 912                  date.setHours(h);
 913                  date.setMinutes(parseInt(M.innerHTML, 10));
 914                  date.setFullYear(y);
 915                  date.setMonth(m);
 916                  date.setDate(d);
 917                  this.dateClicked = false;
 918                  this.callHandler();
 919              };
 920          })();
 921      } else {
 922          this.onSetTime = this.onUpdateTime = function() {};
 923      }
 924  
 925      var tfoot = Calendar.createElement("tfoot", table);
 926  
 927      row = Calendar.createElement("tr", tfoot);
 928      row.className = "footrow";
 929  
 930      cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
 931      cell.className = "ttip";
 932      if (this.isPopup) {
 933          cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
 934          cell.style.cursor = "move";
 935      }
 936      this.tooltips = cell;
 937  
 938      div = Calendar.createElement("div", this.element);
 939      this.monthsCombo = div;
 940      div.className = "combo";
 941      for (i = 0; i < Calendar._MN.length; ++i) {
 942          var mn = Calendar.createElement("div");
 943          mn.className = Calendar.is_ie ? "label-IEfix" : "label";
 944          mn.month = i;
 945          mn.innerHTML = Calendar._SMN[i];
 946          div.appendChild(mn);
 947      }
 948  
 949      div = Calendar.createElement("div", this.element);
 950      this.yearsCombo = div;
 951      div.className = "combo";
 952      for (i = 12; i > 0; --i) {
 953          var yr = Calendar.createElement("div");
 954          yr.className = Calendar.is_ie ? "label-IEfix" : "label";
 955          div.appendChild(yr);
 956      }
 957  
 958      this._init(this.firstDayOfWeek, this.date);
 959      parent.appendChild(this.element);
 960  };
 961  
 962  /** keyboard navigation, only for popup calendars */
 963  Calendar._keyEvent = function(ev) {
 964      var cal = window._dynarch_popupCalendar;
 965      if (!cal || cal.multiple)
 966          return false;
 967      (Calendar.is_ie) && (ev = window.event);
 968      var act = (Calendar.is_ie || ev.type == "keypress"),
 969          K = ev.keyCode;
 970      if (ev.ctrlKey) {
 971          switch (K) {
 972              case 37: // KEY left
 973              act && Calendar.cellClick(cal._nav_pm);
 974              break;
 975              case 38: // KEY up
 976              act && Calendar.cellClick(cal._nav_py);
 977              break;
 978              case 39: // KEY right
 979              act && Calendar.cellClick(cal._nav_nm);
 980              break;
 981              case 40: // KEY down
 982              act && Calendar.cellClick(cal._nav_ny);
 983              break;
 984              default:
 985              return false;
 986          }
 987      } else switch (K) {
 988          case 32: // KEY space (now)
 989          Calendar.cellClick(cal._nav_now);
 990          break;
 991          case 27: // KEY esc
 992          act && cal.callCloseHandler();
 993          break;
 994          case 37: // KEY left
 995          case 38: // KEY up
 996          case 39: // KEY right
 997          case 40: // KEY down
 998          if (act) {
 999              var prev, x, y, ne, el, step;
1000              prev = K == 37 || K == 38;
1001              step = (K == 37 || K == 39) ? 1 : 7;
1002  			function setVars() {
1003                  el = cal.currentDateEl;
1004                  var p = el.pos;
1005                  x = p & 15;
1006                  y = p >> 4;
1007                  ne = cal.ar_days[y][x];
1008              };setVars();
1009  			function prevMonth() {
1010                  var date = new Date(cal.date);
1011                  date.setDate(date.getDate() - step);
1012                  cal.setDate(date);
1013              };
1014  			function nextMonth() {
1015                  var date = new Date(cal.date);
1016                  date.setDate(date.getDate() + step);
1017                  cal.setDate(date);
1018              };
1019              while (1) {
1020                  switch (K) {
1021                      case 37: // KEY left
1022                      if (--x >= 0)
1023                          ne = cal.ar_days[y][x];
1024                      else {
1025                          x = 6;
1026                          K = 38;
1027                          continue;
1028                      }
1029                      break;
1030                      case 38: // KEY up
1031                      if (--y >= 0)
1032                          ne = cal.ar_days[y][x];
1033                      else {
1034                          prevMonth();
1035                          setVars();
1036                      }
1037                      break;
1038                      case 39: // KEY right
1039                      if (++x < 7)
1040                          ne = cal.ar_days[y][x];
1041                      else {
1042                          x = 0;
1043                          K = 40;
1044                          continue;
1045                      }
1046                      break;
1047                      case 40: // KEY down
1048                      if (++y < cal.ar_days.length)
1049                          ne = cal.ar_days[y][x];
1050                      else {
1051                          nextMonth();
1052                          setVars();
1053                      }
1054                      break;
1055                  }
1056                  break;
1057              }
1058              if (ne) {
1059                  if (!ne.disabled)
1060                      Calendar.cellClick(ne);
1061                  else if (prev)
1062                      prevMonth();
1063                  else
1064                      nextMonth();
1065              }
1066          }
1067          break;
1068          case 13: // KEY enter
1069          if (act)
1070              Calendar.cellClick(cal.currentDateEl, ev);
1071          break;
1072          default:
1073          return false;
1074      }
1075      return Calendar.stopEvent(ev);
1076  };
1077  
1078  /**
1079   *  (RE)Initializes the calendar to the given date and firstDayOfWeek
1080   */
1081  Calendar.prototype._init = function (firstDayOfWeek, date) {
1082      var today = new Date(),
1083          TY = today.getFullYear(),
1084          TM = today.getMonth(),
1085          TD = today.getDate();
1086      this.table.style.visibility = "hidden";
1087      var year = date.getFullYear();
1088      if (year < this.minYear) {
1089          year = this.minYear;
1090          date.setFullYear(year);
1091      } else if (year > this.maxYear) {
1092          year = this.maxYear;
1093          date.setFullYear(year);
1094      }
1095      this.firstDayOfWeek = firstDayOfWeek;
1096      this.date = new Date(date);
1097      var month = date.getMonth();
1098      var mday = date.getDate();
1099      var no_days = date.getMonthDays();
1100  
1101      // calendar voodoo for computing the first day that would actually be
1102      // displayed in the calendar, even if