3 Modification information for LGPL compliance
5 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
7 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
9 r56972 - 2010-06-15 19:49:32 -0700 (Tue, 15 Jun 2010) - sadek - Bug 37984: Workaround for a Google chrome bug. Still works in other browsers, and now in chrome too. See http://code.google.com/p/chromium/issues/detail?id=38641
11 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
13 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3 tags and updated the build system
15 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
17 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
19 r47729 - 2009-05-29 14:12:28 -0700 (Fri, 29 May 2009) - dwheeler - 30560: Fixed a bug in calendar.js where it referenced the depreciated IE variable document.body.scrollTop which has been replaced by document.documentElement.scrollTop in IE7.
21 r42807 - 2008-12-29 11:16:59 -0800 (Mon, 29 Dec 2008) - dwong - Branch from trunk/sugarcrm r42806 to branches/tokyo/sugarcrm
23 r40171 - 2008-09-30 15:25:06 -0700 (Tue, 30 Sep 2008) - rob - Adds a shortcut to some old code that was working around a bug in very old browsers (NN4, IE5), left it alone for IE (so it would catch IE5)
24 FIXED: 25076, 25249, 25150
26 r26391 - 2007-09-04 18:50:23 -0700 (Tue, 04 Sep 2007) - dwheeler - Bug 14707:
27 Modified datetime field JSCalendar to be compatible with the YUI overlay panel.
29 r24533 - 2007-07-23 01:35:36 -0700 (Mon, 23 Jul 2007) - clee - Added fixes for IE javascript errors where calendar selection causes other form fields to be disabled/readonly.
34 /* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
35 * ------------------------------------------------------------------
37 * The DHTML Calendar, version 0.9.6 "Keep cool but don't freeze"
39 * Details and latest version at:
40 * http://dynarch.com/mishoo/calendar.epl
42 * This script is distributed under the GNU Lesser General Public License.
43 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
48 /** The Calendar object constructor. */
49 Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose, inputField) {
52 this.activeDiv = null;
53 this.currentDateEl = null;
54 this.getDateStatus = null;
56 this.onSelected = onSelected || null;
57 this.onClose = onClose || null;
58 this.dragging = false;
62 this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
63 this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
65 this.weekNumbers = true;
66 this.firstDayOfWeek = firstDayOfWeek; // 0 for Sunday, 1 for Monday, etc.
67 this.showsOtherMonths = false;
68 this.dateStr = dateStr;
70 this.showsTime = false;
77 this.firstdayname = null;
79 this.monthsCombo = null;
80 this.yearsCombo = null;
81 this.hilitedMonth = null;
82 this.activeMonth = null;
83 this.hilitedYear = null;
84 this.activeYear = null;
86 this.dateClicked = false;
87 this.inputField = inputField || null;
89 // one-time initializations
90 if (typeof Calendar._SDN == "undefined") {
91 // table of short day names
92 if (typeof Calendar._SDN_len == "undefined")
93 Calendar._SDN_len = 3;
95 for (var i = 8; i > 0;) {
96 ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
99 // table of short month names
100 if (typeof Calendar._SMN_len == "undefined")
101 Calendar._SMN_len = 3;
103 for (var i = 12; i > 0;) {
104 ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
112 /// "static", needed for event handlers.
115 if(typeof jscal_today != 'undefined') {;
116 Calendar.dateToday = jscal_today;
119 /// detect a special case of "web browser"
120 Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
121 !/opera/i.test(navigator.userAgent) );
123 Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
125 /// detect Opera browser
126 Calendar.is_opera = /opera/i.test(navigator.userAgent);
128 /// detect KHTML-based browsers
129 Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
131 // BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
132 // library, at some point.
134 Calendar.getAbsolutePos = function(el) {
136 var is_div = /^div$/i.test(el.tagName);
137 if (is_div && el.scrollLeft)
139 if (is_div && el.scrollTop)
141 var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
142 if (el.offsetParent) {
143 var tmp = this.getAbsolutePos(el.offsetParent);
150 Calendar.isRelated = function (el, evt) {
151 var related = evt.relatedTarget;
154 if (type == "mouseover") {
155 related = evt.fromElement;
156 } else if (type == "mouseout") {
157 related = evt.toElement;
164 related = related.parentNode;
169 Calendar.removeClass = function(el, className) {
170 if (!(el && el.className)) {
173 var cls = el.className.split(" ");
174 var ar = new Array();
175 for (var i = cls.length; i > 0;) {
176 if (cls[--i] != className) {
177 ar[ar.length] = cls[i];
180 el.className = ar.join(" ");
183 Calendar.addClass = function(el, className) {
184 Calendar.removeClass(el, className);
185 el.className += " " + className;
188 Calendar.getElement = function(ev) {
189 if (Calendar.is_ie) {
190 return window.event.srcElement;
192 return ev.currentTarget;
196 Calendar.getTargetElement = function(ev) {
197 if (Calendar.is_ie) {
198 return window.event.srcElement;
204 Calendar.stopEvent = function(ev) {
205 ev || (ev = window.event);
206 if (Calendar.is_ie) {
207 ev.cancelBubble = true;
208 ev.returnValue = false;
211 ev.stopPropagation();
216 Calendar.addEvent = function(el, evname, func) {
217 if (el.attachEvent) { // IE
218 el.attachEvent("on" + evname, func);
219 } else if (el.addEventListener) { // Gecko / W3C
220 el.addEventListener(evname, func, true);
222 el["on" + evname] = func;
226 Calendar.removeEvent = function(el, evname, func) {
227 if (el.detachEvent) { // IE
228 el.detachEvent("on" + evname, func);
229 } else if (el.removeEventListener) { // Gecko / W3C
230 el.removeEventListener(evname, func, true);
232 el["on" + evname] = null;
236 Calendar.createElement = function(type, parent) {
238 if (document.createElementNS) {
239 // use the XHTML namespace; IE won't normally get here unless
240 // _they_ "fix" the DOM2 implementation.
241 el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
243 el = document.createElement(type);
245 if (typeof parent != "undefined") {
246 parent.appendChild(el);
251 // END: UTILITY FUNCTIONS
253 // BEGIN: CALENDAR STATIC FUNCTIONS
255 /** Internal -- adds a set of events to make some element behave like a button. */
256 Calendar._add_evs = function(el) {
258 addEvent(el, "mouseover", dayMouseOver);
259 addEvent(el, "mousedown", dayMouseDown);
260 addEvent(el, "mouseout", dayMouseOut);
262 addEvent(el, "dblclick", dayMouseDblClick);
263 el.setAttribute("unselectable", true);
268 Calendar.findMonth = function(el) {
269 if (typeof el.month != "undefined") {
271 } else if (typeof el.parentNode.month != "undefined") {
272 return el.parentNode;
277 Calendar.findYear = function(el) {
278 if (typeof el.year != "undefined") {
280 } else if (typeof el.parentNode.year != "undefined") {
281 return el.parentNode;
286 Calendar.showMonthsCombo = function () {
287 var cal = Calendar._C;
292 var cd = cal.activeDiv;
293 var mc = cal.monthsCombo;
294 if (cal.hilitedMonth) {
295 Calendar.removeClass(cal.hilitedMonth, "hilite");
297 if (cal.activeMonth) {
298 Calendar.removeClass(cal.activeMonth, "active");
300 var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
301 Calendar.addClass(mon, "active");
302 cal.activeMonth = mon;
306 s.left = cd.offsetLeft + "px";
308 var mcw = mc.offsetWidth;
309 if (typeof mcw == "undefined")
310 // Konqueror brain-dead techniques
312 s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
314 s.top = (cd.offsetTop + cd.offsetHeight) + "px";
317 Calendar.showYearsCombo = function (fwd) {
318 var cal = Calendar._C;
323 var cd = cal.activeDiv;
324 var yc = cal.yearsCombo;
325 if (cal.hilitedYear) {
326 Calendar.removeClass(cal.hilitedYear, "hilite");
328 if (cal.activeYear) {
329 Calendar.removeClass(cal.activeYear, "active");
331 cal.activeYear = null;
332 var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
333 var yr = yc.firstChild;
335 for (var i = 12; i > 0; --i) {
336 if (Y >= cal.minYear && Y <= cal.maxYear) {
337 yr.firstChild.data = Y;
339 yr.style.display = "block";
342 yr.style.display = "none";
345 Y += fwd ? cal.yearStep : -cal.yearStep;
351 s.left = cd.offsetLeft + "px";
353 var ycw = yc.offsetWidth;
354 if (typeof ycw == "undefined")
355 // Konqueror brain-dead techniques
357 s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
359 s.top = (cd.offsetTop + cd.offsetHeight) + "px";
365 Calendar.tableMouseUp = function(ev) {
366 var cal = Calendar._C;
371 clearTimeout(cal.timeout);
373 var el = cal.activeDiv;
377 var target = Calendar.getTargetElement(ev);
378 ev || (ev = window.event);
379 Calendar.removeClass(el, "active");
380 if (target == el || target.parentNode == el) {
381 Calendar.cellClick(el, ev);
383 var mon = Calendar.findMonth(target);
386 date = new Date(cal.date);
387 if (mon.month != date.getMonth()) {
388 date.setMonth(mon.month);
390 cal.dateClicked = false;
394 var year = Calendar.findYear(target);
396 date = new Date(cal.date);
397 if (year.year != date.getFullYear()) {
398 date.setFullYear(year.year);
400 cal.dateClicked = false;
406 removeEvent(document, "mouseup", tableMouseUp);
407 removeEvent(document, "mouseover", tableMouseOver);
408 removeEvent(document, "mousemove", tableMouseOver);
411 return stopEvent(ev);
415 Calendar.tableMouseOver = function (ev) {
416 var cal = Calendar._C;
420 var el = cal.activeDiv;
421 var target = Calendar.getTargetElement(ev);
422 if (target == el || target.parentNode == el) {
423 Calendar.addClass(el, "hilite active");
424 Calendar.addClass(el.parentNode, "rowhilite");
426 if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
427 Calendar.removeClass(el, "active");
428 Calendar.removeClass(el, "hilite");
429 Calendar.removeClass(el.parentNode, "rowhilite");
431 ev || (ev = window.event);
432 if (el.navtype == 50 && target != el) {
433 var pos = Calendar.getAbsolutePos(el);
434 var w = el.offsetWidth;
445 var range = el._range;
446 var current = el._current;
447 var count = Math.floor(dx / 10) % range.length;
448 for (var i = range.length; --i >= 0;)
449 if (range[i] == current)
454 i = range.length - 1;
455 } else if ( ++i >= range.length )
457 var newval = range[i];
458 el.firstChild.data = newval;
462 var mon = Calendar.findMonth(target);
464 if (mon.month != cal.date.getMonth()) {
465 if (cal.hilitedMonth) {
466 Calendar.removeClass(cal.hilitedMonth, "hilite");
468 Calendar.addClass(mon, "hilite");
469 cal.hilitedMonth = mon;
470 } else if (cal.hilitedMonth) {
471 Calendar.removeClass(cal.hilitedMonth, "hilite");
474 if (cal.hilitedMonth) {
475 Calendar.removeClass(cal.hilitedMonth, "hilite");
477 var year = Calendar.findYear(target);
479 if (year.year != cal.date.getFullYear()) {
480 if (cal.hilitedYear) {
481 Calendar.removeClass(cal.hilitedYear, "hilite");
483 Calendar.addClass(year, "hilite");
484 cal.hilitedYear = year;
485 } else if (cal.hilitedYear) {
486 Calendar.removeClass(cal.hilitedYear, "hilite");
488 } else if (cal.hilitedYear) {
489 Calendar.removeClass(cal.hilitedYear, "hilite");
492 return Calendar.stopEvent(ev);
495 Calendar.tableMouseDown = function (ev) {
496 if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
497 return Calendar.stopEvent(ev);
501 Calendar.calDragIt = function (ev) {
502 var cal = Calendar._C;
503 if (!(cal && cal.dragging)) {
508 if (Calendar.is_ie) {
509 posY = window.event.clientY + document.body.scrollTop;
510 posX = window.event.clientX + document.body.scrollLeft;
515 cal.hideShowCovered();
516 var st = cal.element.style;
517 st.left = (posX - cal.xOffs) + "px";
518 st.top = (posY - cal.yOffs) + "px";
519 return Calendar.stopEvent(ev);
522 Calendar.calDragEnd = function (ev) {
523 var cal = Calendar._C;
527 cal.dragging = false;
529 removeEvent(document, "mousemove", calDragIt);
530 removeEvent(document, "mouseup", calDragEnd);
533 cal.hideShowCovered();
536 Calendar.dayMouseDown = function(ev) {
537 var el = Calendar.getElement(ev);
541 var cal = el.calendar;
544 if (el.navtype != 300) with (Calendar) {
545 if (el.navtype == 50) {
546 el._current = el.firstChild.data;
547 addEvent(document, "mousemove", tableMouseOver);
549 addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
550 addClass(el, "hilite active");
551 addEvent(document, "mouseup", tableMouseUp);
552 } else if (cal.isPopup) {
555 if (el.navtype == -1 || el.navtype == 1) {
556 if (cal.timeout) clearTimeout(cal.timeout);
557 cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
558 } else if (el.navtype == -2 || el.navtype == 2) {
559 if (cal.timeout) clearTimeout(cal.timeout);
560 cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
564 return Calendar.stopEvent(ev);
567 Calendar.dayMouseDblClick = function(ev) {
568 Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
569 if (Calendar.is_ie) {
570 document.selection.empty();
574 Calendar.dayMouseOver = function(ev) {
575 var el = Calendar.getElement(ev);
576 if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
580 if (el.ttip.substr(0, 1) == "_") {
581 el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
583 el.calendar.tooltips.firstChild.data = el.ttip;
585 if (el.navtype != 300) {
586 Calendar.addClass(el, "hilite");
588 Calendar.addClass(el.parentNode, "rowhilite");
591 return Calendar.stopEvent(ev);
594 Calendar.dayMouseOut = function(ev) {
596 var el = getElement(ev);
597 if (isRelated(el, ev) || _C || el.disabled) {
600 removeClass(el, "hilite");
602 removeClass(el.parentNode, "rowhilite");
604 el.calendar.tooltips.firstChild.data = _TT["SEL_DATE"];
605 return stopEvent(ev);
610 * A generic "click" handler :) handles all types of buttons defined in this
613 Calendar.cellClick = function(el, ev) {
614 var cal = el.calendar;
618 if (typeof el.navtype == "undefined") {
619 Calendar.removeClass(cal.currentDateEl, "selected");
620 Calendar.addClass(el, "selected");
621 closing = (cal.currentDateEl == el);
623 cal.currentDateEl = el;
625 cal.date = new Date(el.caldate);
628 // a date was clicked
629 if (!(cal.dateClicked = !el.otherMonth))
630 cal._init(cal.firstDayOfWeek, date);
632 if (el.navtype == 200) {
633 Calendar.removeClass(el, "hilite");
634 cal.callCloseHandler();
637 if(el.navtype == 0) {
638 if(typeof Calendar.dateToday != 'undefined') date = new Date(parseFloat(Calendar.dateToday)); // new today's date based off params
639 else date = new Date();
642 date = new Date(cal.date);
644 // unless "today" was clicked, we assume no date was clicked so
645 // the selected handler will know not to close the calenar when
646 // in single-click mode.
647 // cal.dateClicked = (el.navtype == 0);
648 cal.dateClicked = false;
649 var year = date.getFullYear();
650 var mon = date.getMonth();
651 function setMonth(m) {
652 var day = date.getDate();
653 var max = date.getMonthDays(m);
659 switch (el.navtype) {
661 Calendar.removeClass(el, "hilite");
662 var text = Calendar._TT["ABOUT"];
663 if (typeof text != "undefined") {
664 text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
666 // FIXME: this should be removed as soon as lang files get updated!
667 text = "Help and about box text is not translated into this language.\n" +
668 "If you know this language and you feel generous please update\n" +
669 "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
670 "and send it back to <mishoo@infoiasi.ro> to get it into the distribution ;-)\n\n" +
672 "http://dynarch.com/mishoo/calendar.epl\n";
677 if (year > cal.minYear) {
678 date.setFullYear(year - 1);
684 } else if (year-- > cal.minYear) {
685 date.setFullYear(year);
692 } else if (year < cal.maxYear) {
693 date.setFullYear(year + 1);
698 if (year < cal.maxYear) {
699 date.setFullYear(year + 1);
703 cal.setFirstDayOfWeek(el.fdow);
706 var range = el._range;
707 var current = el.firstChild.data;
708 for (var i = range.length; --i >= 0;)
709 if (range[i] == current)
711 if (ev && ev.shiftKey) {
713 i = range.length - 1;
714 } else if ( ++i >= range.length )
716 var newval = range[i];
717 el.firstChild.data = newval;
721 // TODAY will bring us here
722 if ((typeof cal.getDateStatus == "function") && cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
723 // remember, "date" was previously set to new
724 // Date() if TODAY was clicked; thus, it
725 // contains today date.
730 if (!date.equalsTo(cal.date)) {
739 Calendar.removeClass(el, "hilite");
740 cal.callCloseHandler();
744 // END: CALENDAR STATIC FUNCTIONS
746 // BEGIN: CALENDAR OBJECT FUNCTIONS
749 * This function creates the calendar inside the given parent. If _par is
750 * null than it creates a popup calendar inside the BODY element. If _par is
751 * an element, be it BODY, then it creates a non-popup calendar (still
752 * hidden). Some properties need to be set before calling this function.
754 Calendar.prototype.create = function (_par) {
757 // default parent is the document body, in which case we create
759 parent = document.getElementsByTagName("body")[0];
763 this.isPopup = false;
765 if(this.dateStr) this.date = new Date(this.dateStr)
766 else if(typeof Calendar.dateToday == 'undefined') this.date = new Date();
767 else this.date = new Date(Calendar.dateToday);
769 var table = Calendar.createElement("table");
771 table.cellSpacing = 0;
772 table.cellPadding = 0;
773 table.calendar = this;
774 Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
776 var div = Calendar.createElement("div");
779 div.className = "calendar";
781 div.style.position = "absolute";
782 div.style.display = "none";
783 div.style.zIndex = 12000;
787 div.appendChild(table);
789 var thead = Calendar.createElement("thead", table);
794 var hh = function (text, cs, navtype) {
795 cell = Calendar.createElement("td", row);
797 cell.className = "button";
798 if (navtype != 0 && Math.abs(navtype) <= 2)
799 cell.className += " nav";
800 Calendar._add_evs(cell);
802 cell.navtype = navtype;
803 if (text.substr(0, 1) != "&") {
804 cell.appendChild(document.createTextNode(text));
807 // FIXME: dirty hack for entities
808 cell.innerHTML = text;
813 row = Calendar.createElement("tr", thead);
814 var title_length = 6;
815 (this.isPopup) && --title_length;
816 (this.weekNumbers) && ++title_length;
818 hh("?", 1, 400).ttip = Calendar._TT["INFO"];
819 this.title = hh("", title_length, 300);
820 this.title.className = "title";
822 this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
823 this.title.style.cursor = "move";
824 hh("×", 1, 200).ttip = Calendar._TT["CLOSE"];
827 row = Calendar.createElement("tr", thead);
828 row.className = "headrow";
830 this._nav_py = hh("«", 1, -2);
831 this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
833 this._nav_pm = hh("‹", 1, -1);
834 this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
836 this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
837 this._nav_now.ttip = Calendar._TT["GO_TODAY"];
839 this._nav_nm = hh("›", 1, 1);
840 this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
842 this._nav_ny = hh("»", 1, 2);
843 this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
846 row = Calendar.createElement("tr", thead);
847 row.className = "daynames";
848 if (this.weekNumbers) {
849 cell = Calendar.createElement("td", row);
850 cell.className = "name wn";
851 cell.appendChild(document.createTextNode(Calendar._TT["WK"]));
853 for (var i = 7; i > 0; --i) {
854 cell = Calendar.createElement("td", row);
855 cell.appendChild(document.createTextNode(""));
858 cell.calendar = this;
859 Calendar._add_evs(cell);
862 this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
863 this._displayWeekdays();
865 var tbody = Calendar.createElement("tbody", table);
868 for (i = 6; i > 0; --i) {
869 row = Calendar.createElement("tr", tbody);
870 if (this.weekNumbers) {
871 cell = Calendar.createElement("td", row);
872 cell.appendChild(document.createTextNode(""));
874 for (var j = 7; j > 0; --j) {
875 cell = Calendar.createElement("td", row);
876 cell.appendChild(document.createTextNode(""));
877 cell.calendar = this;
878 Calendar._add_evs(cell);
882 if (this.showsTime) {
883 row = Calendar.createElement("tr", tbody);
884 row.className = "time";
886 cell = Calendar.createElement("td", row);
887 cell.className = "time";
889 cell.innerHTML = Calendar._TT["TIME"] || " ";
891 cell = Calendar.createElement("td", row);
892 cell.className = "time";
893 cell.colSpan = this.weekNumbers ? 4 : 3;
896 function makeTimePart(className, init, range_start, range_end) {
897 var part = Calendar.createElement("span", cell);
898 part.className = className;
899 part.appendChild(document.createTextNode(init));
901 part.ttip = Calendar._TT["TIME_PART"];
904 if (typeof range_start != "number")
905 part._range = range_start;
907 for (var i = range_start; i <= range_end; ++i) {
909 if (i < 10 && range_end >= 10) txt = '0' + i;
911 part._range[part._range.length] = txt;
914 Calendar._add_evs(part);
917 var hrs = cal.date.getHours();
918 var mins = cal.date.getMinutes();
919 var t12 = !cal.time24;
921 if (t12 && pm) hrs -= 12;
922 var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
923 var span = Calendar.createElement("span", cell);
924 span.appendChild(document.createTextNode(":"));
925 span.className = "colon";
926 var M = makeTimePart("minute", mins, 0, 59);
928 cell = Calendar.createElement("td", row);
929 cell.className = "time";
932 AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
934 cell.innerHTML = " ";
936 cal.onSetTime = function() {
937 var hrs = this.date.getHours();
938 var mins = this.date.getMinutes();
940 if (pm && t12) hrs -= 12;
941 H.firstChild.data = (hrs < 10) ? ("0" + hrs) : hrs;
942 M.firstChild.data = (mins < 10) ? ("0" + mins) : mins;
944 AP.firstChild.data = pm ? "pm" : "am";
947 cal.onUpdateTime = function() {
948 var date = this.date;
949 var h = parseInt(H.firstChild.data, 10);
951 if (/pm/i.test(AP.firstChild.data) && h < 12)
953 else if (/am/i.test(AP.firstChild.data) && h == 12)
956 var d = date.getDate();
957 var m = date.getMonth();
958 var y = date.getFullYear();
960 date.setMinutes(parseInt(M.firstChild.data, 10));
964 this.dateClicked = false;
969 this.onSetTime = this.onUpdateTime = function() {};
972 var tfoot = Calendar.createElement("tfoot", table);
974 row = Calendar.createElement("tr", tfoot);
975 row.className = "footrow";
977 cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
978 cell.className = "ttip";
980 cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
981 cell.style.cursor = "move";
983 this.tooltips = cell;
985 div = Calendar.createElement("div", this.element);
986 this.monthsCombo = div;
987 div.className = "combo";
988 for (i = 0; i < Calendar._MN.length; ++i) {
989 var mn = Calendar.createElement("div");
990 mn.className = Calendar.is_ie ? "label-IEfix" : "label";
992 mn.appendChild(document.createTextNode(Calendar._SMN[i]));
996 div = Calendar.createElement("div", this.element);
997 this.yearsCombo = div;
998 div.className = "combo";
999 for (i = 12; i > 0; --i) {
1000 var yr = Calendar.createElement("div");
1001 yr.className = Calendar.is_ie ? "label-IEfix" : "label";
1002 yr.appendChild(document.createTextNode(""));
1003 div.appendChild(yr);
1006 this._init(this.firstDayOfWeek, this.date);
1007 parent.appendChild(this.element);
1010 /** keyboard navigation, only for popup calendars */
1011 Calendar._keyEvent = function(ev) {
1012 if (!window.calendar) {
1015 (Calendar.is_ie) && (ev = window.event);
1016 var cal = window.calendar;
1017 var act = (Calendar.is_ie || ev.type == "keypress");
1019 switch (ev.keyCode) {
1020 case 37: // KEY left
1021 act && Calendar.cellClick(cal._nav_pm);
1024 act && Calendar.cellClick(cal._nav_py);
1026 case 39: // KEY right
1027 act && Calendar.cellClick(cal._nav_nm);
1029 case 40: // KEY down
1030 act && Calendar.cellClick(cal._nav_ny);
1035 } else switch (ev.keyCode) {
1036 case 32: // KEY space (now)
1037 Calendar.cellClick(cal._nav_now);
1040 act && cal.callCloseHandler();
1042 case 37: // KEY left
1044 case 39: // KEY right
1045 case 40: // KEY down
1047 var date = cal.date.getDate() - 1;
1048 var el = cal.currentDateEl;
1050 var prev = (ev.keyCode == 37) || (ev.keyCode == 38);
1051 switch (ev.keyCode) {
1052 case 37: // KEY left
1053 (--date >= 0) && (ne = cal.ar_days[date]);
1057 (date >= 0) && (ne = cal.ar_days[date]);
1059 case 39: // KEY right
1060 (++date < cal.ar_days.length) && (ne = cal.ar_days[date]);
1062 case 40: // KEY down
1064 (date < cal.ar_days.length) && (ne = cal.ar_days[date]);
1069 Calendar.cellClick(cal._nav_pm);
1071 Calendar.cellClick(cal._nav_nm);
1073 date = (prev) ? cal.date.getMonthDays() : 1;
1074 el = cal.currentDateEl;
1075 ne = cal.ar_days[date - 1];
1077 Calendar.removeClass(el, "selected");
1078 Calendar.addClass(ne, "selected");
1079 cal.date = new Date(ne.caldate);
1081 cal.currentDateEl = ne;
1084 case 13: // KEY enter
1093 return Calendar.stopEvent(ev);
1097 * (RE)Initializes the calendar to the given date and firstDayOfWeek
1099 Calendar.prototype._init = function (firstDayOfWeek, date) {
1100 if(typeof Calendar.dateToday == 'undefined') var today = new Date();
1101 else var today = new Date(parseFloat(Calendar.dateToday));
1103 this.table.style.visibility = "hidden";
1104 var year = date.getFullYear();
1105 if (year < this.minYear) {
1106 year = this.minYear;
1107 date.setFullYear(year);
1108 } else if (year > this.maxYear) {
1109 year = this.maxYear;
1110 date.setFullYear(year);
1112 this.firstDayOfWeek = firstDayOfWeek;
1113 this.date = new Date(date);
1114 var month = date.getMonth();
1115 var mday = date.getDate();
1116 var no_days = date.getMonthDays();
1118 // calendar voodoo for computing the first day that would actually be
1119 // displayed in the calendar, even if it's from the previous month.
1120 // WARNING: this is magic. ;-)
1122 var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
1125 // sb: workaround for google chrome bug. changed "-day1" to "-day1 || 0"
1126 date.setDate(-day1 || 0);
1127 date.setDate(date.getDate() + 1);
1129 var row = this.tbody.firstChild;
1130 var MN = Calendar._SMN[month];
1131 var ar_days = new Array();
1132 var weekend = Calendar._TT["WEEKEND"];
1133 for (var i = 0; i < 6; ++i, row = row.nextSibling) {
1134 var cell = row.firstChild;
1135 if (this.weekNumbers) {
1136 cell.className = "day wn";
1137 cell.firstChild.data = date.getWeekNumber();
1138 cell = cell.nextSibling;
1140 row.className = "daysrow";
1141 var hasdays = false;
1142 for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(date.getDate() + 1)) {
1143 var iday = date.getDate();
1144 var wday = date.getDay();
1145 cell.className = "day";
1146 var current_month = (date.getMonth() == month);
1147 if (!current_month) {
1148 if (this.showsOtherMonths) {
1149 cell.className += " othermonth";
1150 cell.otherMonth = true;
1152 cell.className = "emptycell";
1153 cell.innerHTML = " ";
1154 cell.disabled = true;
1158 cell.otherMonth = false;
1161 cell.disabled = false;
1162 cell.firstChild.data = iday;
1163 if (typeof this.getDateStatus == "function") {
1164 var status = this.getDateStatus(date, year, month, iday);
1165 if (status === true) {
1166 cell.className += " disabled";
1167 cell.disabled = true;
1169 if (/disabled/i.test(status))
1170 cell.disabled = true;
1171 cell.className += " " + status;
1174 if (!cell.disabled) {
1175 ar_days[ar_days.length] = cell;
1176 cell.caldate = new Date(date);
1178 if (current_month && iday == mday) {
1179 cell.className += " selected";
1180 this.currentDateEl = cell;
1182 if (date.getFullYear() == today.getFullYear() &&
1183 date.getMonth() == today.getMonth() &&
1184 iday == today.getDate()) {
1185 cell.className += " today";
1186 cell.ttip += Calendar._TT["PART_TODAY"];
1188 if (weekend.indexOf(wday.toString()) != -1) {
1189 cell.className += cell.otherMonth ? " oweekend" : " weekend";
1193 if (!(hasdays || this.showsOtherMonths))
1194 row.className = "emptyrow";
1196 this.ar_days = ar_days;
1197 this.title.firstChild.data = Calendar._MN[month] + ", " + year;
1199 this.table.style.visibility = "visible";
1201 // this.tooltips.firstChild.data = "Generated in " + ((new Date()) - today) + " ms";
1205 * Calls _init function above for going to a certain date (but only if the
1206 * date is different than the currently selected one).
1208 Calendar.prototype.setDate = function (date) {
1209 if (!date.equalsTo(this.date)) {
1210 this._init(this.firstDayOfWeek, date);
1215 * Refreshes the calendar. Useful if the "disabledHandler" function is
1216 * dynamic, meaning that the list of disabled date can change at runtime.
1217 * Just * call this function if you think that the list of disabled dates
1220 Calendar.prototype.refresh = function () {
1221 this._init(this.firstDayOfWeek, this.date);
1224 /** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
1225 Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
1226 this._init(firstDayOfWeek, this.date);
1227 this._displayWeekdays();
1231 * Allows customization of what dates are enabled. The "unaryFunction"
1232 * parameter must be a function object that receives the date (as a JS Date
1233 * object) and returns a boolean value. If the returned value is true then
1234 * the passed date will be marked as disabled.
1236 Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
1237 this.getDateStatus = unaryFunction;
1240 /** Customization of allowed year range for the calendar. */
1241 Calendar.prototype.setRange = function (a, z) {
1246 /** Calls the first user handler (selectedHandler). */
1247 Calendar.prototype.callHandler = function () {
1248 if (this.onSelected) {
1249 this.onSelected(this, this.date.print(this.dateFormat));
1253 /** Calls the second user handler (closeHandler). */
1254 Calendar.prototype.callCloseHandler = function () {
1258 this.hideShowCovered();
1261 /** Removes the calendar object from the DOM tree and destroys it. */
1262 Calendar.prototype.destroy = function () {
1263 var el = this.element.parentNode;
1264 el.removeChild(this.element);
1266 window.calendar = null;
1270 * Moves the calendar element to a different section in the DOM tree (changes
1273 Calendar.prototype.reparent = function (new_parent) {
1274 var el = this.element;
1275 el.parentNode.removeChild(el);
1276 new_parent.appendChild(el);
1279 // This gets called when the user presses a mouse button anywhere in the
1280 // document, if the calendar is shown. If the click was outside the open
1281 // calendar this function closes it.
1282 Calendar._checkCalendar = function(ev) {
1283 if (!window.calendar) {
1286 var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
1287 for (; el != null && el != calendar.element; el = el.parentNode);
1289 // calls closeHandler which should hide the calendar.
1290 window.calendar.callCloseHandler();
1291 return Calendar.stopEvent(ev);
1295 /** Shows the calendar. */
1296 Calendar.prototype.show = function () {
1297 if(this.inputField != null && !this.inputField.readOnly)
1299 var rows = this.table.getElementsByTagName("tr");
1300 for (var i = rows.length; i > 0;) {
1301 var row = rows[--i];
1302 Calendar.removeClass(row, "rowhilite");
1303 var cells = row.getElementsByTagName("td");
1304 for (var j = cells.length; j > 0;) {
1305 var cell = cells[--j];
1306 Calendar.removeClass(cell, "hilite");
1307 Calendar.removeClass(cell, "active");
1311 this.element.style.display = "block";
1312 this.hidden = false;
1314 window.calendar = this;
1315 Calendar.addEvent(document, "keydown", Calendar._keyEvent);
1316 Calendar.addEvent(document, "keypress", Calendar._keyEvent);
1317 Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
1319 this.hideShowCovered();
1324 * Hides the calendar. Also removes any "hilite" from the class of any TD
1327 Calendar.prototype.hide = function () {
1329 Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
1330 Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
1331 Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
1333 this.element.style.display = "none";
1335 this.hideShowCovered();
1339 * Shows the calendar at a given absolute position (beware that, depending on
1340 * the calendar element style -- position property -- this might be relative
1341 * to the parent's containing rectangle).
1343 Calendar.prototype.showAt = function (x, y) {
1344 var s = this.element.style;
1350 /** Shows the calendar near a given element. */
1351 Calendar.prototype.showAtElement = function (el, opts) {
1353 var p = Calendar.getAbsolutePos(el);
1354 if (!opts || typeof opts != "string") {
1355 this.showAt(p.x, p.y + el.offsetHeight);
1358 function fixPosition(box) {
1363 var cp = document.createElement("div");
1365 s.position = "absolute";
1366 s.right = s.bottom = s.width = s.height = "0px";
1367 document.body.appendChild(cp);
1368 var br = Calendar.getAbsolutePos(cp);
1369 document.body.removeChild(cp);
1370 if (Calendar.is_ie) {
1371 br.y += Math.max(document.body.scrollTop, document.documentElement.scrollTop);
1372 br.x += Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);
1374 br.y += window.scrollY;
1375 br.x += window.scrollX;
1377 var tmp = box.x + box.width - br.x;
1378 if (tmp > 0) box.x -= tmp;
1379 tmp = box.y + box.height - br.y;
1380 if (tmp > 0) box.y -= tmp;
1382 this.element.style.display = "block";
1383 Calendar.continuation_for_the_khtml_browser = function() {
1384 var w = self.element.offsetWidth;
1385 var h = self.element.offsetHeight;
1386 self.element.style.display = "none";
1387 var valign = opts.substr(0, 1);
1389 if (opts.length > 1) {
1390 halign = opts.substr(1, 1);
1392 // vertical alignment
1394 case "T": p.y -= h; break;
1395 case "B": p.y += el.offsetHeight; break;
1396 case "C": p.y += (el.offsetHeight - h) / 2; break;
1397 case "t": p.y += el.offsetHeight - h; break;
1398 case "b": break; // already there
1400 // horizontal alignment
1402 case "L": p.x -= w; break;
1403 case "R": p.x += el.offsetWidth; break;
1404 case "C": p.x += (el.offsetWidth - w) / 2; break;
1405 case "r": p.x += el.offsetWidth - w; break;
1406 case "l": break; // already there
1410 self.monthsCombo.style.display = "none";
1412 self.showAt(p.x, p.y);
1414 if (Calendar.is_khtml)
1415 setTimeout("Calendar.continuation_for_the_khtml_browser()", 10);
1417 Calendar.continuation_for_the_khtml_browser();
1420 /** Customizes the date format. */
1421 Calendar.prototype.setDateFormat = function (str) {
1422 this.dateFormat = str;
1425 /** Customizes the tooltip date format. */
1426 Calendar.prototype.setTtDateFormat = function (str) {
1427 this.ttDateFormat = str;
1431 * Tries to identify the date represented in a string. If successful it also
1432 * calls this.setDate which moves the calendar to the given date.
1434 Calendar.prototype.parseDate = function (str, fmt) {
1438 var a = str.split(/\W+/);
1440 fmt = this.dateFormat;
1442 var b = fmt.match(/%./g);
1446 for (i = 0; i < a.length; ++i) {
1452 d = parseInt(a[i], 10);
1456 m = parseInt(a[i], 10) - 1;
1461 y = parseInt(a[i], 10);
1462 (y < 100) && (y += (y > 29) ? 1900 : 2000);
1467 for (j = 0; j < 12; ++j) {
1468 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
1476 hr = parseInt(a[i], 10);
1481 if (/pm/i.test(a[i]) && hr < 12)
1486 min = parseInt(a[i], 10);
1490 if (y != 0 && m != -1 && d != 0) {
1491 this.setDate(new Date(y, m, d, hr, min, 0));
1494 y = 0; m = -1; d = 0;
1495 for (i = 0; i < a.length; ++i) {
1496 if (a[i].search(/[a-zA-Z]+/) != -1) {
1498 for (j = 0; j < 12; ++j) {
1499 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
1507 } else if (parseInt(a[i], 10) <= 12 && m == -1) {
1509 } else if (parseInt(a[i], 10) > 31 && y == 0) {
1510 y = parseInt(a[i], 10);
1511 (y < 100) && (y += (y > 29) ? 1900 : 2000);
1512 } else if (d == 0) {
1517 var today = new Date();
1518 y = today.getFullYear();
1520 if (m != -1 && d != 0) {
1521 this.setDate(new Date(y, m, d, hr, min, 0));
1525 Calendar.prototype.hideShowCovered = function () {
1526 if (!Calendar.is_ie) {
1527 // This is no longer needed for newer browsers, IE is left in here for the sake of IE5
1531 Calendar.continuation_for_the_khtml_browser = function() {
1532 function getVisib(obj){
1533 var value = obj.style.visibility;
1535 if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
1536 if (!Calendar.is_khtml)
1537 value = document.defaultView.
1538 getComputedStyle(obj, "").getPropertyValue("visibility");
1541 } else if (obj.currentStyle) { // IE
1542 value = obj.currentStyle.visibility;
1549 var tags = new Array("applet", "iframe", "select");
1550 var el = self.element;
1552 var p = Calendar.getAbsolutePos(el);
1554 var EX2 = el.offsetWidth + EX1;
1556 var EY2 = el.offsetHeight + EY1;
1558 for (var k = tags.length; k > 0; ) {
1559 var ar = document.getElementsByTagName(tags[--k]);
1562 for (var i = ar.length; i > 0;) {
1565 p = Calendar.getAbsolutePos(cc);
1567 var CX2 = cc.offsetWidth + CX1;
1569 var CY2 = cc.offsetHeight + CY1;
1571 if (self.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
1572 if (!cc.__msh_save_visibility) {
1573 cc.__msh_save_visibility = getVisib(cc);
1575 cc.style.visibility = cc.__msh_save_visibility;
1577 if (!cc.__msh_save_visibility) {
1578 cc.__msh_save_visibility = getVisib(cc);
1580 cc.style.visibility = "hidden";
1585 if (Calendar.is_khtml)
1586 setTimeout("Calendar.continuation_for_the_khtml_browser()", 10);
1588 Calendar.continuation_for_the_khtml_browser();
1591 /** Internal function; it displays the bar with the names of the weekday. */
1592 Calendar.prototype._displayWeekdays = function () {
1593 var fdow = this.firstDayOfWeek;
1594 var cell = this.firstdayname;
1595 var weekend = Calendar._TT["WEEKEND"];
1596 for (var i = 0; i < 7; ++i) {
1597 cell.className = "day name";
1598 var realday = (i + fdow) % 7;
1600 cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
1602 cell.calendar = this;
1603 cell.fdow = realday;
1604 Calendar._add_evs(cell);
1606 if (weekend.indexOf(realday.toString()) != -1) {
1607 Calendar.addClass(cell, "weekend");
1609 cell.firstChild.data = Calendar._SDN[(i + fdow) % 7];
1610 cell = cell.nextSibling;
1614 /** Internal function. Hides all combo boxes that might be displayed. */
1615 Calendar.prototype._hideCombos = function () {
1616 this.monthsCombo.style.display = "none";
1617 this.yearsCombo.style.display = "none";
1620 /** Internal function. Starts dragging the element. */
1621 Calendar.prototype._dragStart = function (ev) {
1622 if (this.dragging) {
1625 this.dragging = true;
1628 if (Calendar.is_ie) {
1629 posY = window.event.clientY + document.body.scrollTop;
1630 posX = window.event.clientX + document.body.scrollLeft;
1632 posY = ev.clientY + window.scrollY;
1633 posX = ev.clientX + window.scrollX;
1635 var st = this.element.style;
1636 this.xOffs = posX - parseInt(st.left);
1637 this.yOffs = posY - parseInt(st.top);
1639 addEvent(document, "mousemove", calDragIt);
1640 addEvent(document, "mouseup", calDragEnd);
1644 // BEGIN: DATE OBJECT PATCHES
1646 /** Adds the number of days array to the Date object. */
1647 Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
1649 /** Constants used for time computations */
1650 Date.SECOND = 1000 /* milliseconds */;
1651 Date.MINUTE = 60 * Date.SECOND;
1652 Date.HOUR = 60 * Date.MINUTE;
1653 Date.DAY = 24 * Date.HOUR;
1654 Date.WEEK = 7 * Date.DAY;
1656 /** Returns the number of days in the current month */
1657 Date.prototype.getMonthDays = function(month) {
1658 var year = this.getFullYear();
1659 if (typeof month == "undefined") {
1660 month = this.getMonth();
1662 if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
1665 return Date._MD[month];
1669 /** Returns the number of day in the year. */
1670 Date.prototype.getDayOfYear = function() {
1671 var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
1672 var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
1673 var time = now - then;
1674 return Math.floor(time / Date.DAY);
1677 /** Returns the number of the week in year, as defined in ISO 8601. */
1678 Date.prototype.getWeekNumber = function() {
1679 var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
1680 var DoW = d.getDay();
1681 d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
1682 var ms = d.valueOf(); // GMT
1684 d.setDate(4); // Thu in Week 1
1685 return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
1688 /** Checks dates equality (ignores time) */
1689 Date.prototype.equalsTo = function(date) {
1690 return ((this.getFullYear() == date.getFullYear()) &&
1691 (this.getMonth() == date.getMonth()) &&
1692 (this.getDate() == date.getDate()) &&
1693 (this.getHours() == date.getHours()) &&
1694 (this.getMinutes() == date.getMinutes()));
1697 /** Prints the date in a string according to the given format. */
1698 Date.prototype.print = function (str) {
1699 var m = this.getMonth();
1700 var d = this.getDate();
1701 var y = this.getFullYear();
1702 var wn = this.getWeekNumber();
1703 var w = this.getDay();
1705 var hr = this.getHours();
1706 var pm = (hr >= 12);
1707 var ir = (pm) ? (hr - 12) : hr;
1708 var dy = this.getDayOfYear();
1711 var min = this.getMinutes();
1712 var sec = this.getSeconds();
1713 s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
1714 s["%A"] = Calendar._DN[w]; // full weekday name
1715 s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
1716 s["%B"] = Calendar._MN[m]; // full month name
1717 // FIXME: %c : preferred date and time representation for the current locale
1718 s["%C"] = 1 + Math.floor(y / 100); // the century number
1719 s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
1720 s["%e"] = d; // the day of the month (range 1 to 31)
1721 // FIXME: %D : american date style: %m/%d/%y
1722 // FIXME: %E, %F, %G, %g, %h (man strftime)
1723 s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
1724 s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
1725 s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
1726 s["%k"] = hr; // hour, range 0 to 23 (24h format)
1727 s["%l"] = ir; // hour, range 1 to 12 (12h format)
1728 s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
1729 s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
1730 s["%n"] = "\n"; // a newline character
1731 s["%p"] = pm ? "PM" : "AM";
1732 s["%P"] = pm ? "pm" : "am";
1733 // FIXME: %r : the time in am/pm notation %I:%M:%S %p
1734 // FIXME: %R : the time in 24-hour notation %H:%M
1735 s["%s"] = Math.floor(this.getTime() / 1000);
1736 s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
1737 s["%t"] = "\t"; // a tab character
1738 // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
1739 s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
1740 s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
1741 s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
1742 // FIXME: %x : preferred date representation for the current locale without the time
1743 // FIXME: %X : preferred time representation for the current locale without the date
1744 s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
1745 s["%Y"] = y; // year with the century
1746 s["%%"] = "%"; // a literal '%' character
1749 var isSafari=navigator.userAgent.toLowerCase().indexOf ("safari")!=-1;
1750 if (!Calendar.is_ie5 && !isSafari)
1751 return str.replace(re, function (par) { return s[par] || par; })
1753 var a = str.match(re);
1754 for (var i = 0; i < a.length; i++) {
1757 re = new RegExp(a[i], 'g');
1758 str = str.replace(re, tmp);
1765 Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
1766 Date.prototype.setFullYear = function(y) {
1767 var d = new Date(this);
1768 d.__msh_oldSetFullYear(y);
1769 if (d.getMonth() != this.getMonth())
1771 this.__msh_oldSetFullYear(y);
1774 // END: DATE OBJECT PATCHES
1777 // global object that remembers the calendar
1778 window.calendar = null;