PageSourceSearch

https://www.performancehealthacademy.com/js/calendar/calendar.js

js performancehealthacademy.com collected 2026-09-25 21:11:47 UTC 62,591 bytes, 1,912 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.