Fix calendar sidebar issue - changed holder from 'contentholder' to 'sidebarholder'
[elgg.git] / _calendar / jscalendar / calendar.js
blob3f6ce969c503b1b5112b9776e013b5abfd09b86f
1 /*  Copyright Mihai Bazon, 2002-2005  |  www.bazon.net/mishoo\r
2  * -----------------------------------------------------------\r
3  *\r
4  * The DHTML Calendar, version 1.0 "It is happening again"\r
5  *\r
6  * Details and latest version at:\r
7  * www.dynarch.com/projects/calendar\r
8  *\r
9  * This script is developed by Dynarch.com.  Visit us at www.dynarch.com.\r
10  *\r
11  * This script is distributed under the GNU Lesser General Public License.\r
12  * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html\r
13  */\r
15 // $Id: calendar.js,v 1.1 2005/11/19 18:42:23 siexchange Exp $\r
17 /** The Calendar object constructor. */\r
18 Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {\r
19         // member variables\r
20         this.activeDiv = null;\r
21         this.currentDateEl = null;\r
22         this.getDateStatus = null;\r
23         this.getDateToolTip = null;\r
24         this.getDateText = null;\r
25         this.timeout = null;\r
26         this.onSelected = onSelected || null;\r
27         this.onClose = onClose || null;\r
28         this.dragging = false;\r
29         this.hidden = false;\r
30         this.minYear = 1970;\r
31         this.maxYear = 2050;\r
32         this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];\r
33         this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];\r
34         this.isPopup = true;\r
35         this.weekNumbers = true;\r
36         this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.\r
37         this.showsOtherMonths = false;\r
38         this.dateStr = dateStr;\r
39         this.ar_days = null;\r
40         this.showsTime = false;\r
41         this.time24 = true;\r
42         this.yearStep = 2;\r
43         this.hiliteToday = true;\r
44         this.multiple = null;\r
45         // HTML elements\r
46         this.table = null;\r
47         this.element = null;\r
48         this.tbody = null;\r
49         this.firstdayname = null;\r
50         // Combo boxes\r
51         this.monthsCombo = null;\r
52         this.yearsCombo = null;\r
53         this.hilitedMonth = null;\r
54         this.activeMonth = null;\r
55         this.hilitedYear = null;\r
56         this.activeYear = null;\r
57         // Information\r
58         this.dateClicked = false;\r
60         // one-time initializations\r
61         if (typeof Calendar._SDN == "undefined") {\r
62                 // table of short day names\r
63                 if (typeof Calendar._SDN_len == "undefined")\r
64                         Calendar._SDN_len = 3;\r
65                 var ar = new Array();\r
66                 for (var i = 8; i > 0;) {\r
67                         ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);\r
68                 }\r
69                 Calendar._SDN = ar;\r
70                 // table of short month names\r
71                 if (typeof Calendar._SMN_len == "undefined")\r
72                         Calendar._SMN_len = 3;\r
73                 ar = new Array();\r
74                 for (var i = 12; i > 0;) {\r
75                         ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);\r
76                 }\r
77                 Calendar._SMN = ar;\r
78         }\r
79 };\r
81 // ** constants\r
83 /// "static", needed for event handlers.\r
84 Calendar._C = null;\r
86 /// detect a special case of "web browser"\r
87 Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&\r
88                    !/opera/i.test(navigator.userAgent) );\r
90 Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );\r
92 /// detect Opera browser\r
93 Calendar.is_opera = /opera/i.test(navigator.userAgent);\r
95 /// detect KHTML-based browsers\r
96 Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);\r
98 // BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate\r
99 //        library, at some point.\r
101 Calendar.getAbsolutePos = function(el) {\r
102         var SL = 0, ST = 0;\r
103         var is_div = /^div$/i.test(el.tagName);\r
104         if (is_div && el.scrollLeft)\r
105                 SL = el.scrollLeft;\r
106         if (is_div && el.scrollTop)\r
107                 ST = el.scrollTop;\r
108         var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };\r
109         if (el.offsetParent) {\r
110                 var tmp = this.getAbsolutePos(el.offsetParent);\r
111                 r.x += tmp.x;\r
112                 r.y += tmp.y;\r
113         }\r
114         return r;\r
115 };\r
117 Calendar.isRelated = function (el, evt) {\r
118         var related = evt.relatedTarget;\r
119         if (!related) {\r
120                 var type = evt.type;\r
121                 if (type == "mouseover") {\r
122                         related = evt.fromElement;\r
123                 } else if (type == "mouseout") {\r
124                         related = evt.toElement;\r
125                 }\r
126         }\r
127         while (related) {\r
128                 if (related == el) {\r
129                         return true;\r
130                 }\r
131                 related = related.parentNode;\r
132         }\r
133         return false;\r
134 };\r
136 Calendar.removeClass = function(el, className) {\r
137         if (!(el && el.className)) {\r
138                 return;\r
139         }\r
140         var cls = el.className.split(" ");\r
141         var ar = new Array();\r
142         for (var i = cls.length; i > 0;) {\r
143                 if (cls[--i] != className) {\r
144                         ar[ar.length] = cls[i];\r
145                 }\r
146         }\r
147         el.className = ar.join(" ");\r
148 };\r
150 Calendar.addClass = function(el, className) {\r
151         Calendar.removeClass(el, className);\r
152         el.className += " " + className;\r
153 };\r
155 // FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.\r
156 Calendar.getElement = function(ev) {\r
157         var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;\r
158         while (f.nodeType != 1 || /^div$/i.test(f.tagName))\r
159                 f = f.parentNode;\r
160         return f;\r
161 };\r
163 Calendar.getTargetElement = function(ev) {\r
164         var f = Calendar.is_ie ? window.event.srcElement : ev.target;\r
165         while (f.nodeType != 1)\r
166                 f = f.parentNode;\r
167         return f;\r
168 };\r
170 Calendar.stopEvent = function(ev) {\r
171         ev || (ev = window.event);\r
172         if (Calendar.is_ie) {\r
173                 ev.cancelBubble = true;\r
174                 ev.returnValue = false;\r
175         } else {\r
176                 ev.preventDefault();\r
177                 ev.stopPropagation();\r
178         }\r
179         return false;\r
180 };\r
182 Calendar.addEvent = function(el, evname, func) {\r
183         if (el.attachEvent) { // IE\r
184                 el.attachEvent("on" + evname, func);\r
185         } else if (el.addEventListener) { // Gecko / W3C\r
186                 el.addEventListener(evname, func, true);\r
187         } else {\r
188                 el["on" + evname] = func;\r
189         }\r
190 };\r
192 Calendar.removeEvent = function(el, evname, func) {\r
193         if (el.detachEvent) { // IE\r
194                 el.detachEvent("on" + evname, func);\r
195         } else if (el.removeEventListener) { // Gecko / W3C\r
196                 el.removeEventListener(evname, func, true);\r
197         } else {\r
198                 el["on" + evname] = null;\r
199         }\r
200 };\r
202 Calendar.createElement = function(type, parent) {\r
203         var el = null;\r
204         if (document.createElementNS) {\r
205                 // use the XHTML namespace; IE won't normally get here unless\r
206                 // _they_ "fix" the DOM2 implementation.\r
207                 el = document.createElementNS("http://www.w3.org/1999/xhtml", type);\r
208         } else {\r
209                 el = document.createElement(type);\r
210         }\r
211         if (typeof parent != "undefined") {\r
212                 parent.appendChild(el);\r
213         }\r
214         return el;\r
215 };\r
217 // END: UTILITY FUNCTIONS\r
219 // BEGIN: CALENDAR STATIC FUNCTIONS\r
221 /** Internal -- adds a set of events to make some element behave like a button. */\r
222 Calendar._add_evs = function(el) {\r
223         with (Calendar) {\r
224                 addEvent(el, "mouseover", dayMouseOver);\r
225                 addEvent(el, "mousedown", dayMouseDown);\r
226                 addEvent(el, "mouseout", dayMouseOut);\r
227                 if (is_ie) {\r
228                         addEvent(el, "dblclick", dayMouseDblClick);\r
229                         el.setAttribute("unselectable", true);\r
230                 }\r
231         }\r
232 };\r
234 Calendar.findMonth = function(el) {\r
235         if (typeof el.month != "undefined") {\r
236                 return el;\r
237         } else if (typeof el.parentNode.month != "undefined") {\r
238                 return el.parentNode;\r
239         }\r
240         return null;\r
241 };\r
243 Calendar.findYear = function(el) {\r
244         if (typeof el.year != "undefined") {\r
245                 return el;\r
246         } else if (typeof el.parentNode.year != "undefined") {\r
247                 return el.parentNode;\r
248         }\r
249         return null;\r
250 };\r
252 Calendar.showMonthsCombo = function () {\r
253         var cal = Calendar._C;\r
254         if (!cal) {\r
255                 return false;\r
256         }\r
257         var cal = cal;\r
258         var cd = cal.activeDiv;\r
259         var mc = cal.monthsCombo;\r
260         if (cal.hilitedMonth) {\r
261                 Calendar.removeClass(cal.hilitedMonth, "hilite");\r
262         }\r
263         if (cal.activeMonth) {\r
264                 Calendar.removeClass(cal.activeMonth, "active");\r
265         }\r
266         var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];\r
267         Calendar.addClass(mon, "active");\r
268         cal.activeMonth = mon;\r
269         var s = mc.style;\r
270         s.display = "block";\r
271         if (cd.navtype < 0)\r
272                 s.left = cd.offsetLeft + "px";\r
273         else {\r
274                 var mcw = mc.offsetWidth;\r
275                 if (typeof mcw == "undefined")\r
276                         // Konqueror brain-dead techniques\r
277                         mcw = 50;\r
278                 s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";\r
279         }\r
280         s.top = (cd.offsetTop + cd.offsetHeight) + "px";\r
281 };\r
283 Calendar.showYearsCombo = function (fwd) {\r
284         var cal = Calendar._C;\r
285         if (!cal) {\r
286                 return false;\r
287         }\r
288         var cal = cal;\r
289         var cd = cal.activeDiv;\r
290         var yc = cal.yearsCombo;\r
291         if (cal.hilitedYear) {\r
292                 Calendar.removeClass(cal.hilitedYear, "hilite");\r
293         }\r
294         if (cal.activeYear) {\r
295                 Calendar.removeClass(cal.activeYear, "active");\r
296         }\r
297         cal.activeYear = null;\r
298         var Y = cal.date.getFullYear() + (fwd ? 1 : -1);\r
299         var yr = yc.firstChild;\r
300         var show = false;\r
301         for (var i = 12; i > 0; --i) {\r
302                 if (Y >= cal.minYear && Y <= cal.maxYear) {\r
303                         yr.innerHTML = Y;\r
304                         yr.year = Y;\r
305                         yr.style.display = "block";\r
306                         show = true;\r
307                 } else {\r
308                         yr.style.display = "none";\r
309                 }\r
310                 yr = yr.nextSibling;\r
311                 Y += fwd ? cal.yearStep : -cal.yearStep;\r
312         }\r
313         if (show) {\r
314                 var s = yc.style;\r
315                 s.display = "block";\r
316                 if (cd.navtype < 0)\r
317                         s.left = cd.offsetLeft + "px";\r
318                 else {\r
319                         var ycw = yc.offsetWidth;\r
320                         if (typeof ycw == "undefined")\r
321                                 // Konqueror brain-dead techniques\r
322                                 ycw = 50;\r
323                         s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";\r
324                 }\r
325                 s.top = (cd.offsetTop + cd.offsetHeight) + "px";\r
326         }\r
327 };\r
329 // event handlers\r
331 Calendar.tableMouseUp = function(ev) {\r
332         var cal = Calendar._C;\r
333         if (!cal) {\r
334                 return false;\r
335         }\r
336         if (cal.timeout) {\r
337                 clearTimeout(cal.timeout);\r
338         }\r
339         var el = cal.activeDiv;\r
340         if (!el) {\r
341                 return false;\r
342         }\r
343         var target = Calendar.getTargetElement(ev);\r
344         ev || (ev = window.event);\r
345         Calendar.removeClass(el, "active");\r
346         if (target == el || target.parentNode == el) {\r
347                 Calendar.cellClick(el, ev);\r
348         }\r
349         var mon = Calendar.findMonth(target);\r
350         var date = null;\r
351         if (mon) {\r
352                 date = new Date(cal.date);\r
353                 if (mon.month != date.getMonth()) {\r
354                         date.setMonth(mon.month);\r
355                         cal.setDate(date);\r
356                         cal.dateClicked = false;\r
357                         cal.callHandler();\r
358                 }\r
359         } else {\r
360                 var year = Calendar.findYear(target);\r
361                 if (year) {\r
362                         date = new Date(cal.date);\r
363                         if (year.year != date.getFullYear()) {\r
364                                 date.setFullYear(year.year);\r
365                                 cal.setDate(date);\r
366                                 cal.dateClicked = false;\r
367                                 cal.callHandler();\r
368                         }\r
369                 }\r
370         }\r
371         with (Calendar) {\r
372                 removeEvent(document, "mouseup", tableMouseUp);\r
373                 removeEvent(document, "mouseover", tableMouseOver);\r
374                 removeEvent(document, "mousemove", tableMouseOver);\r
375                 cal._hideCombos();\r
376                 _C = null;\r
377                 return stopEvent(ev);\r
378         }\r
379 };\r
381 Calendar.tableMouseOver = function (ev) {\r
382         var cal = Calendar._C;\r
383         if (!cal) {\r
384                 return;\r
385         }\r
386         var el = cal.activeDiv;\r
387         var target = Calendar.getTargetElement(ev);\r
388         if (target == el || target.parentNode == el) {\r
389                 Calendar.addClass(el, "hilite active");\r
390                 Calendar.addClass(el.parentNode, "rowhilite");\r
391         } else {\r
392                 if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))\r
393                         Calendar.removeClass(el, "active");\r
394                 Calendar.removeClass(el, "hilite");\r
395                 Calendar.removeClass(el.parentNode, "rowhilite");\r
396         }\r
397         ev || (ev = window.event);\r
398         if (el.navtype == 50 && target != el) {\r
399                 var pos = Calendar.getAbsolutePos(el);\r
400                 var w = el.offsetWidth;\r
401                 var x = ev.clientX;\r
402                 var dx;\r
403                 var decrease = true;\r
404                 if (x > pos.x + w) {\r
405                         dx = x - pos.x - w;\r
406                         decrease = false;\r
407                 } else\r
408                         dx = pos.x - x;\r
410                 if (dx < 0) dx = 0;\r
411                 var range = el._range;\r
412                 var current = el._current;\r
413                 var count = Math.floor(dx / 10) % range.length;\r
414                 for (var i = range.length; --i >= 0;)\r
415                         if (range[i] == current)\r
416                                 break;\r
417                 while (count-- > 0)\r
418                         if (decrease) {\r
419                                 if (--i < 0)\r
420                                         i = range.length - 1;\r
421                         } else if ( ++i >= range.length )\r
422                                 i = 0;\r
423                 var newval = range[i];\r
424                 el.innerHTML = newval;\r
426                 cal.onUpdateTime();\r
427         }\r
428         var mon = Calendar.findMonth(target);\r
429         if (mon) {\r
430                 if (mon.month != cal.date.getMonth()) {\r
431                         if (cal.hilitedMonth) {\r
432                                 Calendar.removeClass(cal.hilitedMonth, "hilite");\r
433                         }\r
434                         Calendar.addClass(mon, "hilite");\r
435                         cal.hilitedMonth = mon;\r
436                 } else if (cal.hilitedMonth) {\r
437                         Calendar.removeClass(cal.hilitedMonth, "hilite");\r
438                 }\r
439         } else {\r
440                 if (cal.hilitedMonth) {\r
441                         Calendar.removeClass(cal.hilitedMonth, "hilite");\r
442                 }\r
443                 var year = Calendar.findYear(target);\r
444                 if (year) {\r
445                         if (year.year != cal.date.getFullYear()) {\r
446                                 if (cal.hilitedYear) {\r
447                                         Calendar.removeClass(cal.hilitedYear, "hilite");\r
448                                 }\r
449                                 Calendar.addClass(year, "hilite");\r
450                                 cal.hilitedYear = year;\r
451                         } else if (cal.hilitedYear) {\r
452                                 Calendar.removeClass(cal.hilitedYear, "hilite");\r
453                         }\r
454                 } else if (cal.hilitedYear) {\r
455                         Calendar.removeClass(cal.hilitedYear, "hilite");\r
456                 }\r
457         }\r
458         return Calendar.stopEvent(ev);\r
459 };\r
461 Calendar.tableMouseDown = function (ev) {\r
462         if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {\r
463                 return Calendar.stopEvent(ev);\r
464         }\r
465 };\r
467 Calendar.calDragIt = function (ev) {\r
468         var cal = Calendar._C;\r
469         if (!(cal && cal.dragging)) {\r
470                 return false;\r
471         }\r
472         var posX;\r
473         var posY;\r
474         if (Calendar.is_ie) {\r
475                 posY = window.event.clientY + document.body.scrollTop;\r
476                 posX = window.event.clientX + document.body.scrollLeft;\r
477         } else {\r
478                 posX = ev.pageX;\r
479                 posY = ev.pageY;\r
480         }\r
481         cal.hideShowCovered();\r
482         var st = cal.element.style;\r
483         st.left = (posX - cal.xOffs) + "px";\r
484         st.top = (posY - cal.yOffs) + "px";\r
485         return Calendar.stopEvent(ev);\r
486 };\r
488 Calendar.calDragEnd = function (ev) {\r
489         var cal = Calendar._C;\r
490         if (!cal) {\r
491                 return false;\r
492         }\r
493         cal.dragging = false;\r
494         with (Calendar) {\r
495                 removeEvent(document, "mousemove", calDragIt);\r
496                 removeEvent(document, "mouseup", calDragEnd);\r
497                 tableMouseUp(ev);\r
498         }\r
499         cal.hideShowCovered();\r
500 };\r
502 Calendar.dayMouseDown = function(ev) {\r
503         var el = Calendar.getElement(ev);\r
504         if (el.disabled) {\r
505                 return false;\r
506         }\r
507         var cal = el.calendar;\r
508         cal.activeDiv = el;\r
509         Calendar._C = cal;\r
510         if (el.navtype != 300) with (Calendar) {\r
511                 if (el.navtype == 50) {\r
512                         el._current = el.innerHTML;\r
513                         addEvent(document, "mousemove", tableMouseOver);\r
514                 } else\r
515                         addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);\r
516                 addClass(el, "hilite active");\r
517                 addEvent(document, "mouseup", tableMouseUp);\r
518         } else if (cal.isPopup) {\r
519                 cal._dragStart(ev);\r
520         }\r
521         if (el.navtype == -1 || el.navtype == 1) {\r
522                 if (cal.timeout) clearTimeout(cal.timeout);\r
523                 cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);\r
524         } else if (el.navtype == -2 || el.navtype == 2) {\r
525                 if (cal.timeout) clearTimeout(cal.timeout);\r
526                 cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);\r
527         } else {\r
528                 cal.timeout = null;\r
529         }\r
530         return Calendar.stopEvent(ev);\r
531 };\r
533 Calendar.dayMouseDblClick = function(ev) {\r
534         Calendar.cellClick(Calendar.getElement(ev), ev || window.event);\r
535         if (Calendar.is_ie) {\r
536                 document.selection.empty();\r
537         }\r
538 };\r
540 Calendar.dayMouseOver = function(ev) {\r
541         var el = Calendar.getElement(ev);\r
542         if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {\r
543                 return false;\r
544         }\r
545         if (el.ttip) {\r
546                 if (el.ttip.substr(0, 1) == "_") {\r
547                         el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);\r
548                 }\r
549                 el.calendar.tooltips.innerHTML = el.ttip;\r
550         }\r
551         if (el.navtype != 300) {\r
552                 Calendar.addClass(el, "hilite");\r
553                 if (el.caldate) {\r
554                         Calendar.addClass(el.parentNode, "rowhilite");\r
555                 }\r
556         }\r
557         return Calendar.stopEvent(ev);\r
558 };\r
560 Calendar.dayMouseOut = function(ev) {\r
561         with (Calendar) {\r
562                 var el = getElement(ev);\r
563                 if (isRelated(el, ev) || _C || el.disabled)\r
564                         return false;\r
565                 removeClass(el, "hilite");\r
566                 if (el.caldate)\r
567                         removeClass(el.parentNode, "rowhilite");\r
568                 if (el.calendar)\r
569                         el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];\r
570                 return stopEvent(ev);\r
571         }\r
572 };\r
574 /**\r
575  *  A generic "click" handler :) handles all types of buttons defined in this\r
576  *  calendar.\r
577  */\r
578 Calendar.cellClick = function(el, ev) {\r
579         var cal = el.calendar;\r
580         var closing = false;\r
581         var newdate = false;\r
582         var date = null;\r
583         if (typeof el.navtype == "undefined") {\r
584                 if (cal.currentDateEl) {\r
585                         Calendar.removeClass(cal.currentDateEl, "selected");\r
586                         Calendar.addClass(el, "selected");\r
587                         closing = (cal.currentDateEl == el);\r
588                         if (!closing) {\r
589                                 cal.currentDateEl = el;\r
590                         }\r
591                 }\r
592                 cal.date.setDateOnly(el.caldate);\r
593                 date = cal.date;\r
594                 var other_month = !(cal.dateClicked = !el.otherMonth);\r
595                 if (!other_month && !cal.currentDateEl)\r
596                         cal._toggleMultipleDate(new Date(date));\r
597                 else\r
598                         newdate = !el.disabled;\r
599                 // a date was clicked\r
600                 if (other_month)\r
601                         cal._init(cal.firstDayOfWeek, date);\r
602         } else {\r
603                 if (el.navtype == 200) {\r
604                         Calendar.removeClass(el, "hilite");\r
605                         cal.callCloseHandler();\r
606                         return;\r
607                 }\r
608                 date = new Date(cal.date);\r
609                 if (el.navtype == 0)\r
610                         date.setDateOnly(new Date()); // TODAY\r
611                 // unless "today" was clicked, we assume no date was clicked so\r
612                 // the selected handler will know not to close the calenar when\r
613                 // in single-click mode.\r
614                 // cal.dateClicked = (el.navtype == 0);\r
615                 cal.dateClicked = false;\r
616                 var year = date.getFullYear();\r
617                 var mon = date.getMonth();\r
618                 function setMonth(m) {\r
619                         var day = date.getDate();\r
620                         var max = date.getMonthDays(m);\r
621                         if (day > max) {\r
622                                 date.setDate(max);\r
623                         }\r
624                         date.setMonth(m);\r
625                 };\r
626                 switch (el.navtype) {\r
627                     case 400:\r
628                         Calendar.removeClass(el, "hilite");\r
629                         var text = Calendar._TT["ABOUT"];\r
630                         if (typeof text != "undefined") {\r
631                                 text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";\r
632                         } else {\r
633                                 // FIXME: this should be removed as soon as lang files get updated!\r
634                                 text = "Help and about box text is not translated into this language.\n" +\r
635                                         "If you know this language and you feel generous please update\n" +\r
636                                         "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +\r
637                                         "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n" +\r
638                                         "Thank you!\n" +\r
639                                         "http://dynarch.com/mishoo/calendar.epl\n";\r
640                         }\r
641                         alert(text);\r
642                         return;\r
643                     case -2:\r
644                         if (year > cal.minYear) {\r
645                                 date.setFullYear(year - 1);\r
646                         }\r
647                         break;\r
648                     case -1:\r
649                         if (mon > 0) {\r
650                                 setMonth(mon - 1);\r
651                         } else if (year-- > cal.minYear) {\r
652                                 date.setFullYear(year);\r
653                                 setMonth(11);\r
654                         }\r
655                         break;\r
656                     case 1:\r
657                         if (mon < 11) {\r
658                                 setMonth(mon + 1);\r
659                         } else if (year < cal.maxYear) {\r
660                                 date.setFullYear(year + 1);\r
661                                 setMonth(0);\r
662                         }\r
663                         break;\r
664                     case 2:\r
665                         if (year < cal.maxYear) {\r
666                                 date.setFullYear(year + 1);\r
667                         }\r
668                         break;\r
669                     case 100:\r
670                         cal.setFirstDayOfWeek(el.fdow);\r
671                         return;\r
672                     case 50:\r
673                         var range = el._range;\r
674                         var current = el.innerHTML;\r
675                         for (var i = range.length; --i >= 0;)\r
676                                 if (range[i] == current)\r
677                                         break;\r
678                         if (ev && ev.shiftKey) {\r
679                                 if (--i < 0)\r
680                                         i = range.length - 1;\r
681                         } else if ( ++i >= range.length )\r
682                                 i = 0;\r
683                         var newval = range[i];\r
684                         el.innerHTML = newval;\r
685                         cal.onUpdateTime();\r
686                         return;\r
687                     case 0:\r
688                         // TODAY will bring us here\r
689                         if ((typeof cal.getDateStatus == "function") &&\r
690                             cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {\r
691                                 return false;\r
692                         }\r
693                         break;\r
694                 }\r
695                 if (!date.equalsTo(cal.date)) {\r
696                         cal.setDate(date);\r
697                         newdate = true;\r
698                 } else if (el.navtype == 0)\r
699                         newdate = closing = true;\r
700         }\r
701         if (newdate) {\r
702                 ev && cal.callHandler();\r
703         }\r
704         if (closing) {\r
705                 Calendar.removeClass(el, "hilite");\r
706                 ev && cal.callCloseHandler();\r
707         }\r
708 };\r
710 // END: CALENDAR STATIC FUNCTIONS\r
712 // BEGIN: CALENDAR OBJECT FUNCTIONS\r
714 /**\r
715  *  This function creates the calendar inside the given parent.  If _par is\r
716  *  null than it creates a popup calendar inside the BODY element.  If _par is\r
717  *  an element, be it BODY, then it creates a non-popup calendar (still\r
718  *  hidden).  Some properties need to be set before calling this function.\r
719  */\r
720 Calendar.prototype.create = function (_par) {\r
721         var parent = null;\r
722         if (! _par) {\r
723                 // default parent is the document body, in which case we create\r
724                 // a popup calendar.\r
725                 parent = document.getElementsByTagName("body")[0];\r
726                 this.isPopup = true;\r
727         } else {\r
728                 parent = _par;\r
729                 this.isPopup = false;\r
730         }\r
731         this.date = this.dateStr ? new Date(this.dateStr) : new Date();\r
733         var table = Calendar.createElement("table");\r
734         this.table = table;\r
735         table.cellSpacing = 0;\r
736         table.cellPadding = 0;\r
737         table.calendar = this;\r
738         Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);\r
740         var div = Calendar.createElement("div");\r
741         this.element = div;\r
742         div.className = "calendar";\r
743         if (this.isPopup) {\r
744                 div.style.position = "absolute";\r
745                 div.style.display = "none";\r
746         }\r
747         div.appendChild(table);\r
749         var thead = Calendar.createElement("thead", table);\r
750         var cell = null;\r
751         var row = null;\r
753         var cal = this;\r
754         var hh = function (text, cs, navtype) {\r
755                 cell = Calendar.createElement("td", row);\r
756                 cell.colSpan = cs;\r
757                 cell.className = "button";\r
758                 if (navtype != 0 && Math.abs(navtype) <= 2)\r
759                         cell.className += " nav";\r
760                 Calendar._add_evs(cell);\r
761                 cell.calendar = cal;\r
762                 cell.navtype = navtype;\r
763                 cell.innerHTML = "<div unselectable='on'>" + text + "</div>";\r
764                 return cell;\r
765         };\r
767         row = Calendar.createElement("tr", thead);\r
768         var title_length = 6;\r
769         (this.isPopup) && --title_length;\r
770         (this.weekNumbers) && ++title_length;\r
772         hh("?", 1, 400).ttip = Calendar._TT["INFO"];\r
773         this.title = hh("", title_length, 300);\r
774         this.title.className = "title";\r
775         if (this.isPopup) {\r
776                 this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];\r
777                 this.title.style.cursor = "move";\r
778                 hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];\r
779         }\r
781         row = Calendar.createElement("tr", thead);\r
782         row.className = "headrow";\r
784         this._nav_py = hh("&#x00ab;", 1, -2);\r
785         this._nav_py.ttip = Calendar._TT["PREV_YEAR"];\r
787         this._nav_pm = hh("&#x2039;", 1, -1);\r
788         this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];\r
790         this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);\r
791         this._nav_now.ttip = Calendar._TT["GO_TODAY"];\r
793         this._nav_nm = hh("&#x203a;", 1, 1);\r
794         this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];\r
796         this._nav_ny = hh("&#x00bb;", 1, 2);\r
797         this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];\r
799         // day names\r
800         row = Calendar.createElement("tr", thead);\r
801         row.className = "daynames";\r
802         if (this.weekNumbers) {\r
803                 cell = Calendar.createElement("td", row);\r
804                 cell.className = "name wn";\r
805                 cell.innerHTML = Calendar._TT["WK"];\r
806         }\r
807         for (var i = 7; i > 0; --i) {\r
808                 cell = Calendar.createElement("td", row);\r
809                 if (!i) {\r
810                         cell.navtype = 100;\r
811                         cell.calendar = this;\r
812                         Calendar._add_evs(cell);\r
813                 }\r
814         }\r
815         this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;\r
816         this._displayWeekdays();\r
818         var tbody = Calendar.createElement("tbody", table);\r
819         this.tbody = tbody;\r
821         for (i = 6; i > 0; --i) {\r
822                 row = Calendar.createElement("tr", tbody);\r
823                 if (this.weekNumbers) {\r
824                         cell = Calendar.createElement("td", row);\r
825                 }\r
826                 for (var j = 7; j > 0; --j) {\r
827                         cell = Calendar.createElement("td", row);\r
828                         cell.calendar = this;\r
829                         Calendar._add_evs(cell);\r
830                 }\r
831         }\r
833         if (this.showsTime) {\r
834                 row = Calendar.createElement("tr", tbody);\r
835                 row.className = "time";\r
837                 cell = Calendar.createElement("td", row);\r
838                 cell.className = "time";\r
839                 cell.colSpan = 2;\r
840                 cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";\r
842                 cell = Calendar.createElement("td", row);\r
843                 cell.className = "time";\r
844                 cell.colSpan = this.weekNumbers ? 4 : 3;\r
846                 (function(){\r
847                         function makeTimePart(className, init, range_start, range_end) {\r
848                                 var part = Calendar.createElement("span", cell);\r
849                                 part.className = className;\r
850                                 part.innerHTML = init;\r
851                                 part.calendar = cal;\r
852                                 part.ttip = Calendar._TT["TIME_PART"];\r
853                                 part.navtype = 50;\r
854                                 part._range = [];\r
855                                 if (typeof range_start != "number")\r
856                                         part._range = range_start;\r
857                                 else {\r
858                                         for (var i = range_start; i <= range_end; ++i) {\r
859                                                 var txt;\r
860                                                 if (i < 10 && range_end >= 10) txt = '0' + i;\r
861                                                 else txt = '' + i;\r
862                                                 part._range[part._range.length] = txt;\r
863                                         }\r
864                                 }\r
865                                 Calendar._add_evs(part);\r
866                                 return part;\r
867                         };\r
868                         var hrs = cal.date.getHours();\r
869                         var mins = cal.date.getMinutes();\r
870                         var t12 = !cal.time24;\r
871                         var pm = (hrs > 12);\r
872                         if (t12 && pm) hrs -= 12;\r
873                         var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);\r
874                         var span = Calendar.createElement("span", cell);\r
875                         span.innerHTML = ":";\r
876                         span.className = "colon";\r
877                         var M = makeTimePart("minute", mins, 0, 59);\r
878                         var AP = null;\r
879                         cell = Calendar.createElement("td", row);\r
880                         cell.className = "time";\r
881                         cell.colSpan = 2;\r
882                         if (t12)\r
883                                 AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);\r
884                         else\r
885                                 cell.innerHTML = "&nbsp;";\r
887                         cal.onSetTime = function() {\r
888                                 var pm, hrs = this.date.getHours(),\r
889                                         mins = this.date.getMinutes();\r
890                                 if (t12) {\r
891                                         pm = (hrs >= 12);\r
892                                         if (pm) hrs -= 12;\r
893                                         if (hrs == 0) hrs = 12;\r
894                                         AP.innerHTML = pm ? "pm" : "am";\r
895                                 }\r
896                                 H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;\r
897                                 M.innerHTML = (mins < 10) ? ("0" + mins) : mins;\r
898                         };\r
900                         cal.onUpdateTime = function() {\r
901                                 var date = this.date;\r
902                                 var h = parseInt(H.innerHTML, 10);\r
903                                 if (t12) {\r
904                                         if (/pm/i.test(AP.innerHTML) && h < 12)\r
905                                                 h += 12;\r
906                                         else if (/am/i.test(AP.innerHTML) && h == 12)\r
907                                                 h = 0;\r
908                                 }\r
909                                 var d = date.getDate();\r
910                                 var m = date.getMonth();\r
911                                 var y = date.getFullYear();\r
912                                 date.setHours(h);\r
913                                 date.setMinutes(parseInt(M.innerHTML, 10));\r
914                                 date.setFullYear(y);\r
915                                 date.setMonth(m);\r
916                                 date.setDate(d);\r
917                                 this.dateClicked = false;\r
918                                 this.callHandler();\r
919                         };\r
920                 })();\r
921         } else {\r
922                 this.onSetTime = this.onUpdateTime = function() {};\r
923         }\r
925         var tfoot = Calendar.createElement("tfoot", table);\r
927         row = Calendar.createElement("tr", tfoot);\r
928         row.className = "footrow";\r
930         cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);\r
931         cell.className = "ttip";\r
932         if (this.isPopup) {\r
933                 cell.ttip = Calendar._TT["DRAG_TO_MOVE"];\r
934                 cell.style.cursor = "move";\r
935         }\r
936         this.tooltips = cell;\r
938         div = Calendar.createElement("div", this.element);\r
939         this.monthsCombo = div;\r
940         div.className = "combo";\r
941         for (i = 0; i < Calendar._MN.length; ++i) {\r
942                 var mn = Calendar.createElement("div");\r
943                 mn.className = Calendar.is_ie ? "label-IEfix" : "label";\r
944                 mn.month = i;\r
945                 mn.innerHTML = Calendar._SMN[i];\r
946                 div.appendChild(mn);\r
947         }\r
949         div = Calendar.createElement("div", this.element);\r
950         this.yearsCombo = div;\r
951         div.className = "combo";\r
952         for (i = 12; i > 0; --i) {\r
953                 var yr = Calendar.createElement("div");\r
954                 yr.className = Calendar.is_ie ? "label-IEfix" : "label";\r
955                 div.appendChild(yr);\r
956         }\r
958         this._init(this.firstDayOfWeek, this.date);\r
959         parent.appendChild(this.element);\r
960 };\r
962 /** keyboard navigation, only for popup calendars */\r
963 Calendar._keyEvent = function(ev) {\r
964         var cal = window._dynarch_popupCalendar;\r
965         if (!cal || cal.multiple)\r
966                 return false;\r
967         (Calendar.is_ie) && (ev = window.event);\r
968         var act = (Calendar.is_ie || ev.type == "keypress"),\r
969                 K = ev.keyCode;\r
970         if (ev.ctrlKey) {\r
971                 switch (K) {\r
972                     case 37: // KEY left\r
973                         act && Calendar.cellClick(cal._nav_pm);\r
974                         break;\r
975                     case 38: // KEY up\r
976                         act && Calendar.cellClick(cal._nav_py);\r
977                         break;\r
978                     case 39: // KEY right\r
979                         act && Calendar.cellClick(cal._nav_nm);\r
980                         break;\r
981                     case 40: // KEY down\r
982                         act && Calendar.cellClick(cal._nav_ny);\r
983                         break;\r
984                     default:\r
985                         return false;\r
986                 }\r
987         } else switch (K) {\r
988             case 32: // KEY space (now)\r
989                 Calendar.cellClick(cal._nav_now);\r
990                 break;\r
991             case 27: // KEY esc\r
992                 act && cal.callCloseHandler();\r
993                 break;\r
994             case 37: // KEY left\r
995             case 38: // KEY up\r
996             case 39: // KEY right\r
997             case 40: // KEY down\r
998                 if (act) {\r
999                         var prev, x, y, ne, el, step;\r
1000                         prev = K == 37 || K == 38;\r
1001                         step = (K == 37 || K == 39) ? 1 : 7;\r
1002                         function setVars() {\r
1003                                 el = cal.currentDateEl;\r
1004                                 var p = el.pos;\r
1005                                 x = p & 15;\r
1006                                 y = p >> 4;\r
1007                                 ne = cal.ar_days[y][x];\r
1008                         };setVars();\r
1009                         function prevMonth() {\r
1010                                 var date = new Date(cal.date);\r
1011                                 date.setDate(date.getDate() - step);\r
1012                                 cal.setDate(date);\r
1013                         };\r
1014                         function nextMonth() {\r
1015                                 var date = new Date(cal.date);\r
1016                                 date.setDate(date.getDate() + step);\r
1017                                 cal.setDate(date);\r
1018                         };\r
1019                         while (1) {\r
1020                                 switch (K) {\r
1021                                     case 37: // KEY left\r
1022                                         if (--x >= 0)\r
1023                                                 ne = cal.ar_days[y][x];\r
1024                                         else {\r
1025                                                 x = 6;\r
1026                                                 K = 38;\r
1027                                                 continue;\r
1028                                         }\r
1029                                         break;\r
1030                                     case 38: // KEY up\r
1031                                         if (--y >= 0)\r
1032                                                 ne = cal.ar_days[y][x];\r
1033                                         else {\r
1034                                                 prevMonth();\r
1035                                                 setVars();\r
1036                                         }\r
1037                                         break;\r
1038                                     case 39: // KEY right\r
1039                                         if (++x < 7)\r
1040                                                 ne = cal.ar_days[y][x];\r
1041                                         else {\r
1042                                                 x = 0;\r
1043                                                 K = 40;\r
1044                                                 continue;\r
1045                                         }\r
1046                                         break;\r
1047                                     case 40: // KEY down\r
1048                                         if (++y < cal.ar_days.length)\r
1049                                                 ne = cal.ar_days[y][x];\r
1050                                         else {\r
1051                                                 nextMonth();\r
1052                                                 setVars();\r
1053                                         }\r
1054                                         break;\r
1055                                 }\r
1056                                 break;\r
1057                         }\r
1058                         if (ne) {\r
1059                                 if (!ne.disabled)\r
1060                                         Calendar.cellClick(ne);\r
1061                                 else if (prev)\r
1062                                         prevMonth();\r
1063                                 else\r
1064                                         nextMonth();\r
1065                         }\r
1066                 }\r
1067                 break;\r
1068             case 13: // KEY enter\r
1069                 if (act)\r
1070                         Calendar.cellClick(cal.currentDateEl, ev);\r
1071                 break;\r
1072             default:\r
1073                 return false;\r
1074         }\r
1075         return Calendar.stopEvent(ev);\r
1076 };\r
1078 /**\r
1079  *  (RE)Initializes the calendar to the given date and firstDayOfWeek\r
1080  */\r
1081 Calendar.prototype._init = function (firstDayOfWeek, date) {\r
1082         var today = new Date(),\r
1083                 TY = today.getFullYear(),\r
1084                 TM = today.getMonth(),\r
1085                 TD = today.getDate();\r
1086         this.table.style.visibility = "hidden";\r
1087         var year = date.getFullYear();\r
1088         if (year < this.minYear) {\r
1089                 year = this.minYear;\r
1090                 date.setFullYear(year);\r
1091         } else if (year > this.maxYear) {\r
1092                 year = this.maxYear;\r
1093                 date.setFullYear(year);\r
1094         }\r
1095         this.firstDayOfWeek = firstDayOfWeek;\r
1096         this.date = new Date(date);\r
1097         var month = date.getMonth();\r
1098         var mday = date.getDate();\r
1099         var no_days = date.getMonthDays();\r
1101         // calendar voodoo for computing the first day that would actually be\r
1102         // displayed in the calendar, even if it's from the previous month.\r
1103         // WARNING: this is magic. ;-)\r
1104         date.setDate(1);\r
1105         var day1 = (date.getDay() - this.firstDayOfWeek) % 7;\r
1106         if (day1 < 0)\r
1107                 day1 += 7;\r
1108         date.setDate(-day1);\r
1109         date.setDate(date.getDate() + 1);\r
1111         var row = this.tbody.firstChild;\r
1112         var MN = Calendar._SMN[month];\r
1113         var ar_days = this.ar_days = new Array();\r
1114         var weekend = Calendar._TT["WEEKEND"];\r
1115         var dates = this.multiple ? (this.datesCells = {}) : null;\r
1116         for (var i = 0; i < 6; ++i, row = row.nextSibling) {\r
1117                 var cell = row.firstChild;\r
1118                 if (this.weekNumbers) {\r
1119                         cell.className = "day wn";\r
1120                         cell.innerHTML = date.getWeekNumber();\r
1121                         cell = cell.nextSibling;\r
1122                 }\r
1123                 row.className = "daysrow";\r
1124                 var hasdays = false, iday, dpos = ar_days[i] = [];\r
1125                 for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {\r
1126                         iday = date.getDate();\r
1127                         var wday = date.getDay();\r
1128                         cell.className = "day";\r
1129                         cell.pos = i << 4 | j;\r
1130                         dpos[j] = cell;\r
1131                         var current_month = (date.getMonth() == month);\r
1132                         if (!current_month) {\r
1133                                 if (this.showsOtherMonths) {\r
1134                                         cell.className += " othermonth";\r
1135                                         cell.otherMonth = true;\r
1136                                 } else {\r
1137                                         cell.className = "emptycell";\r
1138                                         cell.innerHTML = "&nbsp;";\r
1139                                         cell.disabled = true;\r
1140                                         continue;\r
1141                                 }\r
1142                         } else {\r
1143                                 cell.otherMonth = false;\r
1144                                 hasdays = true;\r
1145                         }\r
1146                         cell.disabled = false;\r
1147                         cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;\r
1148                         if (dates)\r
1149                                 dates[date.print("%Y%m%d")] = cell;\r
1150                         if (this.getDateStatus) {\r
1151                                 var status = this.getDateStatus(date, year, month, iday);\r
1152                                 if (this.getDateToolTip) {\r
1153                                         var toolTip = this.getDateToolTip(date, year, month, iday);\r
1154                                         if (toolTip)\r
1155                                                 cell.title = toolTip;\r
1156                                 }\r
1157                                 if (status === true) {\r
1158                                         cell.className += " disabled";\r
1159                                         cell.disabled = true;\r
1160                                 } else {\r
1161                                         if (/disabled/i.test(status))\r
1162                                                 cell.disabled = true;\r
1163                                         cell.className += " " + status;\r
1164                                 }\r
1165                         }\r
1166                         if (!cell.disabled) {\r
1167                                 cell.caldate = new Date(date);\r
1168                                 cell.ttip = "_";\r
1169                                 if (!this.multiple && current_month\r
1170                                     && iday == mday && this.hiliteToday) {\r
1171                                         cell.className += " selected";\r
1172                                         this.currentDateEl = cell;\r
1173                                 }\r
1174                                 if (date.getFullYear() == TY &&\r
1175                                     date.getMonth() == TM &&\r
1176                                     iday == TD) {\r
1177                                         cell.className += " today";\r
1178                                         cell.ttip += Calendar._TT["PART_TODAY"];\r
1179                                 }\r
1180                                 if (weekend.indexOf(wday.toString()) != -1)\r
1181                                         cell.className += cell.otherMonth ? " oweekend" : " weekend";\r
1182                         }\r
1183                 }\r
1184                 if (!(hasdays || this.showsOtherMonths))\r
1185                         row.className = "emptyrow";\r
1186         }\r
1187         this.title.innerHTML = Calendar._MN[month] + ", " + year;\r
1188         this.onSetTime();\r
1189         this.table.style.visibility = "visible";\r
1190         this._initMultipleDates();\r
1191         // PROFILE\r
1192         // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";\r
1193 };\r
1195 Calendar.prototype._initMultipleDates = function() {\r
1196         if (this.multiple) {\r
1197                 for (var i in this.multiple) {\r
1198                         var cell = this.datesCells[i];\r
1199                         var d = this.multiple[i];\r
1200                         if (!d)\r
1201                                 continue;\r
1202                         if (cell)\r
1203                                 cell.className += " selected";\r
1204                 }\r
1205         }\r
1206 };\r
1208 Calendar.prototype._toggleMultipleDate = function(date) {\r
1209         if (this.multiple) {\r
1210                 var ds = date.print("%Y%m%d");\r
1211                 var cell = this.datesCells[ds];\r
1212                 if (cell) {\r
1213                         var d = this.multiple[ds];\r
1214                         if (!d) {\r
1215                                 Calendar.addClass(cell, "selected");\r
1216                                 this.multiple[ds] = date;\r
1217                         } else {\r
1218                                 Calendar.removeClass(cell, "selected");\r
1219                                 delete this.multiple[ds];\r
1220                         }\r
1221                 }\r
1222         }\r
1223 };\r
1225 Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {\r
1226         this.getDateToolTip = unaryFunction;\r
1227 };\r
1229 /**\r
1230  *  Calls _init function above for going to a certain date (but only if the\r
1231  *  date is different than the currently selected one).\r
1232  */\r
1233 Calendar.prototype.setDate = function (date) {\r
1234         if (!date.equalsTo(this.date)) {\r
1235                 this._init(this.firstDayOfWeek, date);\r
1236         }\r
1237 };\r
1239 /**\r
1240  *  Refreshes the calendar.  Useful if the "disabledHandler" function is\r
1241  *  dynamic, meaning that the list of disabled date can change at runtime.\r
1242  *  Just * call this function if you think that the list of disabled dates\r
1243  *  should * change.\r
1244  */\r
1245 Calendar.prototype.refresh = function () {\r
1246         this._init(this.firstDayOfWeek, this.date);\r
1247 };\r
1249 /** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */\r
1250 Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {\r
1251         this._init(firstDayOfWeek, this.date);\r
1252         this._displayWeekdays();\r
1253 };\r
1255 /**\r
1256  *  Allows customization of what dates are enabled.  The "unaryFunction"\r
1257  *  parameter must be a function object that receives the date (as a JS Date\r
1258  *  object) and returns a boolean value.  If the returned value is true then\r
1259  *  the passed date will be marked as disabled.\r
1260  */\r
1261 Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {\r
1262         this.getDateStatus = unaryFunction;\r
1263 };\r
1265 /** Customization of allowed year range for the calendar. */\r
1266 Calendar.prototype.setRange = function (a, z) {\r
1267         this.minYear = a;\r
1268         this.maxYear = z;\r
1269 };\r
1271 /** Calls the first user handler (selectedHandler). */\r
1272 Calendar.prototype.callHandler = function () {\r
1273         if (this.onSelected) {\r
1274                 this.onSelected(this, this.date.print(this.dateFormat));\r
1275         }\r
1276 };\r
1278 /** Calls the second user handler (closeHandler). */\r
1279 Calendar.prototype.callCloseHandler = function () {\r
1280         if (this.onClose) {\r
1281                 this.onClose(this);\r
1282         }\r
1283         this.hideShowCovered();\r
1284 };\r
1286 /** Removes the calendar object from the DOM tree and destroys it. */\r
1287 Calendar.prototype.destroy = function () {\r
1288         var el = this.element.parentNode;\r
1289         el.removeChild(this.element);\r
1290         Calendar._C = null;\r
1291         window._dynarch_popupCalendar = null;\r
1292 };\r
1294 /**\r
1295  *  Moves the calendar element to a different section in the DOM tree (changes\r
1296  *  its parent).\r
1297  */\r
1298 Calendar.prototype.reparent = function (new_parent) {\r
1299         var el = this.element;\r
1300         el.parentNode.removeChild(el);\r
1301         new_parent.appendChild(el);\r
1302 };\r
1304 // This gets called when the user presses a mouse button anywhere in the\r
1305 // document, if the calendar is shown.  If the click was outside the open\r
1306 // calendar this function closes it.\r
1307 Calendar._checkCalendar = function(ev) {\r
1308         var calendar = window._dynarch_popupCalendar;\r
1309         if (!calendar) {\r
1310                 return false;\r
1311         }\r
1312         var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);\r
1313         for (; el != null && el != calendar.element; el = el.parentNode);\r
1314         if (el == null) {\r
1315                 // calls closeHandler which should hide the calendar.\r
1316                 window._dynarch_popupCalendar.callCloseHandler();\r
1317                 return Calendar.stopEvent(ev);\r
1318         }\r
1319 };\r
1321 /** Shows the calendar. */\r
1322 Calendar.prototype.show = function () {\r
1323         var rows = this.table.getElementsByTagName("tr");\r
1324         for (var i = rows.length; i > 0;) {\r
1325                 var row = rows[--i];\r
1326                 Calendar.removeClass(row, "rowhilite");\r
1327                 var cells = row.getElementsByTagName("td");\r
1328                 for (var j = cells.length; j > 0;) {\r
1329                         var cell = cells[--j];\r
1330                         Calendar.removeClass(cell, "hilite");\r
1331                         Calendar.removeClass(cell, "active");\r
1332                 }\r
1333         }\r
1334         this.element.style.display = "block";\r
1335         this.hidden = false;\r
1336         if (this.isPopup) {\r
1337                 window._dynarch_popupCalendar = this;\r
1338                 Calendar.addEvent(document, "keydown", Calendar._keyEvent);\r
1339                 Calendar.addEvent(document, "keypress", Calendar._keyEvent);\r
1340                 Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);\r
1341         }\r
1342         this.hideShowCovered();\r
1343 };\r
1345 /**\r
1346  *  Hides the calendar.  Also removes any "hilite" from the class of any TD\r
1347  *  element.\r
1348  */\r
1349 Calendar.prototype.hide = function () {\r
1350         if (this.isPopup) {\r
1351                 Calendar.removeEvent(document, "keydown", Calendar._keyEvent);\r
1352                 Calendar.removeEvent(document, "keypress", Calendar._keyEvent);\r
1353                 Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);\r
1354         }\r
1355         this.element.style.display = "none";\r
1356         this.hidden = true;\r
1357         this.hideShowCovered();\r
1358 };\r
1360 /**\r
1361  *  Shows the calendar at a given absolute position (beware that, depending on\r
1362  *  the calendar element style -- position property -- this might be relative\r
1363  *  to the parent's containing rectangle).\r
1364  */\r
1365 Calendar.prototype.showAt = function (x, y) {\r
1366         var s = this.element.style;\r
1367         s.left = x + "px";\r
1368         s.top = y + "px";\r
1369         this.show();\r
1370 };\r
1372 /** Shows the calendar near a given element. */\r
1373 Calendar.prototype.showAtElement = function (el, opts) {\r
1374         var self = this;\r
1375         var p = Calendar.getAbsolutePos(el);\r
1376         if (!opts || typeof opts != "string") {\r
1377                 this.showAt(p.x, p.y + el.offsetHeight);\r
1378                 return true;\r
1379         }\r
1380         function fixPosition(box) {\r
1381                 if (box.x < 0)\r
1382                         box.x = 0;\r
1383                 if (box.y < 0)\r
1384                         box.y = 0;\r
1385                 var cp = document.createElement("div");\r
1386                 var s = cp.style;\r
1387                 s.position = "absolute";\r
1388                 s.right = s.bottom = s.width = s.height = "0px";\r
1389                 document.body.appendChild(cp);\r
1390                 var br = Calendar.getAbsolutePos(cp);\r
1391                 document.body.removeChild(cp);\r
1392                 if (Calendar.is_ie) {\r
1393                         br.y += document.body.scrollTop;\r
1394                         br.x += document.body.scrollLeft;\r
1395                 } else {\r
1396                         br.y += window.scrollY;\r
1397                         br.x += window.scrollX;\r
1398                 }\r
1399                 var tmp = box.x + box.width - br.x;\r
1400                 if (tmp > 0) box.x -= tmp;\r
1401                 tmp = box.y + box.height - br.y;\r
1402                 if (tmp > 0) box.y -= tmp;\r
1403         };\r
1404         this.element.style.display = "block";\r
1405         Calendar.continuation_for_the_fucking_khtml_browser = function() {\r
1406                 var w = self.element.offsetWidth;\r
1407                 var h = self.element.offsetHeight;\r
1408                 self.element.style.display = "none";\r
1409                 var valign = opts.substr(0, 1);\r
1410                 var halign = "l";\r
1411                 if (opts.length > 1) {\r
1412                         halign = opts.substr(1, 1);\r
1413                 }\r
1414                 // vertical alignment\r
1415                 switch (valign) {\r
1416                     case "T": p.y -= h; break;\r
1417                     case "B": p.y += el.offsetHeight; break;\r
1418                     case "C": p.y += (el.offsetHeight - h) / 2; break;\r
1419                     case "t": p.y += el.offsetHeight - h; break;\r
1420                     case "b": break; // already there\r
1421                 }\r
1422                 // horizontal alignment\r
1423                 switch (halign) {\r
1424                     case "L": p.x -= w; break;\r
1425                     case "R": p.x += el.offsetWidth; break;\r
1426                     case "C": p.x += (el.offsetWidth - w) / 2; break;\r
1427                     case "l": p.x += el.offsetWidth - w; break;\r
1428                     case "r": break; // already there\r
1429                 }\r
1430                 p.width = w;\r
1431                 p.height = h + 40;\r
1432                 self.monthsCombo.style.display = "none";\r
1433                 fixPosition(p);\r
1434                 self.showAt(p.x, p.y);\r
1435         };\r
1436         if (Calendar.is_khtml)\r
1437                 setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);\r
1438         else\r
1439                 Calendar.continuation_for_the_fucking_khtml_browser();\r
1440 };\r
1442 /** Customizes the date format. */\r
1443 Calendar.prototype.setDateFormat = function (str) {\r
1444         this.dateFormat = str;\r
1445 };\r
1447 /** Customizes the tooltip date format. */\r
1448 Calendar.prototype.setTtDateFormat = function (str) {\r
1449         this.ttDateFormat = str;\r
1450 };\r
1452 /**\r
1453  *  Tries to identify the date represented in a string.  If successful it also\r
1454  *  calls this.setDate which moves the calendar to the given date.\r
1455  */\r
1456 Calendar.prototype.parseDate = function(str, fmt) {\r
1457         if (!fmt)\r
1458                 fmt = this.dateFormat;\r
1459         this.setDate(Date.parseDate(str, fmt));\r
1460 };\r
1462 Calendar.prototype.hideShowCovered = function () {\r
1463         if (!Calendar.is_ie && !Calendar.is_opera)\r
1464                 return;\r
1465         function getVisib(obj){\r
1466                 var value = obj.style.visibility;\r
1467                 if (!value) {\r
1468                         if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C\r
1469                                 if (!Calendar.is_khtml)\r
1470                                         value = document.defaultView.\r
1471                                                 getComputedStyle(obj, "").getPropertyValue("visibility");\r
1472                                 else\r
1473                                         value = '';\r
1474                         } else if (obj.currentStyle) { // IE\r
1475                                 value = obj.currentStyle.visibility;\r
1476                         } else\r
1477                                 value = '';\r
1478                 }\r
1479                 return value;\r
1480         };\r
1482         var tags = new Array("applet", "iframe", "select");\r
1483         var el = this.element;\r
1485         var p = Calendar.getAbsolutePos(el);\r
1486         var EX1 = p.x;\r
1487         var EX2 = el.offsetWidth + EX1;\r
1488         var EY1 = p.y;\r
1489         var EY2 = el.offsetHeight + EY1;\r
1491         for (var k = tags.length; k > 0; ) {\r
1492                 var ar = document.getElementsByTagName(tags[--k]);\r
1493                 var cc = null;\r
1495                 for (var i = ar.length; i > 0;) {\r
1496                         cc = ar[--i];\r
1498                         p = Calendar.getAbsolutePos(cc);\r
1499                         var CX1 = p.x;\r
1500                         var CX2 = cc.offsetWidth + CX1;\r
1501                         var CY1 = p.y;\r
1502                         var CY2 = cc.offsetHeight + CY1;\r
1504                         if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {\r
1505                                 if (!cc.__msh_save_visibility) {\r
1506                                         cc.__msh_save_visibility = getVisib(cc);\r
1507                                 }\r
1508                                 cc.style.visibility = cc.__msh_save_visibility;\r
1509                         } else {\r
1510                                 if (!cc.__msh_save_visibility) {\r
1511                                         cc.__msh_save_visibility = getVisib(cc);\r
1512                                 }\r
1513                                 cc.style.visibility = "hidden";\r
1514                         }\r
1515                 }\r
1516         }\r
1517 };\r
1519 /** Internal function; it displays the bar with the names of the weekday. */\r
1520 Calendar.prototype._displayWeekdays = function () {\r
1521         var fdow = this.firstDayOfWeek;\r
1522         var cell = this.firstdayname;\r
1523         var weekend = Calendar._TT["WEEKEND"];\r
1524         for (var i = 0; i < 7; ++i) {\r
1525                 cell.className = "day name";\r
1526                 var realday = (i + fdow) % 7;\r
1527                 if (i) {\r
1528                         cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);\r
1529                         cell.navtype = 100;\r
1530                         cell.calendar = this;\r
1531                         cell.fdow = realday;\r
1532                         Calendar._add_evs(cell);\r
1533                 }\r
1534                 if (weekend.indexOf(realday.toString()) != -1) {\r
1535                         Calendar.addClass(cell, "weekend");\r
1536                 }\r
1537                 cell.innerHTML = Calendar._SDN[(i + fdow) % 7];\r
1538                 cell = cell.nextSibling;\r
1539         }\r
1540 };\r
1542 /** Internal function.  Hides all combo boxes that might be displayed. */\r
1543 Calendar.prototype._hideCombos = function () {\r
1544         this.monthsCombo.style.display = "none";\r
1545         this.yearsCombo.style.display = "none";\r
1546 };\r
1548 /** Internal function.  Starts dragging the element. */\r
1549 Calendar.prototype._dragStart = function (ev) {\r
1550         if (this.dragging) {\r
1551                 return;\r
1552         }\r
1553         this.dragging = true;\r
1554         var posX;\r
1555         var posY;\r
1556         if (Calendar.is_ie) {\r
1557                 posY = window.event.clientY + document.body.scrollTop;\r
1558                 posX = window.event.clientX + document.body.scrollLeft;\r
1559         } else {\r
1560                 posY = ev.clientY + window.scrollY;\r
1561                 posX = ev.clientX + window.scrollX;\r
1562         }\r
1563         var st = this.element.style;\r
1564         this.xOffs = posX - parseInt(st.left);\r
1565         this.yOffs = posY - parseInt(st.top);\r
1566         with (Calendar) {\r
1567                 addEvent(document, "mousemove", calDragIt);\r
1568                 addEvent(document, "mouseup", calDragEnd);\r
1569         }\r
1570 };\r
1572 // BEGIN: DATE OBJECT PATCHES\r
1574 /** Adds the number of days array to the Date object. */\r
1575 Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);\r
1577 /** Constants used for time computations */\r
1578 Date.SECOND = 1000 /* milliseconds */;\r
1579 Date.MINUTE = 60 * Date.SECOND;\r
1580 Date.HOUR   = 60 * Date.MINUTE;\r
1581 Date.DAY    = 24 * Date.HOUR;\r
1582 Date.WEEK   =  7 * Date.DAY;\r
1584 Date.parseDate = function(str, fmt) {\r
1585         var today = new Date();\r
1586         var y = 0;\r
1587         var m = -1;\r
1588         var d = 0;\r
1589         var a = str.split(/\W+/);\r
1590         var b = fmt.match(/%./g);\r
1591         var i = 0, j = 0;\r
1592         var hr = 0;\r
1593         var min = 0;\r
1594         for (i = 0; i < a.length; ++i) {\r
1595                 if (!a[i])\r
1596                         continue;\r
1597                 switch (b[i]) {\r
1598                     case "%d":\r
1599                     case "%e":\r
1600                         d = parseInt(a[i], 10);\r
1601                         break;\r
1603                     case "%m":\r
1604                         m = parseInt(a[i], 10) - 1;\r
1605                         break;\r
1607                     case "%Y":\r
1608                     case "%y":\r
1609                         y = parseInt(a[i], 10);\r
1610                         (y < 100) && (y += (y > 29) ? 1900 : 2000);\r
1611                         break;\r
1613                     case "%b":\r
1614                     case "%B":\r
1615                         for (j = 0; j < 12; ++j) {\r
1616                                 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }\r
1617                         }\r
1618                         break;\r
1620                     case "%H":\r
1621                     case "%I":\r
1622                     case "%k":\r
1623                     case "%l":\r
1624                         hr = parseInt(a[i], 10);\r
1625                         break;\r
1627                     case "%P":\r
1628                     case "%p":\r
1629                         if (/pm/i.test(a[i]) && hr < 12)\r
1630                                 hr += 12;\r
1631                         else if (/am/i.test(a[i]) && hr >= 12)\r
1632                                 hr -= 12;\r
1633                         break;\r
1635                     case "%M":\r
1636                         min = parseInt(a[i], 10);\r
1637                         break;\r
1638                 }\r
1639         }\r
1640         if (isNaN(y)) y = today.getFullYear();\r
1641         if (isNaN(m)) m = today.getMonth();\r
1642         if (isNaN(d)) d = today.getDate();\r
1643         if (isNaN(hr)) hr = today.getHours();\r
1644         if (isNaN(min)) min = today.getMinutes();\r
1645         if (y != 0 && m != -1 && d != 0)\r
1646                 return new Date(y, m, d, hr, min, 0);\r
1647         y = 0; m = -1; d = 0;\r
1648         for (i = 0; i < a.length; ++i) {\r
1649                 if (a[i].search(/[a-zA-Z]+/) != -1) {\r
1650                         var t = -1;\r
1651                         for (j = 0; j < 12; ++j) {\r
1652                                 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }\r
1653                         }\r
1654                         if (t != -1) {\r
1655                                 if (m != -1) {\r
1656                                         d = m+1;\r
1657                                 }\r
1658                                 m = t;\r
1659                         }\r
1660                 } else if (parseInt(a[i], 10) <= 12 && m == -1) {\r
1661                         m = a[i]-1;\r
1662                 } else if (parseInt(a[i], 10) > 31 && y == 0) {\r
1663                         y = parseInt(a[i], 10);\r
1664                         (y < 100) && (y += (y > 29) ? 1900 : 2000);\r
1665                 } else if (d == 0) {\r
1666                         d = a[i];\r
1667                 }\r
1668         }\r
1669         if (y == 0)\r
1670                 y = today.getFullYear();\r
1671         if (m != -1 && d != 0)\r
1672                 return new Date(y, m, d, hr, min, 0);\r
1673         return today;\r
1674 };\r
1676 /** Returns the number of days in the current month */\r
1677 Date.prototype.getMonthDays = function(month) {\r
1678         var year = this.getFullYear();\r
1679         if (typeof month == "undefined") {\r
1680                 month = this.getMonth();\r
1681         }\r
1682         if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {\r
1683                 return 29;\r
1684         } else {\r
1685                 return Date._MD[month];\r
1686         }\r
1687 };\r
1689 /** Returns the number of day in the year. */\r
1690 Date.prototype.getDayOfYear = function() {\r
1691         var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);\r
1692         var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);\r
1693         var time = now - then;\r
1694         return Math.floor(time / Date.DAY);\r
1695 };\r
1697 /** Returns the number of the week in year, as defined in ISO 8601. */\r
1698 Date.prototype.getWeekNumber = function() {\r
1699         var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);\r
1700         var DoW = d.getDay();\r
1701         d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu\r
1702         var ms = d.valueOf(); // GMT\r
1703         d.setMonth(0);\r
1704         d.setDate(4); // Thu in Week 1\r
1705         return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;\r
1706 };\r
1708 /** Checks date and time equality */\r
1709 Date.prototype.equalsTo = function(date) {\r
1710         return ((this.getFullYear() == date.getFullYear()) &&\r
1711                 (this.getMonth() == date.getMonth()) &&\r
1712                 (this.getDate() == date.getDate()) &&\r
1713                 (this.getHours() == date.getHours()) &&\r
1714                 (this.getMinutes() == date.getMinutes()));\r
1715 };\r
1717 /** Set only the year, month, date parts (keep existing time) */\r
1718 Date.prototype.setDateOnly = function(date) {\r
1719         var tmp = new Date(date);\r
1720         this.setDate(1);\r
1721         this.setFullYear(tmp.getFullYear());\r
1722         this.setMonth(tmp.getMonth());\r
1723         this.setDate(tmp.getDate());\r
1724 };\r
1726 /** Prints the date in a string according to the given format. */\r
1727 Date.prototype.print = function (str) {\r
1728         var m = this.getMonth();\r
1729         var d = this.getDate();\r
1730         var y = this.getFullYear();\r
1731         var wn = this.getWeekNumber();\r
1732         var w = this.getDay();\r
1733         var s = {};\r
1734         var hr = this.getHours();\r
1735         var pm = (hr >= 12);\r
1736         var ir = (pm) ? (hr - 12) : hr;\r
1737         var dy = this.getDayOfYear();\r
1738         if (ir == 0)\r
1739                 ir = 12;\r
1740         var min = this.getMinutes();\r
1741         var sec = this.getSeconds();\r
1742         s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]\r
1743         s["%A"] = Calendar._DN[w]; // full weekday name\r
1744         s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]\r
1745         s["%B"] = Calendar._MN[m]; // full month name\r
1746         // FIXME: %c : preferred date and time representation for the current locale\r
1747         s["%C"] = 1 + Math.floor(y / 100); // the century number\r
1748         s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)\r
1749         s["%e"] = d; // the day of the month (range 1 to 31)\r
1750         // FIXME: %D : american date style: %m/%d/%y\r
1751         // FIXME: %E, %F, %G, %g, %h (man strftime)\r
1752         s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)\r
1753         s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)\r
1754         s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)\r
1755         s["%k"] = hr;           // hour, range 0 to 23 (24h format)\r
1756         s["%l"] = ir;           // hour, range 1 to 12 (12h format)\r
1757         s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12\r
1758         s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59\r
1759         s["%n"] = "\n";         // a newline character\r
1760         s["%p"] = pm ? "PM" : "AM";\r
1761         s["%P"] = pm ? "pm" : "am";\r
1762         // FIXME: %r : the time in am/pm notation %I:%M:%S %p\r
1763         // FIXME: %R : the time in 24-hour notation %H:%M\r
1764         s["%s"] = Math.floor(this.getTime() / 1000);\r
1765         s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59\r
1766         s["%t"] = "\t";         // a tab character\r
1767         // FIXME: %T : the time in 24-hour notation (%H:%M:%S)\r
1768         s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;\r
1769         s["%u"] = w + 1;        // the day of the week (range 1 to 7, 1 = MON)\r
1770         s["%w"] = w;            // the day of the week (range 0 to 6, 0 = SUN)\r
1771         // FIXME: %x : preferred date representation for the current locale without the time\r
1772         // FIXME: %X : preferred time representation for the current locale without the date\r
1773         s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)\r
1774         s["%Y"] = y;            // year with the century\r
1775         s["%%"] = "%";          // a literal '%' character\r
1777         var re = /%./g;\r
1778         if (!Calendar.is_ie5 && !Calendar.is_khtml)\r
1779                 return str.replace(re, function (par) { return s[par] || par; });\r
1781         var a = str.match(re);\r
1782         for (var i = 0; i < a.length; i++) {\r
1783                 var tmp = s[a[i]];\r
1784                 if (tmp) {\r
1785                         re = new RegExp(a[i], 'g');\r
1786                         str = str.replace(re, tmp);\r
1787                 }\r
1788         }\r
1790         return str;\r
1791 };\r
1793 Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;\r
1794 Date.prototype.setFullYear = function(y) {\r
1795         var d = new Date(this);\r
1796         d.__msh_oldSetFullYear(y);\r
1797         if (d.getMonth() != this.getMonth())\r
1798                 this.setDate(28);\r
1799         this.__msh_oldSetFullYear(y);\r
1800 };\r
1802 // END: DATE OBJECT PATCHES\r
1805 // global object that remembers the calendar\r
1806 window._dynarch_popupCalendar = null;\r