source: trunk/workflow/js/jscalendar/calendar.js @ 795

Revision 795, 48.0 KB checked in by viani, 15 years ago (diff)

Ticket #488 - Inclusão do módulo workflow no ramo trunk do repositório Expresso.

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