source: temp/test-xoops.ec-cube.net/html/include/calendar.js @ 405

Revision 405, 32.7 KB checked in by root, 20 years ago (diff)
Line 
1/*  Copyright Mihai Bazon, 2002  |  http://students.infoiasi.ro/~mishoo
2 * ---------------------------------------------------------------------
3 *
4 * The DHTML Calendar, version 0.9.2 "The art of date selection"
5 *
6 * Details and latest version at:
7 * http://students.infoiasi.ro/~mishoo/site/calendar.epl
8 *
9 * Feel free to use this script under the terms of the GNU Lesser General
10 * Public License, as long as you do not remove or alter this notice.
11 */
12
13// $Id: calendar.js,v 1.2 2005/03/18 12:51:55 onokazu Exp $
14
15/** The Calendar object constructor. */
16Calendar = function (mondayFirst, dateStr, onSelected, onClose) {
17    // member variables
18    this.activeDiv = null;
19    this.currentDateEl = null;
20    this.checkDisabled = null;
21    this.timeout = null;
22    this.onSelected = onSelected || null;
23    this.onClose = onClose || null;
24    this.dragging = false;
25    this.hidden = false;
26    this.minYear = 1970;
27    this.maxYear = 2050;
28    this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
29    this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
30    this.isPopup = true;
31    this.weekNumbers = true;
32    this.mondayFirst = mondayFirst;
33    this.dateStr = dateStr;
34    this.ar_days = null;
35    // HTML elements
36    this.table = null;
37    this.element = null;
38    this.tbody = null;
39    this.firstdayname = null;
40    // Combo boxes
41    this.monthsCombo = null;
42    this.yearsCombo = null;
43    this.hilitedMonth = null;
44    this.activeMonth = null;
45    this.hilitedYear = null;
46    this.activeYear = null;
47
48    // one-time initializations
49    if (!Calendar._DN3) {
50        // table of short day names
51        var ar = new Array();
52        for (var i = 8; i > 0;) {
53            ar[--i] = Calendar._DN[i].substr(0, 3);
54        }
55        Calendar._DN3 = ar;
56        // table of short month names
57        ar = new Array();
58        for (var i = 12; i > 0;) {
59            ar[--i] = Calendar._MN[i].substr(0, 3);
60        }
61        Calendar._MN3 = ar;
62    }
63};
64
65// ** constants
66
67/// "static", needed for event handlers.
68Calendar._C = null;
69
70/// detect a special case of "web browser"
71Calendar.is_ie = ( (navigator.userAgent.toLowerCase().indexOf("msie") != -1) &&
72           (navigator.userAgent.toLowerCase().indexOf("opera") == -1) );
73
74// short day names array (initialized at first constructor call)
75Calendar._DN3 = null;
76
77// short month names array (initialized at first constructor call)
78Calendar._MN3 = null;
79
80// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
81//        library, at some point.
82
83Calendar.getAbsolutePos = function(el) {
84    var r = { x: el.offsetLeft, y: el.offsetTop };
85    if (el.offsetParent) {
86        var tmp = Calendar.getAbsolutePos(el.offsetParent);
87        r.x += tmp.x;
88        r.y += tmp.y;
89    }
90    return r;
91};
92
93Calendar.isRelated = function (el, evt) {
94    var related = evt.relatedTarget;
95    if (!related) {
96        var type = evt.type;
97        if (type == "mouseover") {
98            related = evt.fromElement;
99        } else if (type == "mouseout") {
100            related = evt.toElement;
101        }
102    }
103    while (related) {
104        if (related == el) {
105            return true;
106        }
107        related = related.parentNode;
108    }
109    return false;
110};
111
112Calendar.removeClass = function(el, className) {
113    if (!(el && el.className)) {
114        return;
115    }
116    var cls = el.className.split(" ");
117    var ar = new Array();
118    for (var i = cls.length; i > 0;) {
119        if (cls[--i] != className) {
120            ar[ar.length] = cls[i];
121        }
122    }
123    el.className = ar.join(" ");
124};
125
126Calendar.addClass = function(el, className) {
127    Calendar.removeClass(el, className);
128    el.className += " " + className;
129};
130
131Calendar.getElement = function(ev) {
132    if (Calendar.is_ie) {
133        return window.event.srcElement;
134    } else {
135        return ev.currentTarget;
136    }
137};
138
139Calendar.getTargetElement = function(ev) {
140    if (Calendar.is_ie) {
141        return window.event.srcElement;
142    } else {
143        return ev.target;
144    }
145};
146
147Calendar.stopEvent = function(ev) {
148    if (Calendar.is_ie) {
149        window.event.cancelBubble = true;
150        window.event.returnValue = false;
151    } else {
152        ev.preventDefault();
153        ev.stopPropagation();
154    }
155};
156
157Calendar.addEvent = function(el, evname, func) {
158    if (Calendar.is_ie) {
159        el.attachEvent("on" + evname, func);
160    } else {
161        el.addEventListener(evname, func, true);
162    }
163};
164
165Calendar.removeEvent = function(el, evname, func) {
166    if (Calendar.is_ie) {
167        el.detachEvent("on" + evname, func);
168    } else {
169        el.removeEventListener(evname, func, true);
170    }
171};
172
173Calendar.createElement = function(type, parent) {
174    var el = null;
175    if (document.createElementNS) {
176        // use the XHTML namespace; IE won't normally get here unless
177        // _they_ "fix" the DOM2 implementation.
178        el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
179    } else {
180        el = document.createElement(type);
181    }
182    if (typeof parent != "undefined") {
183        parent.appendChild(el);
184    }
185    return el;
186};
187
188// END: UTILITY FUNCTIONS
189
190// BEGIN: CALENDAR STATIC FUNCTIONS
191
192/** Internal -- adds a set of events to make some element behave like a button. */
193Calendar._add_evs = function(el) {
194    with (Calendar) {
195        addEvent(el, "mouseover", dayMouseOver);
196        addEvent(el, "mousedown", dayMouseDown);
197        addEvent(el, "mouseout", dayMouseOut);
198        if (is_ie) {
199            addEvent(el, "dblclick", dayMouseDblClick);
200            el.setAttribute("unselectable", true);
201        }
202    }
203};
204
205Calendar.findMonth = function(el) {
206    if (typeof el.month != "undefined") {
207        return el;
208    } else if (typeof el.parentNode.month != "undefined") {
209        return el.parentNode;
210    }
211    return null;
212};
213
214Calendar.findYear = function(el) {
215    if (typeof el.year != "undefined") {
216        return el;
217    } else if (typeof el.parentNode.year != "undefined") {
218        return el.parentNode;
219    }
220    return null;
221};
222
223Calendar.showMonthsCombo = function () {
224    var cal = Calendar._C;
225    if (!cal) {
226        return false;
227    }
228    var cal = cal;
229    var cd = cal.activeDiv;
230    var mc = cal.monthsCombo;
231    if (cal.hilitedMonth) {
232        Calendar.removeClass(cal.hilitedMonth, "hilite");
233    }
234    if (cal.activeMonth) {
235        Calendar.removeClass(cal.activeMonth, "active");
236    }
237    var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
238    Calendar.addClass(mon, "active");
239    cal.activeMonth = mon;
240    mc.style.left = cd.offsetLeft + "px";
241    mc.style.top = (cd.offsetTop + cd.offsetHeight) + "px";
242    mc.style.display = "block";
243};
244
245Calendar.showYearsCombo = function (fwd) {
246    var cal = Calendar._C;
247    if (!cal) {
248        return false;
249    }
250    var cal = cal;
251    var cd = cal.activeDiv;
252    var yc = cal.yearsCombo;
253    if (cal.hilitedYear) {
254        Calendar.removeClass(cal.hilitedYear, "hilite");
255    }
256    if (cal.activeYear) {
257        Calendar.removeClass(cal.activeYear, "active");
258    }
259    cal.activeYear = null;
260    var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
261    var yr = yc.firstChild;
262    var show = false;
263    for (var i = 12; i > 0; --i) {
264        if (Y >= cal.minYear && Y <= cal.maxYear) {
265            yr.firstChild.data = Y;
266            yr.year = Y;
267            yr.style.display = "block";
268            show = true;
269        } else {
270            yr.style.display = "none";
271        }
272        yr = yr.nextSibling;
273        Y += fwd ? 2 : -2;
274    }
275    if (show) {
276        yc.style.left = cd.offsetLeft + "px";
277        yc.style.top = (cd.offsetTop + cd.offsetHeight) + "px";
278        yc.style.display = "block";
279    }
280};
281
282// event handlers
283
284Calendar.tableMouseUp = function(ev) {
285    var cal = Calendar._C;
286    if (!cal) {
287        return false;
288    }
289    if (cal.timeout) {
290        clearTimeout(cal.timeout);
291    }
292    var el = cal.activeDiv;
293    if (!el) {
294        return false;
295    }
296    var target = Calendar.getTargetElement(ev);
297    Calendar.removeClass(el, "active");
298    if (target == el || target.parentNode == el) {
299        Calendar.cellClick(el);
300    }
301    var mon = Calendar.findMonth(target);
302    var date = null;
303    if (mon) {
304        date = new Date(cal.date);
305        if (mon.month != date.getMonth()) {
306            date.setMonth(mon.month);
307            cal.setDate(date);
308        }
309    } else {
310        var year = Calendar.findYear(target);
311        if (year) {
312            date = new Date(cal.date);
313            if (year.year != date.getFullYear()) {
314                date.setFullYear(year.year);
315                cal.setDate(date);
316            }
317        }
318    }
319    with (Calendar) {
320        removeEvent(document, "mouseup", tableMouseUp);
321        removeEvent(document, "mouseover", tableMouseOver);
322        removeEvent(document, "mousemove", tableMouseOver);
323        cal._hideCombos();
324        stopEvent(ev);
325        _C = null;
326    }
327};
328
329Calendar.tableMouseOver = function (ev) {
330    var cal = Calendar._C;
331    if (!cal) {
332        return;
333    }
334    var el = cal.activeDiv;
335    var target = Calendar.getTargetElement(ev);
336    if (target == el || target.parentNode == el) {
337        Calendar.addClass(el, "hilite active");
338        Calendar.addClass(el.parentNode, "rowhilite");
339    } else {
340        Calendar.removeClass(el, "active");
341        Calendar.removeClass(el, "hilite");
342        Calendar.removeClass(el.parentNode, "rowhilite");
343    }
344    var mon = Calendar.findMonth(target);
345    if (mon) {
346        if (mon.month != cal.date.getMonth()) {
347            if (cal.hilitedMonth) {
348                Calendar.removeClass(cal.hilitedMonth, "hilite");
349            }
350            Calendar.addClass(mon, "hilite");
351            cal.hilitedMonth = mon;
352        } else if (cal.hilitedMonth) {
353            Calendar.removeClass(cal.hilitedMonth, "hilite");
354        }
355    } else {
356        var year = Calendar.findYear(target);
357        if (year) {
358            if (year.year != cal.date.getFullYear()) {
359                if (cal.hilitedYear) {
360                    Calendar.removeClass(cal.hilitedYear, "hilite");
361                }
362                Calendar.addClass(year, "hilite");
363                cal.hilitedYear = year;
364            } else if (cal.hilitedYear) {
365                Calendar.removeClass(cal.hilitedYear, "hilite");
366            }
367        }
368    }
369    Calendar.stopEvent(ev);
370};
371
372Calendar.tableMouseDown = function (ev) {
373    if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
374        Calendar.stopEvent(ev);
375    }
376};
377
378Calendar.calDragIt = function (ev) {
379    var cal = Calendar._C;
380    if (!(cal && cal.dragging)) {
381        return false;
382    }
383    var posX;
384    var posY;
385    if (Calendar.is_ie) {
386        posY = window.event.clientY + document.body.scrollTop;
387        posX = window.event.clientX + document.body.scrollLeft;
388    } else {
389        posX = ev.pageX;
390        posY = ev.pageY;
391    }
392    cal.hideShowCovered();
393    var st = cal.element.style;
394    st.left = (posX - cal.xOffs) + "px";
395    st.top = (posY - cal.yOffs) + "px";
396    Calendar.stopEvent(ev);
397};
398
399Calendar.calDragEnd = function (ev) {
400    var cal = Calendar._C;
401    if (!cal) {
402        return false;
403    }
404    cal.dragging = false;
405    with (Calendar) {
406        removeEvent(document, "mousemove", calDragIt);
407        removeEvent(document, "mouseover", stopEvent);
408        removeEvent(document, "mouseup", calDragEnd);
409        tableMouseUp(ev);
410    }
411    cal.hideShowCovered();
412};
413
414Calendar.dayMouseDown = function(ev) {
415    var el = Calendar.getElement(ev);
416    if (el.disabled) {
417        return false;
418    }
419    var cal = el.calendar;
420    cal.activeDiv = el;
421    Calendar._C = cal;
422    if (el.navtype != 300) with (Calendar) {
423        addClass(el, "hilite active");
424        addEvent(document, "mouseover", tableMouseOver);
425        addEvent(document, "mousemove", tableMouseOver);
426        addEvent(document, "mouseup", tableMouseUp);
427    } else if (cal.isPopup) {
428        cal._dragStart(ev);
429    }
430    Calendar.stopEvent(ev);
431    if (el.navtype == -1 || el.navtype == 1) {
432        cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
433    } else if (el.navtype == -2 || el.navtype == 2) {
434        cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
435    } else {
436        cal.timeout = null;
437    }
438};
439
440Calendar.dayMouseDblClick = function(ev) {
441    Calendar.cellClick(Calendar.getElement(ev));
442    if (Calendar.is_ie) {
443        document.selection.empty();
444    }
445};
446
447Calendar.dayMouseOver = function(ev) {
448    var el = Calendar.getElement(ev);
449    if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
450        return false;
451    }
452    if (el.ttip) {
453        if (el.ttip.substr(0, 1) == "_") {
454            var date = null;
455            with (el.calendar.date) {
456                date = new Date(getFullYear(), getMonth(), el.caldate);
457            }
458            el.ttip = date.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
459        }
460        el.calendar.tooltips.firstChild.data = el.ttip;
461    }
462    if (el.navtype != 300) {
463        Calendar.addClass(el, "hilite");
464        if (el.caldate) {
465            Calendar.addClass(el.parentNode, "rowhilite");
466        }
467    }
468    Calendar.stopEvent(ev);
469};
470
471Calendar.dayMouseOut = function(ev) {
472    with (Calendar) {
473        var el = getElement(ev);
474        if (isRelated(el, ev) || _C || el.disabled) {
475            return false;
476        }
477        removeClass(el, "hilite");
478        if (el.caldate) {
479            removeClass(el.parentNode, "rowhilite");
480        }
481        el.calendar.tooltips.firstChild.data = _TT["SEL_DATE"];
482        stopEvent(ev);
483    }
484};
485
486/**
487 *  A generic "click" handler :) handles all types of buttons defined in this
488 *  calendar.
489 */
490Calendar.cellClick = function(el) {
491    var cal = el.calendar;
492    var closing = false;
493    var newdate = false;
494    var date = null;
495    if (typeof el.navtype == "undefined") {
496        Calendar.removeClass(cal.currentDateEl, "selected");
497        Calendar.addClass(el, "selected");
498        closing = (cal.currentDateEl == el);
499        if (!closing) {
500            cal.currentDateEl = el;
501        }
502        cal.date.setDate(el.caldate);
503        date = cal.date;
504        newdate = true;
505    } else {
506        if (el.navtype == 200) {
507            Calendar.removeClass(el, "hilite");
508            cal.callCloseHandler();
509            return;
510        }
511        date = (el.navtype == 0) ? new Date() : new Date(cal.date);
512        var year = date.getFullYear();
513        var mon = date.getMonth();
514        function setMonth(m) {
515            var day = date.getDate();
516            var max = date.getMonthDays(m);
517            if (day > max) {
518                date.setDate(max);
519            }
520            date.setMonth(m);
521        };
522        switch (el.navtype) {
523            case -2:
524            if (year > cal.minYear) {
525                date.setFullYear(year - 1);
526            }
527            break;
528            case -1:
529            if (mon > 0) {
530                setMonth(mon - 1);
531            } else if (year-- > cal.minYear) {
532                date.setFullYear(year);
533                setMonth(11);
534            }
535            break;
536            case 1:
537            if (mon < 11) {
538                setMonth(mon + 1);
539            } else if (year < cal.maxYear) {
540                date.setFullYear(year + 1);
541                setMonth(0);
542            }
543            break;
544            case 2:
545            if (year < cal.maxYear) {
546                date.setFullYear(year + 1);
547            }
548            break;
549            case 100:
550            cal.setMondayFirst(!cal.mondayFirst);
551            return;
552        }
553        if (!date.equalsTo(cal.date)) {
554            cal.setDate(date);
555            newdate = el.navtype == 0;
556        }
557    }
558    if (newdate) {
559        cal.callHandler();
560    }
561    if (closing) {
562        Calendar.removeClass(el, "hilite");
563        cal.callCloseHandler();
564    }
565};
566
567// END: CALENDAR STATIC FUNCTIONS
568
569// BEGIN: CALENDAR OBJECT FUNCTIONS
570
571/**
572 *  This function creates the calendar inside the given parent.  If _par is
573 *  null than it creates a popup calendar inside the BODY element.  If _par is
574 *  an element, be it BODY, then it creates a non-popup calendar (still
575 *  hidden).  Some properties need to be set before calling this function.
576 */
577Calendar.prototype.create = function (_par) {
578    var parent = null;
579    if (! _par) {
580        // default parent is the document body, in which case we create
581        // a popup calendar.
582        parent = document.getElementsByTagName("body")[0];
583        this.isPopup = true;
584    } else {
585        parent = _par;
586        this.isPopup = false;
587    }
588    this.date = this.dateStr ? new Date(this.dateStr) : new Date();
589
590    var table = Calendar.createElement("table");
591    this.table = table;
592    table.cellSpacing = 0;
593    table.cellPadding = 0;
594    table.calendar = this;
595    Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
596
597    var div = Calendar.createElement("div");
598    this.element = div;
599    div.className = "calendar";
600    if (this.isPopup) {
601        div.style.position = "absolute";
602        div.style.display = "none";
603        div.style.width = "150px";
604    }
605    div.appendChild(table);
606
607    var thead = Calendar.createElement("thead", table);
608    var cell = null;
609    var row = null;
610
611    var cal = this;
612    var hh = function (text, cs, navtype) {
613        cell = Calendar.createElement("td", row);
614        cell.colSpan = cs;
615        cell.className = "button";
616        Calendar._add_evs(cell);
617        cell.calendar = cal;
618        cell.navtype = navtype;
619        if (text.substr(0, 1) != "&") {
620            cell.appendChild(document.createTextNode(text));
621        }
622        else {
623            // FIXME: dirty hack for entities
624            cell.innerHTML = text;
625        }
626        return cell;
627    };
628
629    row = Calendar.createElement("tr", thead);
630    var title_length = 6;
631    (this.isPopup) && --title_length;
632    (this.weekNumbers) && ++title_length;
633
634    hh("-", 1, 100).ttip = Calendar._TT["TOGGLE"];
635    this.title = hh("", title_length, 300);
636    this.title.className = "title";
637    if (this.isPopup) {
638        this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
639        this.title.style.cursor = "move";
640        hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
641    }
642
643    row = Calendar.createElement("tr", thead);
644    row.className = "headrow";
645
646    this._nav_py = hh("&#x00ab;", 1, -2);
647    this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
648
649    this._nav_pm = hh("&#x2039;", 1, -1);
650    this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
651
652    this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
653    this._nav_now.ttip = Calendar._TT["GO_TODAY"];
654
655    this._nav_nm = hh("&#x203a;", 1, 1);
656    this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
657
658    this._nav_ny = hh("&#x00bb;", 1, 2);
659    this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"]
660
661    // day names
662    row = Calendar.createElement("tr", thead);
663    row.className = "daynames";
664    if (this.weekNumbers) {
665        cell = Calendar.createElement("td", row);
666        cell.className = "name wn";
667        cell.appendChild(document.createTextNode(Calendar._TT["WK"]));
668    }
669    for (var i = 7; i > 0; --i) {
670        cell = Calendar.createElement("td", row);
671        cell.appendChild(document.createTextNode(""));
672        if (!i) {
673            cell.navtype = 100;
674            cell.calendar = this;
675            Calendar._add_evs(cell);
676        }
677    }
678    this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
679    this._displayWeekdays();
680
681    var tbody = Calendar.createElement("tbody", table);
682    this.tbody = tbody;
683
684    for (i = 6; i > 0; --i) {
685        row = Calendar.createElement("tr", tbody);
686        if (this.weekNumbers) {
687            cell = Calendar.createElement("td", row);
688            cell.appendChild(document.createTextNode(""));
689        }
690        for (var j = 7; j > 0; --j) {
691            cell = Calendar.createElement("td", row);
692            cell.appendChild(document.createTextNode(""));
693            cell.calendar = this;
694            Calendar._add_evs(cell);
695        }
696    }
697
698    var tfoot = Calendar.createElement("tfoot", table);
699
700    row = Calendar.createElement("tr", tfoot);
701    row.className = "footrow";
702
703    cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
704    cell.className = "ttip";
705    if (this.isPopup) {
706        cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
707        cell.style.cursor = "move";
708    }
709    this.tooltips = cell;
710
711    div = Calendar.createElement("div", this.element);
712    this.monthsCombo = div;
713    div.className = "combo";
714    for (i = 0; i < Calendar._MN.length; ++i) {
715        var mn = Calendar.createElement("div");
716        mn.className = "label";
717        mn.month = i;
718        mn.appendChild(document.createTextNode(Calendar._MN3[i]));
719        div.appendChild(mn);
720    }
721
722    div = Calendar.createElement("div", this.element);
723    this.yearsCombo = div;
724    div.className = "combo";
725    for (i = 12; i > 0; --i) {
726        var yr = Calendar.createElement("div");
727        yr.className = "label";
728        yr.appendChild(document.createTextNode(""));
729        div.appendChild(yr);
730    }
731
732    this._init(this.mondayFirst, this.date);
733    parent.appendChild(this.element);
734};
735
736/** keyboard navigation, only for popup calendars */
737Calendar._keyEvent = function(ev) {
738    if (!window.calendar) {
739        return false;
740    }
741    (Calendar.is_ie) && (ev = window.event);
742    var cal = window.calendar;
743    var act = (Calendar.is_ie || ev.type == "keypress");
744    if (ev.ctrlKey) {
745        switch (ev.keyCode) {
746            case 37: // KEY left
747            act && Calendar.cellClick(cal._nav_pm);
748            break;
749            case 38: // KEY up
750            act && Calendar.cellClick(cal._nav_py);
751            break;
752            case 39: // KEY right
753            act && Calendar.cellClick(cal._nav_nm);
754            break;
755            case 40: // KEY down
756            act && Calendar.cellClick(cal._nav_ny);
757            break;
758            default:
759            return false;
760        }
761    } else switch (ev.keyCode) {
762        case 32: // KEY space (now)
763        Calendar.cellClick(cal._nav_now);
764        break;
765        case 27: // KEY esc
766        act && cal.hide();
767        break;
768        case 37: // KEY left
769        case 38: // KEY up
770        case 39: // KEY right
771        case 40: // KEY down
772        if (act) {
773            var date = cal.date.getDate() - 1;
774            var el = cal.currentDateEl;
775            var ne = null;
776            var prev = (ev.keyCode == 37) || (ev.keyCode == 38);
777            switch (ev.keyCode) {
778                case 37: // KEY left
779                (--date >= 0) && (ne = cal.ar_days[date]);
780                break;
781                case 38: // KEY up
782                date -= 7;
783                (date >= 0) && (ne = cal.ar_days[date]);
784                break;
785                case 39: // KEY right
786                (++date < cal.ar_days.length) && (ne = cal.ar_days[date]);
787                break;
788                case 40: // KEY down
789                date += 7;
790                (date < cal.ar_days.length) && (ne = cal.ar_days[date]);
791                break;
792            }
793            if (!ne) {
794                if (prev) {
795                    Calendar.cellClick(cal._nav_pm);
796                } else {
797                    Calendar.cellClick(cal._nav_nm);
798                }
799                date = (prev) ? cal.date.getMonthDays() : 1;
800                el = cal.currentDateEl;
801                ne = cal.ar_days[date - 1];
802            }
803            Calendar.removeClass(el, "selected");
804            Calendar.addClass(ne, "selected");
805            cal.date.setDate(ne.caldate);
806            cal.currentDateEl = ne;
807        }
808        break;
809        case 13: // KEY enter
810        if (act) {
811            cal.callHandler();
812            cal.hide();
813        }
814        break;
815        default:
816        return false;
817    }
818    Calendar.stopEvent(ev);
819};
820
821/**
822 *  (RE)Initializes the calendar to the given date and style (if mondayFirst is
823 *  true it makes Monday the first day of week, otherwise the weeks start on
824 *  Sunday.
825 */
826Calendar.prototype._init = function (mondayFirst, date) {
827    var today = new Date();
828    var year = date.getFullYear();
829    if (year < this.minYear) {
830        year = this.minYear;
831        date.setFullYear(year);
832    } else if (year > this.maxYear) {
833        year = this.maxYear;
834        date.setFullYear(year);
835    }
836    this.mondayFirst = mondayFirst;
837    this.date = new Date(date);
838    var month = date.getMonth();
839    var mday = date.getDate();
840    var no_days = date.getMonthDays();
841    date.setDate(1);
842    var wday = date.getDay();
843    var MON = mondayFirst ? 1 : 0;
844    var SAT = mondayFirst ? 5 : 6;
845    var SUN = mondayFirst ? 6 : 0;
846    if (mondayFirst) {
847        wday = (wday > 0) ? (wday - 1) : 6;
848    }
849    var iday = 1;
850    var row = this.tbody.firstChild;
851    var MN = Calendar._MN3[month];
852    var hasToday = ((today.getFullYear() == year) && (today.getMonth() == month));
853    var todayDate = today.getDate();
854    var week_number = date.getWeekNumber();
855    var ar_days = new Array();
856    for (var i = 0; i < 6; ++i) {
857        if (iday > no_days) {
858            row.className = "emptyrow";
859            row = row.nextSibling;
860            continue;
861        }
862        var cell = row.firstChild;
863        if (this.weekNumbers) {
864            cell.className = "day wn";
865            cell.firstChild.data = week_number;
866            cell = cell.nextSibling;
867        }
868        ++week_number;
869        row.className = "daysrow";
870        for (var j = 0; j < 7; ++j) {
871            cell.className = "day";
872            if ((!i && j < wday) || iday > no_days) {
873                // cell.className = "emptycell";
874                cell.innerHTML = "&nbsp;";
875                cell.disabled = true;
876                cell = cell.nextSibling;
877                continue;
878            }
879            cell.disabled = false;
880            cell.firstChild.data = iday;
881            if (typeof this.checkDisabled == "function") {
882                date.setDate(iday);
883                if (this.checkDisabled(date)) {
884                    cell.className += " disabled";
885                    cell.disabled = true;
886                }
887            }
888            if (!cell.disabled) {
889                ar_days[ar_days.length] = cell;
890                cell.caldate = iday;
891                cell.ttip = "_";
892                if (iday == mday) {
893                    cell.className += " selected";
894                    this.currentDateEl = cell;
895                }
896                if (hasToday && (iday == todayDate)) {
897                    cell.className += " today";
898                    cell.ttip += Calendar._TT["PART_TODAY"];
899                }
900                if (wday == SAT || wday == SUN) {
901                    cell.className += " weekend";
902                }
903            }
904            ++iday;
905            ((++wday) ^ 7) || (wday = 0);
906            cell = cell.nextSibling;
907        }
908        row = row.nextSibling;
909    }
910    this.ar_days = ar_days;
911    this.title.firstChild.data = Calendar._MN[month] + ", " + year;
912    // PROFILE
913    // this.tooltips.firstChild.data = "Generated in " + ((new Date()) - today) + " ms";
914};
915
916/**
917 *  Calls _init function above for going to a certain date (but only if the
918 *  date is different than the currently selected one).
919 */
920Calendar.prototype.setDate = function (date) {
921    if (!date.equalsTo(this.date)) {
922        this._init(this.mondayFirst, date);
923    }
924};
925
926/** Modifies the "mondayFirst" parameter (EU/US style). */
927Calendar.prototype.setMondayFirst = function (mondayFirst) {
928    this._init(mondayFirst, this.date);
929    this._displayWeekdays();
930};
931
932/**
933 *  Allows customization of what dates are enabled.  The "unaryFunction"
934 *  parameter must be a function object that receives the date (as a JS Date
935 *  object) and returns a boolean value.  If the returned value is true then
936 *  the passed date will be marked as disabled.
937 */
938Calendar.prototype.setDisabledHandler = function (unaryFunction) {
939    this.checkDisabled = unaryFunction;
940};
941
942/** Customization of allowed year range for the calendar. */
943Calendar.prototype.setRange = function (a, z) {
944    this.minYear = a;
945    this.maxYear = z;
946};
947
948/** Calls the first user handler (selectedHandler). */
949Calendar.prototype.callHandler = function () {
950    if (this.onSelected) {
951        this.onSelected(this, this.date.print(this.dateFormat));
952    }
953};
954
955/** Calls the second user handler (closeHandler). */
956Calendar.prototype.callCloseHandler = function () {
957    if (this.onClose) {
958        this.onClose(this);
959    }
960    this.hideShowCovered();
961};
962
963/** Removes the calendar object from the DOM tree and destroys it. */
964Calendar.prototype.destroy = function () {
965    var el = this.element.parentNode;
966    el.removeChild(this.element);
967    Calendar._C = null;
968    delete el;
969};
970
971/**
972 *  Moves the calendar element to a different section in the DOM tree (changes
973 *  its parent).
974 */
975Calendar.prototype.reparent = function (new_parent) {
976    var el = this.element;
977    el.parentNode.removeChild(el);
978    new_parent.appendChild(el);
979};
980
981// This gets called when the user presses a mouse button anywhere in the
982// document, if the calendar is shown.  If the click was outside the open
983// calendar this function closes it.
984Calendar._checkCalendar = function(ev) {
985    if (!window.calendar) {
986        return false;
987    }
988    var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
989    for (; el != null && el != calendar.element; el = el.parentNode);
990    if (el == null) {
991        // calls closeHandler which should hide the calendar.
992        window.calendar.callCloseHandler();
993        Calendar.stopEvent(ev);
994    }
995};
996
997/** Shows the calendar. */
998Calendar.prototype.show = function () {
999    var rows = this.table.getElementsByTagName("tr");
1000    for (var i = rows.length; i > 0;) {
1001        var row = rows[--i];
1002        Calendar.removeClass(row, "rowhilite");
1003        var cells = row.getElementsByTagName("td");
1004        for (var j = cells.length; j > 0;) {
1005            var cell = cells[--j];
1006            Calendar.removeClass(cell, "hilite");
1007            Calendar.removeClass(cell, "active");
1008        }
1009    }
1010    this.element.style.display = "block";
1011    this.hidden = false;
1012    if (this.isPopup) {
1013        window.calendar = this;
1014        Calendar.addEvent(document, "keydown", Calendar._keyEvent);
1015        Calendar.addEvent(document, "keypress", Calendar._keyEvent);
1016        Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
1017    }
1018    this.hideShowCovered();
1019};
1020
1021/**
1022 *  Hides the calendar.  Also removes any "hilite" from the class of any TD
1023 *  element.
1024 */
1025Calendar.prototype.hide = function () {
1026    if (this.isPopup) {
1027        Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
1028        Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
1029        Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
1030    }
1031    this.element.style.display = "none";
1032    this.hidden = true;
1033    this.hideShowCovered();
1034};
1035
1036/**
1037 *  Shows the calendar at a given absolute position (beware that, depending on
1038 *  the calendar element style -- position property -- this might be relative
1039 *  to the parent's containing rectangle).
1040 */
1041Calendar.prototype.showAt = function (x, y) {
1042    var s = this.element.style;
1043    s.left = x + "px";
1044    s.top = y + "px";
1045    this.show();
1046};
1047
1048/** Shows the calendar near a given element. */
1049Calendar.prototype.showAtElement = function (el) {
1050    var p = Calendar.getAbsolutePos(el);
1051    this.showAt(p.x, p.y + el.offsetHeight);
1052};
1053
1054/** Customizes the date format. */
1055Calendar.prototype.setDateFormat = function (str) {
1056    this.dateFormat = str;
1057};
1058
1059/** Customizes the tooltip date format. */
1060Calendar.prototype.setTtDateFormat = function (str) {
1061    this.ttDateFormat = str;
1062};
1063
1064/**
1065 *  Tries to identify the date represented in a string.  If successful it also
1066 *  calls this.setDate which moves the calendar to the given date.
1067 */
1068Calendar.prototype.parseDate = function (str, fmt) {
1069    var y = 0;
1070    var m = -1;
1071    var d = 0;
1072    var a = str.split(/\W+/);
1073    if (!fmt) {
1074        fmt = this.dateFormat;
1075    }
1076    var b = fmt.split(/\W+/);
1077    var i = 0, j = 0;
1078    for (i = 0; i < a.length; ++i) {
1079        if (b[i] == "D" || b[i] == "DD") {
1080            continue;
1081        }
1082        if (b[i] == "d" || b[i] == "dd") {
1083            d = parseInt(a[i]);
1084        }
1085        if (b[i] == "m" || b[i] == "mm") {
1086            m = parseInt(a[i]) - 1;
1087        }
1088        if (b[i] == "y") {
1089            y = parseInt(a[i]);
1090        }
1091        if (b[i] == "yy") {
1092            y = parseInt(a[i]) + 1900;
1093        }
1094        if (b[i] == "M" || b[i] == "MM") {
1095            for (j = 0; j < 12; ++j) {
1096                if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
1097            }
1098        }
1099    }
1100    if (y != 0 && m != -1 && d != 0) {
1101        this.setDate(new Date(y, m, d));
1102        return;
1103    }
1104    y = 0; m = -1; d = 0;
1105    for (i = 0; i < a.length; ++i) {
1106        if (a[i].search(/[a-zA-Z]+/) != -1) {
1107            var t = -1;
1108            for (j = 0; j < 12; ++j) {
1109                if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
1110            }
1111            if (t != -1) {
1112                if (m != -1) {
1113                    d = m+1;
1114                }
1115                m = t;
1116            }
1117        } else if (parseInt(a[i]) <= 12 && m == -1) {
1118            m = a[i]-1;
1119        } else if (parseInt(a[i]) > 31 && y == 0) {
1120            y = a[i];
1121        } else if (d == 0) {
1122            d = a[i];
1123        }
1124    }
1125    if (y == 0) {
1126        var today = new Date();
1127        y = today.getFullYear();
1128    }
1129    if (m != -1 && d != 0) {
1130        this.setDate(new Date(y, m, d));
1131    }
1132};
1133
1134Calendar.prototype.hideShowCovered = function () {
1135    var tags = new Array("applet", "iframe", "select");
1136    var el = this.element;
1137
1138    var p = Calendar.getAbsolutePos(el);
1139    var EX1 = p.x;
1140    var EX2 = el.offsetWidth + EX1;
1141    var EY1 = p.y;
1142    var EY2 = el.offsetHeight + EY1;
1143
1144    for (var k = tags.length; k > 0; ) {
1145        var ar = document.getElementsByTagName(tags[--k]);
1146        var cc = null;
1147
1148        for (var i = ar.length; i > 0;) {
1149            cc = ar[--i];
1150
1151            p = Calendar.getAbsolutePos(cc);
1152            var CX1 = p.x;
1153            var CX2 = cc.offsetWidth + CX1;
1154            var CY1 = p.y;
1155            var CY2 = cc.offsetHeight + CY1;
1156
1157            if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
1158                cc.style.visibility = "visible";
1159            } else {
1160                cc.style.visibility = "hidden";
1161            }
1162        }
1163    }
1164};
1165
1166/** Internal function; it displays the bar with the names of the weekday. */
1167Calendar.prototype._displayWeekdays = function () {
1168    var MON = this.mondayFirst ? 0 : 1;
1169    var SUN = this.mondayFirst ? 6 : 0;
1170    var SAT = this.mondayFirst ? 5 : 6;
1171    var cell = this.firstdayname;
1172    for (var i = 0; i < 7; ++i) {
1173        cell.className = "day name";
1174        if (!i) {
1175            cell.ttip = this.mondayFirst ? Calendar._TT["SUN_FIRST"] : Calendar._TT["MON_FIRST"];
1176            cell.navtype = 100;
1177            cell.calendar = this;
1178            Calendar._add_evs(cell);
1179        }
1180        if (i == SUN || i == SAT) {
1181            Calendar.addClass(cell, "weekend");
1182        }
1183        cell.firstChild.data = Calendar._DN3[i + 1 - MON];
1184        cell = cell.nextSibling;
1185    }
1186};
1187
1188/** Internal function.  Hides all combo boxes that might be displayed. */
1189Calendar.prototype._hideCombos = function () {
1190    this.monthsCombo.style.display = "none";
1191    this.yearsCombo.style.display = "none";
1192};
1193
1194/** Internal function.  Starts dragging the element. */
1195Calendar.prototype._dragStart = function (ev) {
1196    if (this.dragging) {
1197        return;
1198    }
1199    this.dragging = true;
1200    var posX;
1201    var posY;
1202    if (Calendar.is_ie) {
1203        posY = window.event.clientY + document.body.scrollTop;
1204        posX = window.event.clientX + document.body.scrollLeft;
1205    } else {
1206        posY = ev.clientY + window.scrollY;
1207        posX = ev.clientX + window.scrollX;
1208    }
1209    var st = this.element.style;
1210    this.xOffs = posX - parseInt(st.left);
1211    this.yOffs = posY - parseInt(st.top);
1212    with (Calendar) {
1213        addEvent(document, "mousemove", calDragIt);
1214        addEvent(document, "mouseover", stopEvent);
1215        addEvent(document, "mouseup", calDragEnd);
1216    }
1217};
1218
1219// BEGIN: DATE OBJECT PATCHES
1220
1221/** Adds the number of days array to the Date object. */
1222Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
1223
1224/** Constants used for time computations */
1225Date.SECOND = 1000 /* milliseconds */;
1226Date.MINUTE = 60 * Date.SECOND;
1227Date.HOUR   = 60 * Date.MINUTE;
1228Date.DAY    = 24 * Date.HOUR;
1229Date.WEEK   =  7 * Date.DAY;
1230
1231/** Returns the number of days in the current month */
1232Date.prototype.getMonthDays = function(month) {
1233    var year = this.getFullYear();
1234    if (typeof month == "undefined") {
1235        month = this.getMonth();
1236    }
1237    if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
1238        return 29;
1239    } else {
1240        return Date._MD[month];
1241    }
1242};
1243
1244/** Returns the number of the week.  The algorithm was "stolen" from PPK's
1245 * website, hope it's correct :) http://www.xs4all.nl/~ppk/js/week.html */
1246Date.prototype.getWeekNumber = function() {
1247    var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
1248    var then = new Date(this.getFullYear(), 0, 1, 0, 0, 0);
1249    var time = now - then;
1250    var day = then.getDay();
1251    (day > 3) && (day -= 4) || (day += 3);
1252    return Math.round(((time / Date.DAY) + day) / 7);
1253};
1254
1255/** Checks dates equality (ignores time) */
1256Date.prototype.equalsTo = function(date) {
1257    return ((this.getFullYear() == date.getFullYear()) &&
1258        (this.getMonth() == date.getMonth()) &&
1259        (this.getDate() == date.getDate()));
1260};
1261
1262/** Prints the date in a string according to the given format. */
1263Date.prototype.print = function (frm) {
1264    var str = new String(frm);
1265    var m = this.getMonth();
1266    var d = this.getDate();
1267    var y = this.getFullYear();
1268    var wn = this.getWeekNumber();
1269    var w = this.getDay();
1270    var s = new Array();
1271    s["d"] = d;
1272    s["dd"] = (d < 10) ? ("0" + d) : d;
1273    s["m"] = 1+m;
1274    s["mm"] = (m < 9) ? ("0" + (1+m)) : (1+m);
1275    s["y"] = y;
1276    s["yy"] = new String(y).substr(2, 2);
1277    s["w"] = wn;
1278    s["ww"] = (wn < 10) ? ("0" + wn) : wn;
1279    with (Calendar) {
1280        s["D"] = _DN3[w];
1281        s["DD"] = _DN[w];
1282        s["M"] = _MN3[m];
1283        s["MM"] = _MN[m];
1284    }
1285    var re = /(.*)(\W|^)(d|dd|m|mm|y|yy|MM|M|DD|D|w|ww)(\W|$)(.*)/;
1286    while (re.exec(str) != null) {
1287        str = RegExp.$1 + RegExp.$2 + s[RegExp.$3] + RegExp.$4 + RegExp.$5;
1288    }
1289    return str;
1290};
1291
1292// END: DATE OBJECT PATCHES
1293
1294// global object that remembers the calendar
1295window.calendar = null;
Note: See TracBrowser for help on using the repository browser.