1 /* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
2 * -----------------------------------------------------------
4 * The DHTML Calendar, version 1.0 "It is happening again"
6 * Details and latest version at:
7 * www.dynarch.com/projects/calendar
9 * This script is developed by Dynarch.com. Visit us at www.dynarch.com.
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
15 // $Id: calendar.js,v 1.51 2005/03/07 16:44:31 mishoo Exp $
17 /** The Calendar object constructor. */
18 Calendar = function (firstDayOfWeek
, dateStr
, onSelected
, onClose
) {
20 this.activeDiv
= null;
21 this.currentDateEl
= null;
22 this.getDateStatus
= null;
23 this.getDateToolTip
= null;
24 this.getDateText
= null;
26 this.onSelected
= onSelected
|| null;
27 this.onClose
= onClose
|| null;
28 this.dragging
= false;
32 this.dateFormat
= Calendar
._TT
["DEF_DATE_FORMAT"];
33 this.ttDateFormat
= Calendar
._TT
["TT_DATE_FORMAT"];
35 this.weekNumbers
= true;
36 this.firstDayOfWeek
= typeof firstDayOfWeek
== "number" ? firstDayOfWeek
: Calendar
._FD
; // 0 for Sunday, 1 for Monday, etc.
37 this.showsOtherMonths
= false;
38 this.dateStr
= dateStr
;
40 this.showsTime
= false;
43 this.hiliteToday
= true;
49 this.firstdayname
= null;
51 this.monthsCombo
= null;
52 this.yearsCombo
= null;
53 this.hilitedMonth
= null;
54 this.activeMonth
= null;
55 this.hilitedYear
= null;
56 this.activeYear
= null;
58 this.dateClicked
= false;
60 // one-time initializations
61 if (typeof Calendar
._SDN
== "undefined") {
62 // table of short day names
63 if (typeof Calendar
._SDN_len
== "undefined")
64 Calendar
._SDN_len
= 3;
66 for (var i
= 8; i
> 0;) {
67 ar
[--i
] = Calendar
._DN
[i
].substr(0, Calendar
._SDN_len
);
70 // table of short month names
71 if (typeof Calendar
._SMN_len
== "undefined")
72 Calendar
._SMN_len
= 3;
74 for (var i
= 12; i
> 0;) {
75 ar
[--i
] = Calendar
._MN
[i
].substr(0, Calendar
._SMN_len
);
83 /// "static", needed for event handlers.
86 /// detect a special case of "web browser"
87 Calendar
.is_ie
= ( /msie/i.test(navigator
.userAgent
) &&
88 !/opera/i.test(navigator
.userAgent
) );
90 Calendar
.is_ie5
= ( Calendar
.is_ie
&& /msie 5\.0/i.test(navigator
.userAgent
) );
92 /// detect Opera browser
93 Calendar
.is_opera
= /opera/i.test(navigator
.userAgent
);
95 /// detect KHTML-based browsers
96 Calendar
.is_khtml
= /Konqueror|Safari|KHTML/i.test(navigator
.userAgent
);
98 // BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
99 // library, at some point.
101 Calendar
.getAbsolutePos = function(el
) {
103 var is_div
= /^div$/i.test(el
.tagName
);
104 if (is_div
&& el
.scrollLeft
)
106 if (is_div
&& el
.scrollTop
)
108 var r
= { x
: el
.offsetLeft
- SL
, y
: el
.offsetTop
- ST
};
109 if (el
.offsetParent
) {
110 var tmp
= this.getAbsolutePos(el
.offsetParent
);
117 Calendar
.isRelated = function (el
, evt
) {
118 var related
= evt
.relatedTarget
;
121 if (type
== "mouseover") {
122 related
= evt
.fromElement
;
123 } else if (type
== "mouseout") {
124 related
= evt
.toElement
;
131 related
= related
.parentNode
;
136 Calendar
.removeClass = function(el
, className
) {
137 if (!(el
&& el
.className
)) {
140 var cls
= el
.className
.split(" ");
141 var ar
= new Array();
142 for (var i
= cls
.length
; i
> 0;) {
143 if (cls
[--i
] != className
) {
144 ar
[ar
.length
] = cls
[i
];
147 el
.className
= ar
.join(" ");
150 Calendar
.addClass = function(el
, className
) {
151 Calendar
.removeClass(el
, className
);
152 el
.className
+= " " + className
;
155 // FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
156 Calendar
.getElement = function(ev
) {
157 var f
= Calendar
.is_ie
? window
.event
.srcElement
: ev
.currentTarget
;
158 while (f
.nodeType
!= 1 || /^div$/i.test(f
.tagName
))
163 Calendar
.getTargetElement = function(ev
) {
164 var f
= Calendar
.is_ie
? window
.event
.srcElement
: ev
.target
;
165 while (f
.nodeType
!= 1)
170 Calendar
.stopEvent = function(ev
) {
171 ev
|| (ev
= window
.event
);
172 if (Calendar
.is_ie
) {
173 ev
.cancelBubble
= true;
174 ev
.returnValue
= false;
177 ev
.stopPropagation();
182 Calendar
.addEvent = function(el
, evname
, func
) {
183 if (el
.attachEvent
) { // IE
184 el
.attachEvent("on" + evname
, func
);
185 } else if (el
.addEventListener
) { // Gecko / W3C
186 el
.addEventListener(evname
, func
, true);
188 el
["on" + evname
] = func
;
192 Calendar
.removeEvent = function(el
, evname
, func
) {
193 if (el
.detachEvent
) { // IE
194 el
.detachEvent("on" + evname
, func
);
195 } else if (el
.removeEventListener
) { // Gecko / W3C
196 el
.removeEventListener(evname
, func
, true);
198 el
["on" + evname
] = null;
202 Calendar
.createElement = function(type
, parent
) {
204 if (document
.createElementNS
) {
205 // use the XHTML namespace; IE won't normally get here unless
206 // _they_ "fix" the DOM2 implementation.
207 el
= document
.createElementNS("http://www.w3.org/1999/xhtml", type
);
209 el
= document
.createElement(type
);
211 if (typeof parent
!= "undefined") {
212 parent
.appendChild(el
);
217 // END: UTILITY FUNCTIONS
219 // BEGIN: CALENDAR STATIC FUNCTIONS
221 /** Internal -- adds a set of events to make some element behave like a button. */
222 Calendar
._add_evs = function(el
) {
224 addEvent(el
, "mouseover", dayMouseOver
);
225 addEvent(el
, "mousedown", dayMouseDown
);
226 addEvent(el
, "mouseout", dayMouseOut
);
228 addEvent(el
, "dblclick", dayMouseDblClick
);
229 el
.setAttribute("unselectable", true);
234 Calendar
.findMonth = function(el
) {
235 if (typeof el
.month
!= "undefined") {
237 } else if (typeof el
.parentNode
.month
!= "undefined") {
238 return el
.parentNode
;
243 Calendar
.findYear = function(el
) {
244 if (typeof el
.year
!= "undefined") {
246 } else if (typeof el
.parentNode
.year
!= "undefined") {
247 return el
.parentNode
;
252 Calendar
.showMonthsCombo = function () {
253 var cal
= Calendar
._C
;
258 var cd
= cal
.activeDiv
;
259 var mc
= cal
.monthsCombo
;
260 if (cal
.hilitedMonth
) {
261 Calendar
.removeClass(cal
.hilitedMonth
, "hilite");
263 if (cal
.activeMonth
) {
264 Calendar
.removeClass(cal
.activeMonth
, "active");
266 var mon
= cal
.monthsCombo
.getElementsByTagName("div")[cal
.date
.getMonth()];
267 Calendar
.addClass(mon
, "active");
268 cal
.activeMonth
= mon
;
272 s
.left
= cd
.offsetLeft
+ "px";
274 var mcw
= mc
.offsetWidth
;
275 if (typeof mcw
== "undefined")
276 // Konqueror brain-dead techniques
278 s
.left
= (cd
.offsetLeft
+ cd
.offsetWidth
- mcw
) + "px";
280 s
.top
= (cd
.offsetTop
+ cd
.offsetHeight
) + "px";
283 Calendar
.showYearsCombo = function (fwd
) {
284 var cal
= Calendar
._C
;
289 var cd
= cal
.activeDiv
;
290 var yc
= cal
.yearsCombo
;
291 if (cal
.hilitedYear
) {
292 Calendar
.removeClass(cal
.hilitedYear
, "hilite");
294 if (cal
.activeYear
) {
295 Calendar
.removeClass(cal
.activeYear
, "active");
297 cal
.activeYear
= null;
298 var Y
= cal
.date
.getFullYear() + (fwd
? 1 : -1);
299 var yr
= yc
.firstChild
;
301 for (var i
= 12; i
> 0; --i
) {
302 if (Y
>= cal
.minYear
&& Y
<= cal
.maxYear
) {
305 yr
.style
.display
= "block";
308 yr
.style
.display
= "none";
311 Y
+= fwd
? cal
.yearStep
: -cal
.yearStep
;
317 s
.left
= cd
.offsetLeft
+ "px";
319 var ycw
= yc
.offsetWidth
;
320 if (typeof ycw
== "undefined")
321 // Konqueror brain-dead techniques
323 s
.left
= (cd
.offsetLeft
+ cd
.offsetWidth
- ycw
) + "px";
325 s
.top
= (cd
.offsetTop
+ cd
.offsetHeight
) + "px";
331 Calendar
.tableMouseUp = function(ev
) {
332 var cal
= Calendar
._C
;
337 clearTimeout(cal
.timeout
);
339 var el
= cal
.activeDiv
;
343 var target
= Calendar
.getTargetElement(ev
);
344 ev
|| (ev
= window
.event
);
345 Calendar
.removeClass(el
, "active");
346 if (target
== el
|| target
.parentNode
== el
) {
347 Calendar
.cellClick(el
, ev
);
349 var mon
= Calendar
.findMonth(target
);
352 date
= new Date(cal
.date
);
353 if (mon
.month
!= date
.getMonth()) {
354 date
.setMonth(mon
.month
);
356 cal
.dateClicked
= false;
360 var year
= Calendar
.findYear(target
);
362 date
= new Date(cal
.date
);
363 if (year
.year
!= date
.getFullYear()) {
364 date
.setFullYear(year
.year
);
366 cal
.dateClicked
= false;
372 removeEvent(document
, "mouseup", tableMouseUp
);
373 removeEvent(document
, "mouseover", tableMouseOver
);
374 removeEvent(document
, "mousemove", tableMouseOver
);
377 return stopEvent(ev
);
381 Calendar
.tableMouseOver = function (ev
) {
382 var cal
= Calendar
._C
;
386 var el
= cal
.activeDiv
;
387 var target
= Calendar
.getTargetElement(ev
);
388 if (target
== el
|| target
.parentNode
== el
) {
389 Calendar
.addClass(el
, "hilite active");
390 Calendar
.addClass(el
.parentNode
, "rowhilite");
392 if (typeof el
.navtype
== "undefined" || (el
.navtype
!= 50 && (el
.navtype
== 0 || Math
.abs(el
.navtype
) > 2)))
393 Calendar
.removeClass(el
, "active");
394 Calendar
.removeClass(el
, "hilite");
395 Calendar
.removeClass(el
.parentNode
, "rowhilite");
397 ev
|| (ev
= window
.event
);
398 if (el
.navtype
== 50 && target
!= el
) {
399 var pos
= Calendar
.getAbsolutePos(el
);
400 var w
= el
.offsetWidth
;
411 var range
= el
._range
;
412 var current
= el
._current
;
413 var count
= Math
.floor(dx
/ 10) % range
.length
;
414 for (var i
= range
.length
; --i
>= 0;)
415 if (range
[i
] == current
)
420 i
= range
.length
- 1;
421 } else if ( ++i
>= range
.length
)
423 var newval
= range
[i
];
424 el
.innerHTML
= newval
;
428 var mon
= Calendar
.findMonth(target
);
430 if (mon
.month
!= cal
.date
.getMonth()) {
431 if (cal
.hilitedMonth
) {
432 Calendar
.removeClass(cal
.hilitedMonth
, "hilite");
434 Calendar
.addClass(mon
, "hilite");
435 cal
.hilitedMonth
= mon
;
436 } else if (cal
.hilitedMonth
) {
437 Calendar
.removeClass(cal
.hilitedMonth
, "hilite");
440 if (cal
.hilitedMonth
) {
441 Calendar
.removeClass(cal
.hilitedMonth
, "hilite");
443 var year
= Calendar
.findYear(target
);
445 if (year
.year
!= cal
.date
.getFullYear()) {
446 if (cal
.hilitedYear
) {
447 Calendar
.removeClass(cal
.hilitedYear
, "hilite");
449 Calendar
.addClass(year
, "hilite");
450 cal
.hilitedYear
= year
;
451 } else if (cal
.hilitedYear
) {
452 Calendar
.removeClass(cal
.hilitedYear
, "hilite");
454 } else if (cal
.hilitedYear
) {
455 Calendar
.removeClass(cal
.hilitedYear
, "hilite");
458 return Calendar
.stopEvent(ev
);
461 Calendar
.tableMouseDown = function (ev
) {
462 if (Calendar
.getTargetElement(ev
) == Calendar
.getElement(ev
)) {
463 return Calendar
.stopEvent(ev
);
467 Calendar
.calDragIt = function (ev
) {
468 var cal
= Calendar
._C
;
469 if (!(cal
&& cal
.dragging
)) {
474 if (Calendar
.is_ie
) {
475 posY
= window
.event
.clientY
+ document
.body
.scrollTop
;
476 posX
= window
.event
.clientX
+ document
.body
.scrollLeft
;
481 cal
.hideShowCovered();
482 var st
= cal
.element
.style
;
483 st
.left
= (posX
- cal
.xOffs
) + "px";
484 st
.top
= (posY
- cal
.yOffs
) + "px";
485 return Calendar
.stopEvent(ev
);
488 Calendar
.calDragEnd = function (ev
) {
489 var cal
= Calendar
._C
;
493 cal
.dragging
= false;
495 removeEvent(document
, "mousemove", calDragIt
);
496 removeEvent(document
, "mouseup", calDragEnd
);
499 cal
.hideShowCovered();
502 Calendar
.dayMouseDown = function(ev
) {
503 var el
= Calendar
.getElement(ev
);
507 var cal
= el
.calendar
;
510 if (el
.navtype
!= 300) with (Calendar
) {
511 if (el
.navtype
== 50) {
512 el
._current
= el
.innerHTML
;
513 addEvent(document
, "mousemove", tableMouseOver
);
515 addEvent(document
, Calendar
.is_ie5
? "mousemove" : "mouseover", tableMouseOver
);
516 addClass(el
, "hilite active");
517 addEvent(document
, "mouseup", tableMouseUp
);
518 } else if (cal
.isPopup
) {
521 if (el
.navtype
== -1 || el
.navtype
== 1) {
522 if (cal
.timeout
) clearTimeout(cal
.timeout
);
523 cal
.timeout
= setTimeout("Calendar.showMonthsCombo()", 250);
524 } else if (el
.navtype
== -2 || el
.navtype
== 2) {
525 if (cal
.timeout
) clearTimeout(cal
.timeout
);
526 cal
.timeout
= setTimeout((el
.navtype
> 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
530 return Calendar
.stopEvent(ev
);
533 Calendar
.dayMouseDblClick = function(ev
) {
534 Calendar
.cellClick(Calendar
.getElement(ev
), ev
|| window
.event
);
535 if (Calendar
.is_ie
) {
536 document
.selection
.empty();
540 Calendar
.dayMouseOver = function(ev
) {
541 var el
= Calendar
.getElement(ev
);
542 if (Calendar
.isRelated(el
, ev
) || Calendar
._C
|| el
.disabled
) {
546 if (el
.ttip
.substr(0, 1) == "_") {
547 el
.ttip
= el
.caldate
.print(el
.calendar
.ttDateFormat
) + el
.ttip
.substr(1);
549 el
.calendar
.tooltips
.innerHTML
= el
.ttip
;
551 if (el
.navtype
!= 300) {
552 Calendar
.addClass(el
, "hilite");
554 Calendar
.addClass(el
.parentNode
, "rowhilite");
557 return Calendar
.stopEvent(ev
);
560 Calendar
.dayMouseOut = function(ev
) {
562 var el
= getElement(ev
);
563 if (isRelated(el
, ev
) || _C
|| el
.disabled
)
565 removeClass(el
, "hilite");
567 removeClass(el
.parentNode
, "rowhilite");
569 el
.calendar
.tooltips
.innerHTML
= _TT
["SEL_DATE"];
570 return stopEvent(ev
);
575 * A generic "click" handler :) handles all types of buttons defined in this
578 Calendar
.cellClick = function(el
, ev
) {
579 var cal
= el
.calendar
;
583 if (typeof el
.navtype
== "undefined") {
584 if (cal
.currentDateEl
) {
585 Calendar
.removeClass(cal
.currentDateEl
, "selected");
586 Calendar
.addClass(el
, "selected");
587 closing
= (cal
.currentDateEl
== el
);
589 cal
.currentDateEl
= el
;
592 cal
.date
.setDateOnly(el
.caldate
);
594 var other_month
= !(cal
.dateClicked
= !el
.otherMonth
);
595 if (!other_month
&& !cal
.currentDateEl
)
596 cal
._toggleMultipleDate(new Date(date
));
598 newdate
= !el
.disabled
;
599 // a date was clicked
601 cal
._init(cal
.firstDayOfWeek
, date
);
603 if (el
.navtype
== 200) {
604 Calendar
.removeClass(el
, "hilite");
605 cal
.callCloseHandler();
608 date
= new Date(cal
.date
);
610 date
.setDateOnly(new Date()); // TODAY
611 // unless "today" was clicked, we assume no date was clicked so
612 // the selected handler will know not to close the calenar when
613 // in single-click mode.
614 // cal.dateClicked = (el.navtype == 0);
615 cal
.dateClicked
= false;
616 var year
= date
.getFullYear();
617 var mon
= date
.getMonth();
618 function setMonth(m
) {
619 var day
= date
.getDate();
620 var max
= date
.getMonthDays(m
);
626 switch (el
.navtype
) {
628 Calendar
.removeClass(el
, "hilite");
629 var text
= Calendar
._TT
["ABOUT"];
630 if (typeof text
!= "undefined") {
631 text
+= cal
.showsTime
? Calendar
._TT
["ABOUT_TIME"] : "";
633 // FIXME: this should be removed as soon as lang files get updated!
634 text
= "Help and about box text is not translated into this language.\n" +
635 "If you know this language and you feel generous please update\n" +
636 "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
637 "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution ;-)\n\n" +
639 "http://dynarch.com/mishoo/calendar.epl\n";
644 if (year
> cal
.minYear
) {
645 date
.setFullYear(year
- 1);
651 } else if (year
-- > cal
.minYear
) {
652 date
.setFullYear(year
);
659 } else if (year
< cal
.maxYear
) {
660 date
.setFullYear(year
+ 1);
665 if (year
< cal
.maxYear
) {
666 date
.setFullYear(year
+ 1);
670 cal
.setFirstDayOfWeek(el
.fdow
);
673 var range
= el
._range
;
674 var current
= el
.innerHTML
;
675 for (var i
= range
.length
; --i
>= 0;)
676 if (range
[i
] == current
)
678 if (ev
&& ev
.shiftKey
) {
680 i
= range
.length
- 1;
681 } else if ( ++i
>= range
.length
)
683 var newval
= range
[i
];
684 el
.innerHTML
= newval
;
688 // TODAY will bring us here
689 if ((typeof cal
.getDateStatus
== "function") &&
690 cal
.getDateStatus(date
, date
.getFullYear(), date
.getMonth(), date
.getDate())) {
695 if (!date
.equalsTo(cal
.date
)) {
698 } else if (el
.navtype
== 0)
699 newdate
= closing
= true;
702 ev
&& cal
.callHandler();
705 Calendar
.removeClass(el
, "hilite");
706 ev
&& cal
.callCloseHandler();
710 // END: CALENDAR STATIC FUNCTIONS
712 // BEGIN: CALENDAR OBJECT FUNCTIONS
715 * This function creates the calendar inside the given parent. If _par is
716 * null than it creates a popup calendar inside the BODY element. If _par is
717 * an element, be it BODY, then it creates a non-popup calendar (still
718 * hidden). Some properties need to be set before calling this function.
720 Calendar
.prototype.create = function (_par
) {
723 // default parent is the document body, in which case we create
725 parent
= document
.getElementsByTagName("body")[0];
729 this.isPopup
= false;
731 this.date
= this.dateStr
? new Date(this.dateStr
) : new Date();
733 var table
= Calendar
.createElement("table");
735 table
.cellSpacing
= 0;
736 table
.cellPadding
= 0;
737 table
.calendar
= this;
738 Calendar
.addEvent(table
, "mousedown", Calendar
.tableMouseDown
);
740 var div
= Calendar
.createElement("div");
742 div
.className
= "calendar";
744 div
.style
.position
= "absolute";
745 div
.style
.display
= "none";
747 div
.appendChild(table
);
749 var thead
= Calendar
.createElement("thead", table
);
754 var hh = function (text
, cs
, navtype
) {
755 cell
= Calendar
.createElement("td", row
);
757 cell
.className
= "button";
758 if (navtype
!= 0 && Math
.abs(navtype
) <= 2)
759 cell
.className
+= " nav";
760 Calendar
._add_evs(cell
);
762 cell
.navtype
= navtype
;
763 cell
.innerHTML
= "<div unselectable='on'>" + text
+ "</div>";
767 row
= Calendar
.createElement("tr", thead
);
768 var title_length
= 6;
769 (this.isPopup
) && --title_length
;
770 (this.weekNumbers
) && ++title_length
;
772 hh("?", 1, 400).ttip
= Calendar
._TT
["INFO"];
773 this.title
= hh("", title_length
, 300);
774 this.title
.className
= "title";
776 this.title
.ttip
= Calendar
._TT
["DRAG_TO_MOVE"];
777 this.title
.style
.cursor
= "move";
778 hh("×", 1, 200).ttip
= Calendar
._TT
["CLOSE"];
781 row
= Calendar
.createElement("tr", thead
);
782 row
.className
= "headrow";
784 this._nav_py
= hh("«", 1, -2);
785 this._nav_py
.ttip
= Calendar
._TT
["PREV_YEAR"];
787 this._nav_pm
= hh("‹", 1, -1);
788 this._nav_pm
.ttip
= Calendar
._TT
["PREV_MONTH"];
790 this._nav_now
= hh(Calendar
._TT
["TODAY"], this.weekNumbers
? 4 : 3, 0);
791 this._nav_now
.ttip
= Calendar
._TT
["GO_TODAY"];
793 this._nav_nm
= hh("›", 1, 1);
794 this._nav_nm
.ttip
= Calendar
._TT
["NEXT_MONTH"];
796 this._nav_ny
= hh("»", 1, 2);
797 this._nav_ny
.ttip
= Calendar
._TT
["NEXT_YEAR"];
800 row
= Calendar
.createElement("tr", thead
);
801 row
.className
= "daynames";
802 if (this.weekNumbers
) {
803 cell
= Calendar
.createElement("td", row
);
804 cell
.className
= "name wn";
805 cell
.innerHTML
= Calendar
._TT
["WK"];
807 for (var i
= 7; i
> 0; --i
) {
808 cell
= Calendar
.createElement("td", row
);
811 cell
.calendar
= this;
812 Calendar
._add_evs(cell
);
815 this.firstdayname
= (this.weekNumbers
) ? row
.firstChild
.nextSibling
: row
.firstChild
;
816 this._displayWeekdays();
818 var tbody
= Calendar
.createElement("tbody", table
);
821 for (i
= 6; i
> 0; --i
) {
822 row
= Calendar
.createElement("tr", tbody
);
823 if (this.weekNumbers
) {
824 cell
= Calendar
.createElement("td", row
);
826 for (var j
= 7; j
> 0; --j
) {
827 cell
= Calendar
.createElement("td", row
);
828 cell
.calendar
= this;
829 Calendar
._add_evs(cell
);
833 if (this.showsTime
) {
834 row
= Calendar
.createElement("tr", tbody
);
835 row
.className
= "time";
837 cell
= Calendar
.createElement("td", row
);
838 cell
.className
= "time";
840 cell
.innerHTML
= Calendar
._TT
["TIME"] || " ";
842 cell
= Calendar
.createElement("td", row
);
843 cell
.className
= "time";
844 cell
.colSpan
= this.weekNumbers
? 4 : 3;
847 function makeTimePart(className
, init
, range_start
, range_end
) {
848 var part
= Calendar
.createElement("span", cell
);
849 part
.className
= className
;
850 part
.innerHTML
= init
;
852 part
.ttip
= Calendar
._TT
["TIME_PART"];
855 if (typeof range_start
!= "number")
856 part
._range
= range_start
;
858 for (var i
= range_start
; i
<= range_end
; ++i
) {
860 if (i
< 10 && range_end
>= 10) txt
= '0' + i
;
862 part
._range
[part
._range
.length
] = txt
;
865 Calendar
._add_evs(part
);
868 var hrs
= cal
.date
.getHours();
869 var mins
= cal
.date
.getMinutes();
870 var t12
= !cal
.time24
;
872 if (t12
&& pm
) hrs
-= 12;
873 var H
= makeTimePart("hour", hrs
, t12
? 1 : 0, t12
? 12 : 23);
874 var span
= Calendar
.createElement("span", cell
);
875 span
.innerHTML
= ":";
876 span
.className
= "colon";
877 var M
= makeTimePart("minute", mins
, 0, 59);
879 cell
= Calendar
.createElement("td", row
);
880 cell
.className
= "time";
883 AP
= makeTimePart("ampm", pm
? "pm" : "am", ["am", "pm"]);
885 cell
.innerHTML
= " ";
887 cal
.onSetTime = function() {
888 var pm
, hrs
= this.date
.getHours(),
889 mins
= this.date
.getMinutes();
893 if (hrs
== 0) hrs
= 12;
894 AP
.innerHTML
= pm
? "pm" : "am";
896 H
.innerHTML
= (hrs
< 10) ? ("0" + hrs
) : hrs
;
897 M
.innerHTML
= (mins
< 10) ? ("0" + mins
) : mins
;
900 cal
.onUpdateTime = function() {
901 var date
= this.date
;
902 var h
= parseInt(H
.innerHTML
, 10);
904 if (/pm/i.test(AP
.innerHTML
) && h
< 12)
906 else if (/am/i.test(AP
.innerHTML
) && h
== 12)
909 var d
= date
.getDate();
910 var m
= date
.getMonth();
911 var y
= date
.getFullYear();
913 date
.setMinutes(parseInt(M
.innerHTML
, 10));
917 this.dateClicked
= false;
922 this.onSetTime
= this.onUpdateTime = function() {};
925 var tfoot
= Calendar
.createElement("tfoot", table
);
927 row
= Calendar
.createElement("tr", tfoot
);
928 row
.className
= "footrow";
930 cell
= hh(Calendar
._TT
["SEL_DATE"], this.weekNumbers
? 8 : 7, 300);
931 cell
.className
= "ttip";
933 cell
.ttip
= Calendar
._TT
["DRAG_TO_MOVE"];
934 cell
.style
.cursor
= "move";
936 this.tooltips
= cell
;
938 div
= Calendar
.createElement("div", this.element
);
939 this.monthsCombo
= div
;
940 div
.className
= "combo";
941 for (i
= 0; i
< Calendar
._MN
.length
; ++i
) {
942 var mn
= Calendar
.createElement("div");
943 mn
.className
= Calendar
.is_ie
? "label-IEfix" : "label";
945 mn
.innerHTML
= Calendar
._SMN
[i
];
949 div
= Calendar
.createElement("div", this.element
);
950 this.yearsCombo
= div
;
951 div
.className
= "combo";
952 for (i
= 12; i
> 0; --i
) {
953 var yr
= Calendar
.createElement("div");
954 yr
.className
= Calendar
.is_ie
? "label-IEfix" : "label";
958 this._init(this.firstDayOfWeek
, this.date
);
959 parent
.appendChild(this.element
);
962 /** keyboard navigation, only for popup calendars */
963 Calendar
._keyEvent = function(ev
) {
964 var cal
= window
._dynarch_popupCalendar
;
965 if (!cal
|| cal
.multiple
)
967 (Calendar
.is_ie
) && (ev
= window
.event
);
968 var act
= (Calendar
.is_ie
|| ev
.type
== "keypress"),
973 act
&& Calendar
.cellClick(cal
._nav_pm
);
976 act
&& Calendar
.cellClick(cal
._nav_py
);
978 case 39: // KEY right
979 act
&& Calendar
.cellClick(cal
._nav_nm
);
982 act
&& Calendar
.cellClick(cal
._nav_ny
);
988 case 32: // KEY space (now)
989 Calendar
.cellClick(cal
._nav_now
);
992 act
&& cal
.callCloseHandler();
996 case 39: // KEY right
999 var prev
, x
, y
, ne
, el
, step
;
1000 prev
= K
== 37 || K
== 38;
1001 step
= (K
== 37 || K
== 39) ? 1 : 7;
1002 function setVars() {
1003 el
= cal
.currentDateEl
;
1007 ne
= cal
.ar_days
[y
][x
];
1009 function prevMonth() {
1010 var date
= new Date(cal
.date
);
1011 date
.setDate(date
.getDate() - step
);
1014 function nextMonth() {
1015 var date
= new Date(cal
.date
);
1016 date
.setDate(date
.getDate() + step
);
1021 case 37: // KEY left
1023 ne
= cal
.ar_days
[y
][x
];
1032 ne
= cal
.ar_days
[y
][x
];
1038 case 39: // KEY right
1040 ne
= cal
.ar_days
[y
][x
];
1047 case 40: // KEY down
1048 if (++y
< cal
.ar_days
.length
)
1049 ne
= cal
.ar_days
[y
][x
];
1060 Calendar
.cellClick(ne
);
1068 case 13: // KEY enter
1070 Calendar
.cellClick(cal
.currentDateEl
, ev
);
1075 return Calendar
.stopEvent(ev
);
1079 * (RE)Initializes the calendar to the given date and firstDayOfWeek
1081 Calendar
.prototype._init = function (firstDayOfWeek
, date
) {
1082 var today
= new Date(),
1083 TY
= today
.getFullYear(),
1084 TM
= today
.getMonth(),
1085 TD
= today
.getDate();
1086 this.table
.style
.visibility
= "hidden";
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
);
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();
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. ;-)
1105 var day1
= (date
.getDay() - this.firstDayOfWeek
) % 7;
1108 date
.setDate(-day1
);
1109 date
.setDate(date
.getDate() + 1);
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
;
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
;
1131 var current_month
= (date
.getMonth() == month
);
1132 if (!current_month
) {
1133 if (this.showsOtherMonths
) {
1134 cell
.className
+= " othermonth";
1135 cell
.otherMonth
= true;
1137 cell
.className
= "emptycell";
1138 cell
.innerHTML
= " ";
1139 cell
.disabled
= true;
1143 cell
.otherMonth
= false;
1146 cell
.disabled
= false;
1147 cell
.innerHTML
= this.getDateText
? this.getDateText(date
, iday
) : iday
;
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
);
1155 cell
.title
= toolTip
;
1157 if (status
=== true) {
1158 cell
.className
+= " disabled";
1159 cell
.disabled
= true;
1161 if (/disabled/i.test(status
))
1162 cell
.disabled
= true;
1163 cell
.className
+= " " + status
;
1166 if (!cell
.disabled
) {
1167 cell
.caldate
= new Date(date
);
1169 if (!this.multiple
&& current_month
1170 && iday
== mday
&& this.hiliteToday
) {
1171 cell
.className
+= " selected";
1172 this.currentDateEl
= cell
;
1174 if (date
.getFullYear() == TY
&&
1175 date
.getMonth() == TM
&&
1177 cell
.className
+= " today";
1178 cell
.ttip
+= Calendar
._TT
["PART_TODAY"];
1180 if (weekend
.indexOf(wday
.toString()) != -1)
1181 cell
.className
+= cell
.otherMonth
? " oweekend" : " weekend";
1184 if (!(hasdays
|| this.showsOtherMonths
))
1185 row
.className
= "emptyrow";
1187 this.title
.innerHTML
= Calendar
._MN
[month
] + ", " + year
;
1189 this.table
.style
.visibility
= "visible";
1190 this._initMultipleDates();
1192 // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
1195 Calendar
.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
];
1203 cell
.className
+= " selected";
1208 Calendar
.prototype._toggleMultipleDate = function(date
) {
1209 if (this.multiple
) {
1210 var ds
= date
.print("%Y%m%d");
1211 var cell
= this.datesCells
[ds
];
1213 var d
= this.multiple
[ds
];
1215 Calendar
.addClass(cell
, "selected");
1216 this.multiple
[ds
] = date
;
1218 Calendar
.removeClass(cell
, "selected");
1219 delete this.multiple
[ds
];
1225 Calendar
.prototype.setDateToolTipHandler = function (unaryFunction
) {
1226 this.getDateToolTip
= unaryFunction
;
1230 * Calls _init function above for going to a certain date (but only if the
1231 * date is different than the currently selected one).
1233 Calendar
.prototype.setDate = function (date
) {
1234 if (!date
.equalsTo(this.date
)) {
1235 this._init(this.firstDayOfWeek
, date
);
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
1245 Calendar
.prototype.refresh = function () {
1246 this._init(this.firstDayOfWeek
, this.date
);
1249 /** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
1250 Calendar
.prototype.setFirstDayOfWeek = function (firstDayOfWeek
) {
1251 this._init(firstDayOfWeek
, this.date
);
1252 this._displayWeekdays();
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.
1261 Calendar
.prototype.setDateStatusHandler
= Calendar
.prototype.setDisabledHandler = function (unaryFunction
) {
1262 this.getDateStatus
= unaryFunction
;
1265 /** Customization of allowed year range for the calendar. */
1266 Calendar
.prototype.setRange = function (a
, z
) {
1271 /** Calls the first user handler (selectedHandler). */
1272 Calendar
.prototype.callHandler = function () {
1273 if (this.onSelected
) {
1274 this.onSelected(this, this.date
.print(this.dateFormat
));
1278 /** Calls the second user handler (closeHandler). */
1279 Calendar
.prototype.callCloseHandler = function () {
1283 this.hideShowCovered();
1286 /** Removes the calendar object from the DOM tree and destroys it. */
1287 Calendar
.prototype.destroy = function () {
1288 var el
= this.element
.parentNode
;
1289 el
.removeChild(this.element
);
1291 window
._dynarch_popupCalendar
= null;
1295 * Moves the calendar element to a different section in the DOM tree (changes
1298 Calendar
.prototype.reparent = function (new_parent
) {
1299 var el
= this.element
;
1300 el
.parentNode
.removeChild(el
);
1301 new_parent
.appendChild(el
);
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.
1307 Calendar
._checkCalendar = function(ev
) {
1308 var calendar
= window
._dynarch_popupCalendar
;
1312 var el
= Calendar
.is_ie
? Calendar
.getElement(ev
) : Calendar
.getTargetElement(ev
);
1313 for (; el
!= null && el
!= calendar
.element
; el
= el
.parentNode
);
1315 // calls closeHandler which should hide the calendar.
1316 window
._dynarch_popupCalendar
.callCloseHandler();
1317 return Calendar
.stopEvent(ev
);
1321 /** Shows the calendar. */
1322 Calendar
.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");
1334 this.element
.style
.display
= "block";
1335 this.hidden
= false;
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
);
1342 this.hideShowCovered();
1346 * Hides the calendar. Also removes any "hilite" from the class of any TD
1349 Calendar
.prototype.hide = function () {
1351 Calendar
.removeEvent(document
, "keydown", Calendar
._keyEvent
);
1352 Calendar
.removeEvent(document
, "keypress", Calendar
._keyEvent
);
1353 Calendar
.removeEvent(document
, "mousedown", Calendar
._checkCalendar
);
1355 this.element
.style
.display
= "none";
1357 this.hideShowCovered();
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).
1365 Calendar
.prototype.showAt = function (x
, y
) {
1366 var s
= this.element
.style
;
1372 /** Shows the calendar near a given element. */
1373 Calendar
.prototype.showAtElement = function (el
, opts
) {
1375 var p
= Calendar
.getAbsolutePos(el
);
1376 if (!opts
|| typeof opts
!= "string") {
1377 this.showAt(p
.x
, p
.y
+ el
.offsetHeight
);
1380 function fixPosition(box
) {
1385 var cp
= document
.createElement("div");
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
;
1396 br
.y
+= window
.scrollY
;
1397 br
.x
+= window
.scrollX
;
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
;
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);
1411 if (opts
.length
> 1) {
1412 halign
= opts
.substr(1, 1);
1414 // vertical alignment
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
1422 // horizontal alignment
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
1432 self
.monthsCombo
.style
.display
= "none";
1434 self
.showAt(p
.x
, p
.y
);
1436 if (Calendar
.is_khtml
)
1437 setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
1439 Calendar
.continuation_for_the_fucking_khtml_browser();
1442 /** Customizes the date format. */
1443 Calendar
.prototype.setDateFormat = function (str
) {
1444 this.dateFormat
= str
;
1447 /** Customizes the tooltip date format. */
1448 Calendar
.prototype.setTtDateFormat = function (str
) {
1449 this.ttDateFormat
= str
;
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.
1456 Calendar
.prototype.parseDate = function(str
, fmt
) {
1458 fmt
= this.dateFormat
;
1459 this.setDate(Date
.parseDate(str
, fmt
));
1462 Calendar
.prototype.hideShowCovered = function () {
1463 if (!Calendar
.is_ie
&& !Calendar
.is_opera
)
1465 function getVisib(obj
){
1466 var value
= obj
.style
.visibility
;
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");
1474 } else if (obj
.currentStyle
) { // IE
1475 value
= obj
.currentStyle
.visibility
;
1482 var tags
= new Array("applet", "iframe", "select");
1483 var el
= this.element
;
1485 var p
= Calendar
.getAbsolutePos(el
);
1487 var EX2
= el
.offsetWidth
+ EX1
;
1489 var EY2
= el
.offsetHeight
+ EY1
;
1491 for (var k
= tags
.length
; k
> 0; ) {
1492 var ar
= document
.getElementsByTagName(tags
[--k
]);
1495 for (var i
= ar
.length
; i
> 0;) {
1498 p
= Calendar
.getAbsolutePos(cc
);
1500 var CX2
= cc
.offsetWidth
+ CX1
;
1502 var CY2
= cc
.offsetHeight
+ CY1
;
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
);
1508 cc
.style
.visibility
= cc
.__msh_save_visibility
;
1510 if (!cc
.__msh_save_visibility
) {
1511 cc
.__msh_save_visibility
= getVisib(cc
);
1513 cc
.style
.visibility
= "hidden";
1519 /** Internal function; it displays the bar with the names of the weekday. */
1520 Calendar
.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;
1528 cell
.ttip
= Calendar
._TT
["DAY_FIRST"].replace("%s", Calendar
._DN
[realday
]);
1530 cell
.calendar
= this;
1531 cell
.fdow
= realday
;
1532 Calendar
._add_evs(cell
);
1534 if (weekend
.indexOf(realday
.toString()) != -1) {
1535 Calendar
.addClass(cell
, "weekend");
1537 cell
.innerHTML
= Calendar
._SDN
[(i
+ fdow
) % 7];
1538 cell
= cell
.nextSibling
;
1542 /** Internal function. Hides all combo boxes that might be displayed. */
1543 Calendar
.prototype._hideCombos = function () {
1544 this.monthsCombo
.style
.display
= "none";
1545 this.yearsCombo
.style
.display
= "none";
1548 /** Internal function. Starts dragging the element. */
1549 Calendar
.prototype._dragStart = function (ev
) {
1550 if (this.dragging
) {
1553 this.dragging
= true;
1556 if (Calendar
.is_ie
) {
1557 posY
= window
.event
.clientY
+ document
.body
.scrollTop
;
1558 posX
= window
.event
.clientX
+ document
.body
.scrollLeft
;
1560 posY
= ev
.clientY
+ window
.scrollY
;
1561 posX
= ev
.clientX
+ window
.scrollX
;
1563 var st
= this.element
.style
;
1564 this.xOffs
= posX
- parseInt(st
.left
);
1565 this.yOffs
= posY
- parseInt(st
.top
);
1567 addEvent(document
, "mousemove", calDragIt
);
1568 addEvent(document
, "mouseup", calDragEnd
);
1572 // BEGIN: DATE OBJECT PATCHES
1574 /** Adds the number of days array to the Date object. */
1575 Date
._MD
= new Array(31,28,31,30,31,30,31,31,30,31,30,31);
1577 /** Constants used for time computations */
1578 Date
.SECOND
= 1000 /* milliseconds */;
1579 Date
.MINUTE
= 60 * Date
.SECOND
;
1580 Date
.HOUR
= 60 * Date
.MINUTE
;
1581 Date
.DAY
= 24 * Date
.HOUR
;
1582 Date
.WEEK
= 7 * Date
.DAY
;
1584 Date
.parseDate = function(str
, fmt
) {
1585 var today
= new Date();
1589 var a
= str
.split(/\W+/);
1590 var b
= fmt
.match(/%./g);
1594 for (i
= 0; i
< a
.length
; ++i
) {
1600 d
= parseInt(a
[i
], 10);
1604 m
= parseInt(a
[i
], 10) - 1;
1609 y
= parseInt(a
[i
], 10);
1610 (y
< 100) && (y
+= (y
> 29) ? 1900 : 2000);
1615 for (j
= 0; j
< 12; ++j
) {
1616 if (Calendar
._MN
[j
].substr(0, a
[i
].length
).toLowerCase() == a
[i
].toLowerCase()) { m
= j
; break; }
1624 hr
= parseInt(a
[i
], 10);
1629 if (/pm/i.test(a
[i
]) && hr
< 12)
1631 else if (/am/i.test(a
[i
]) && hr
>= 12)
1636 min
= parseInt(a
[i
], 10);
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) {
1651 for (j
= 0; j
< 12; ++j
) {
1652 if (Calendar
._MN
[j
].substr(0, a
[i
].length
).toLowerCase() == a
[i
].toLowerCase()) { t
= j
; break; }
1660 } else if (parseInt(a
[i
], 10) <= 12 && m
== -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) {
1670 y
= today
.getFullYear();
1671 if (m
!= -1 && d
!= 0)
1672 return new Date(y
, m
, d
, hr
, min
, 0);
1676 /** Returns the number of days in the current month */
1677 Date
.prototype.getMonthDays = function(month
) {
1678 var year
= this.getFullYear();
1679 if (typeof month
== "undefined") {
1680 month
= this.getMonth();
1682 if (((0 == (year
%4)) && ( (0 != (year
%100)) || (0 == (year
%400)))) && month
== 1) {
1685 return Date
._MD
[month
];
1689 /** Returns the number of day in the year. */
1690 Date
.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
);
1697 /** Returns the number of the week in year, as defined in ISO 8601. */
1698 Date
.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
1704 d
.setDate(4); // Thu in Week 1
1705 return Math
.round((ms
- d
.valueOf()) / (7 * 864e5
)) + 1;
1708 /** Checks date and time equality */
1709 Date
.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()));
1717 /** Set only the year, month, date parts (keep existing time) */
1718 Date
.prototype.setDateOnly = function(date
) {
1719 var tmp
= new Date(date
);
1721 this.setFullYear(tmp
.getFullYear());
1722 this.setMonth(tmp
.getMonth());
1723 this.setDate(tmp
.getDate());
1726 /** Prints the date in a string according to the given format. */
1727 Date
.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();
1734 var hr
= this.getHours();
1735 var pm
= (hr
>= 12);
1736 var ir
= (pm
) ? (hr
- 12) : hr
;
1737 var dy
= this.getDayOfYear();
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
1778 if (!Calendar
.is_ie5
&& !Calendar
.is_khtml
)
1779 return str
.replace(re
, function (par
) { return s
[par
] || par
; });
1781 var a
= str
.match(re
);
1782 for (var i
= 0; i
< a
.length
; i
++) {
1785 re
= new RegExp(a
[i
], 'g');
1786 str
= str
.replace(re
, tmp
);
1793 Date
.prototype.__msh_oldSetFullYear
= Date
.prototype.setFullYear
;
1794 Date
.prototype.setFullYear = function(y
) {
1795 var d
= new Date(this);
1796 d
.__msh_oldSetFullYear(y
);
1797 if (d
.getMonth() != this.getMonth())
1799 this.__msh_oldSetFullYear(y
);
1802 // END: DATE OBJECT PATCHES
1805 // global object that remembers the calendar
1806 window
._dynarch_popupCalendar
= null;
1808 /* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
1809 * ---------------------------------------------------------------------------
1811 * The DHTML Calendar
1813 * Details and latest version at:
1814 * http://dynarch.com/mishoo/calendar.epl
1816 * This script is distributed under the GNU Lesser General Public License.
1817 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
1819 * This file defines helper functions for setting up the calendar. They are
1820 * intended to help non-programmers get a working calendar on their site
1821 * quickly. This script should not be seen as part of the calendar. It just
1822 * shows you what one can do with the calendar, while in the same time
1823 * providing a quick and simple method for setting it up. If you need
1824 * exhaustive customization of the calendar creation process feel free to
1825 * modify this code to suit your needs (this is recommended and much better
1826 * than modifying calendar.js itself).
1829 // $Id: calendar-setup.js,v 1.25 2005/03/07 09:51:33 mishoo Exp $
1832 * This function "patches" an input field (or other element) to use a calendar
1833 * widget for date selection.
1835 * The "params" is a single object that can have the following properties:
1837 * prop. name | description
1838 * -------------------------------------------------------------------------------------------------
1839 * inputField | the ID of an input field to store the date
1840 * displayArea | the ID of a DIV or other element to show the date
1841 * button | ID of a button or other element that will trigger the calendar
1842 * eventName | event that will trigger the calendar, without the "on" prefix (default: "click")
1843 * ifFormat | date format that will be stored in the input field
1844 * daFormat | the date format that will be used to display the date in displayArea
1845 * singleClick | (true/false) wether the calendar is in single click mode or not (default: true)
1846 * firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc.
1847 * align | alignment (default: "Br"); if you don't know what's this see the calendar documentation
1848 * range | array with 2 elements. Default: [1900, 2999] -- the range of years available
1849 * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers
1850 * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID
1851 * flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar)
1852 * disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar
1853 * onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay)
1854 * onClose | function that gets called when the calendar is closed. [default]
1855 * onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar.
1856 * date | the date that the calendar will be initially displayed to
1857 * showsTime | default: false; if true the calendar will include a time selector
1858 * timeFormat | the time format; can be "12" or "24", default is "12"
1859 * electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close
1860 * step | configures the step of the years in drop-down boxes; default: 2
1861 * position | configures the calendar absolute position; default: null
1862 * cache | if "true" (but default: "false") it will reuse the same calendar object, where possible
1863 * showOthers | if "true" (but default: "false") it will show days from other months too
1865 * None of them is required, they all have default values. However, if you
1866 * pass none of "inputField", "displayArea" or "button" you'll get a warning
1867 * saying "nothing to setup".
1869 Calendar
.setup = function (params
) {
1870 function param_default(pname
, def
) { if (typeof params
[pname
] == "undefined") { params
[pname
] = def
; } };
1872 param_default("inputField", null);
1873 param_default("displayArea", null);
1874 param_default("button", null);
1875 param_default("eventName", "click");
1876 param_default("ifFormat", "%Y/%m/%d");
1877 param_default("daFormat", "%Y/%m/%d");
1878 param_default("singleClick", true);
1879 param_default("disableFunc", null);
1880 param_default("dateStatusFunc", params
["disableFunc"]); // takes precedence if both are defined
1881 param_default("dateText", null);
1882 param_default("firstDay", null);
1883 param_default("align", "Br");
1884 param_default("range", [1900, 2999]);
1885 param_default("weekNumbers", true);
1886 param_default("flat", null);
1887 param_default("flatCallback", null);
1888 param_default("onSelect", null);
1889 param_default("onClose", null);
1890 param_default("onUpdate", null);
1891 param_default("date", null);
1892 param_default("showsTime", false);
1893 param_default("timeFormat", "24");
1894 param_default("electric", true);
1895 param_default("step", 2);
1896 param_default("position", null);
1897 param_default("cache", false);
1898 param_default("showOthers", false);
1899 param_default("multiple", null);
1901 var tmp
= ["inputField", "displayArea", "button"];
1902 for (var i
in tmp
) {
1903 if (typeof params
[tmp
[i
]] == "string") {
1904 params
[tmp
[i
]] = document
.getElementById(params
[tmp
[i
]]);
1907 if (!(params
.flat
|| params
.multiple
|| params
.inputField
|| params
.displayArea
|| params
.button
)) {
1908 alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code");
1912 function onSelect(cal
) {
1914 var update
= (cal
.dateClicked
|| p
.electric
);
1915 if (update
&& p
.inputField
) {
1916 p
.inputField
.value
= cal
.date
.print(p
.ifFormat
);
1917 if (typeof p
.inputField
.onchange
== "function")
1918 p
.inputField
.onchange();
1920 if (update
&& p
.displayArea
)
1921 p
.displayArea
.innerHTML
= cal
.date
.print(p
.daFormat
);
1922 if (update
&& typeof p
.onUpdate
== "function")
1924 if (update
&& p
.flat
) {
1925 if (typeof p
.flatCallback
== "function")
1926 p
.flatCallback(cal
);
1928 if (update
&& p
.singleClick
&& cal
.dateClicked
)
1929 cal
.callCloseHandler();
1932 if (params
.flat
!= null) {
1933 if (typeof params
.flat
== "string")
1934 params
.flat
= document
.getElementById(params
.flat
);
1936 alert("Calendar.setup:\n Flat specified but can't find parent.");
1939 var cal
= new Calendar(params
.firstDay
, params
.date
, params
.onSelect
|| onSelect
);
1940 cal
.showsOtherMonths
= params
.showOthers
;
1941 cal
.showsTime
= params
.showsTime
;
1942 cal
.time24
= (params
.timeFormat
== "24");
1943 cal
.params
= params
;
1944 cal
.weekNumbers
= params
.weekNumbers
;
1945 cal
.setRange(params
.range
[0], params
.range
[1]);
1946 cal
.setDateStatusHandler(params
.dateStatusFunc
);
1947 cal
.getDateText
= params
.dateText
;
1948 if (params
.ifFormat
) {
1949 cal
.setDateFormat(params
.ifFormat
);
1951 if (params
.inputField
&& typeof params
.inputField
.value
== "string") {
1952 cal
.parseDate(params
.inputField
.value
);
1954 cal
.create(params
.flat
);
1959 var triggerEl
= params
.button
|| params
.displayArea
|| params
.inputField
;
1960 triggerEl
["on" + params
.eventName
] = function() {
1961 var dateEl
= params
.inputField
|| params
.displayArea
;
1962 var dateFmt
= params
.inputField
? params
.ifFormat
: params
.daFormat
;
1963 var mustCreate
= false;
1964 var cal
= window
.calendar
;
1966 params
.date
= Date
.parseDate(dateEl
.value
|| dateEl
.innerHTML
, dateFmt
);
1967 if (!(cal
&& params
.cache
)) {
1968 window
.calendar
= cal
= new Calendar(params
.firstDay
,
1970 params
.onSelect
|| onSelect
,
1971 params
.onClose
|| function(cal
) { cal
.hide(); });
1972 cal
.showsTime
= params
.showsTime
;
1973 cal
.time24
= (params
.timeFormat
== "24");
1974 cal
.weekNumbers
= params
.weekNumbers
;
1978 cal
.setDate(params
.date
);
1981 if (params
.multiple
) {
1983 for (var i
= params
.multiple
.length
; --i
>= 0;) {
1984 var d
= params
.multiple
[i
];
1985 var ds
= d
.print("%Y%m%d");
1986 cal
.multiple
[ds
] = d
;
1989 cal
.showsOtherMonths
= params
.showOthers
;
1990 cal
.yearStep
= params
.step
;
1991 cal
.setRange(params
.range
[0], params
.range
[1]);
1992 cal
.params
= params
;
1993 cal
.setDateStatusHandler(params
.dateStatusFunc
);
1994 cal
.getDateText
= params
.dateText
;
1995 cal
.setDateFormat(dateFmt
);
1999 if (!params
.position
)
2000 cal
.showAtElement(params
.button
|| params
.displayArea
|| params
.inputField
, params
.align
);
2002 cal
.showAt(params
.position
[0], params
.position
[1]);
2010 // Calendar EN language
2011 // Author: Mihai Bazon, <mihai_bazon@yahoo.com>
2013 // Distributed under the same terms as the calendar itself.
2015 // For translators: please use UTF-8 if possible. We strongly believe that
2016 // Unicode is the answer to a real internationalized world. Also please
2017 // include your contact information in the header, as can be seen above.
2020 Calendar
._DN
= new Array
2030 // Please note that the following array of short day names (and the same goes
2031 // for short month names, _SMN) isn't absolutely necessary. We give it here
2032 // for exemplification on how one can customize the short day names, but if
2033 // they are simply the first N letters of the full name you can simply say:
2035 // Calendar._SDN_len = N; // short day name length
2036 // Calendar._SMN_len = N; // short month name length
2038 // If N = 3 then this is not needed either since we assume a value of 3 if not
2039 // present, to be compatible with translation files that were written before
2043 Calendar
._SDN
= new Array
2053 // First day of the week. "0" means display Sunday first, "1" means display
2054 // Monday first, etc.
2058 Calendar
._MN
= new Array
2072 // short month names
2073 Calendar
._SMN
= new Array
2089 Calendar
._TT
["INFO"] = "About the calendar";
2091 Calendar
._TT
["ABOUT"] =
2092 "DHTML Date/Time Selector\n" +
2093 "(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-)
2094 "For latest version visit: http://www.dynarch.com/projects/calendar/\n" +
2095 "Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." +
2097 "Date selection:\n" +
2098 "- Use the \xab, \xbb buttons to select year\n" +
2099 "- Use the " + String
.fromCharCode(0x2039) + ", " + String
.fromCharCode(0x203a) + " buttons to select month\n" +
2100 "- Hold mouse button on any of the above buttons for faster selection.";
2101 Calendar
._TT
["ABOUT_TIME"] = "\n\n" +
2102 "Time selection:\n" +
2103 "- Click on any of the time parts to increase it\n" +
2104 "- or Shift-click to decrease it\n" +
2105 "- or click and drag for faster selection.";
2107 Calendar
._TT
["PREV_YEAR"] = "Prev. year (hold for menu)";
2108 Calendar
._TT
["PREV_MONTH"] = "Prev. month (hold for menu)";
2109 Calendar
._TT
["GO_TODAY"] = "Go Today";
2110 Calendar
._TT
["NEXT_MONTH"] = "Next month (hold for menu)";
2111 Calendar
._TT
["NEXT_YEAR"] = "Next year (hold for menu)";
2112 Calendar
._TT
["SEL_DATE"] = "Select date";
2113 Calendar
._TT
["DRAG_TO_MOVE"] = "Drag to move";
2114 Calendar
._TT
["PART_TODAY"] = " (today)";
2116 // the following is to inform that "%s" is to be the first day of week
2117 // %s will be replaced with the day name.
2118 Calendar
._TT
["DAY_FIRST"] = "Display %s first";
2120 // This may be locale-dependent. It specifies the week-end days, as an array
2121 // of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1
2122 // means Monday, etc.
2123 Calendar
._TT
["WEEKEND"] = "0,6";
2125 Calendar
._TT
["CLOSE"] = "Close";
2126 Calendar
._TT
["TODAY"] = "Today";
2127 Calendar
._TT
["TIME_PART"] = "(Shift-)Click or drag to change value";
2130 Calendar
._TT
["DEF_DATE_FORMAT"] = "%Y-%m-%d";
2131 Calendar
._TT
["TT_DATE_FORMAT"] = "%a, %b %e";
2133 Calendar
._TT
["WK"] = "wk";
2134 Calendar
._TT
["TIME"] = "Time:";