noalyss  Version-6.7.2
 All Data Structures Namespaces Files Functions Variables Enumerations
calendar.js
Go to the documentation of this file.
00001 /*  Copyright Mihai Bazon, 2002-2005  |  www.bazon.net/mishoo
00002  * -----------------------------------------------------------
00003  *
00004  * The DHTML Calendar, version 1.0 "It is happening again"
00005  *
00006  * Details and latest version at:
00007  * www.dynarch.com/projects/calendar
00008  *
00009  * This script is developed by Dynarch.com.  Visit us at www.dynarch.com.
00010  *
00011  * This script is distributed under the GNU Lesser General Public License.
00012  * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
00013  */
00014 
00015 // $Id$
00016 
00017 /** The Calendar object constructor. */
00018 Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
00019         // member variables
00020         this.activeDiv = null;
00021         this.currentDateEl = null;
00022         this.getDateStatus = null;
00023         this.getDateToolTip = null;
00024         this.getDateText = null;
00025         this.timeout = null;
00026         this.onSelected = onSelected || null;
00027         this.onClose = onClose || null;
00028         this.dragging = false;
00029         this.hidden = false;
00030         this.minYear = 1970;
00031         this.maxYear = 2050;
00032         this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
00033         this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
00034         this.isPopup = true;
00035         this.weekNumbers = true;
00036         this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
00037         this.showsOtherMonths = false;
00038         this.dateStr = dateStr;
00039         this.ar_days = null;
00040         this.showsTime = false;
00041         this.time24 = true;
00042         this.yearStep = 2;
00043         this.hiliteToday = true;
00044         this.multiple = null;
00045         // HTML elements
00046         this.table = null;
00047         this.element = null;
00048         this.tbody = null;
00049         this.firstdayname = null;
00050         // Combo boxes
00051         this.monthsCombo = null;
00052         this.yearsCombo = null;
00053         this.hilitedMonth = null;
00054         this.activeMonth = null;
00055         this.hilitedYear = null;
00056         this.activeYear = null;
00057         // Information
00058         this.dateClicked = false;
00059 
00060         // one-time initializations
00061         if (typeof Calendar._SDN == "undefined") {
00062                 // table of short day names
00063                 if (typeof Calendar._SDN_len == "undefined")
00064                         Calendar._SDN_len = 3;
00065                 var ar = new Array();
00066                 for (var i = 8; i > 0;) {
00067                         ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
00068                 }
00069                 Calendar._SDN = ar;
00070                 // table of short month names
00071                 if (typeof Calendar._SMN_len == "undefined")
00072                         Calendar._SMN_len = 3;
00073                 ar = new Array();
00074                 for (var i = 12; i > 0;) {
00075                         ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
00076                 }
00077                 Calendar._SMN = ar;
00078         }
00079 };
00080 
00081 // ** constants
00082 
00083 /// "static", needed for event handlers.
00084 Calendar._C = null;
00085 
00086 /// detect a special case of "web browser"
00087 Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
00088                    !/opera/i.test(navigator.userAgent) );
00089 
00090 Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
00091 
00092 /// detect Opera browser
00093 Calendar.is_opera = /opera/i.test(navigator.userAgent);
00094 
00095 /// detect KHTML-based browsers
00096 Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
00097 
00098 // BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
00099 //        library, at some point.
00100 
00101 Calendar.getAbsolutePos = function(el) {
00102         var SL = 0, ST = 0;
00103         var is_div = /^div$/i.test(el.tagName);
00104         if (is_div && el.scrollLeft)
00105                 SL = el.scrollLeft;
00106         if (is_div && el.scrollTop)
00107                 ST = el.scrollTop;
00108         var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
00109         if (el.offsetParent) {
00110                 var tmp = this.getAbsolutePos(el.offsetParent);
00111                 r.x += tmp.x;
00112                 r.y += tmp.y;
00113         }
00114         return r;
00115 };
00116 
00117 Calendar.isRelated = function (el, evt) {
00118         var related = evt.relatedTarget;
00119         if (!related) {
00120                 var type = evt.type;
00121                 if (type == "mouseover") {
00122                         related = evt.fromElement;
00123                 } else if (type == "mouseout") {
00124                         related = evt.toElement;
00125                 }
00126         }
00127         while (related) {
00128                 if (related == el) {
00129                         return true;
00130                 }
00131                 related = related.parentNode;
00132         }
00133         return false;
00134 };
00135 
00136 Calendar.removeClass = function(el, className) {
00137         if (!(el && el.className)) {
00138                 return;
00139         }
00140         var cls = el.className.split(" ");
00141         var ar = new Array();
00142         for (var i = cls.length; i > 0;) {
00143                 if (cls[--i] != className) {
00144                         ar[ar.length] = cls[i];
00145                 }
00146         }
00147         el.className = ar.join(" ");
00148 };
00149 
00150 Calendar.addClass = function(el, className) {
00151         Calendar.removeClass(el, className);
00152         el.className += " " + className;
00153 };
00154 
00155 // FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
00156 Calendar.getElement = function(ev) {
00157         var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
00158         while (f.nodeType != 1 || /^div$/i.test(f.tagName))
00159                 f = f.parentNode;
00160         return f;
00161 };
00162 
00163 Calendar.getTargetElement = function(ev) {
00164         var f = Calendar.is_ie ? window.event.srcElement : ev.target;
00165         while (f.nodeType != 1)
00166                 f = f.parentNode;
00167         return f;
00168 };
00169 
00170 Calendar.stopEvent = function(ev) {
00171         ev || (ev = window.event);
00172         if (Calendar.is_ie) {
00173                 ev.cancelBubble = true;
00174                 ev.returnValue = false;
00175         } else {
00176                 ev.preventDefault();
00177                 ev.stopPropagation();
00178         }
00179         return false;
00180 };
00181 
00182 Calendar.addEvent = function(el, evname, func) {
00183         if (el.attachEvent) { // IE
00184                 el.attachEvent("on" + evname, func);
00185         } else if (el.addEventListener) { // Gecko / W3C
00186                 el.addEventListener(evname, func, true);
00187         } else {
00188                 el["on" + evname] = func;
00189         }
00190 };
00191 
00192 Calendar.removeEvent = function(el, evname, func) {
00193         if (el.detachEvent) { // IE
00194                 el.detachEvent("on" + evname, func);
00195         } else if (el.removeEventListener) { // Gecko / W3C
00196                 el.removeEventListener(evname, func, true);
00197         } else {
00198                 el["on" + evname] = null;
00199         }
00200 };
00201 
00202 Calendar.createElement = function(type, parent) {
00203         var el = null;
00204         if (document.createElementNS) {
00205                 // use the XHTML namespace; IE won't normally get here unless
00206                 // _they_ "fix" the DOM2 implementation.
00207                 el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
00208         } else {
00209                 el = document.createElement(type);
00210         }
00211         if (typeof parent != "undefined") {
00212                 parent.appendChild(el);
00213         }
00214         return el;
00215 };
00216 
00217 // END: UTILITY FUNCTIONS
00218 
00219 // BEGIN: CALENDAR STATIC FUNCTIONS
00220 
00221 /** Internal -- adds a set of events to make some element behave like a button. */
00222 Calendar._add_evs = function(el) {
00223         with (Calendar) {
00224                 addEvent(el, "mouseover", dayMouseOver);
00225                 addEvent(el, "mousedown", dayMouseDown);
00226                 addEvent(el, "mouseout", dayMouseOut);
00227                 if (is_ie) {
00228                         addEvent(el, "dblclick", dayMouseDblClick);
00229                         el.setAttribute("unselectable", true);
00230                 }
00231         }
00232 };
00233 
00234 Calendar.findMonth = function(el) {
00235         if (typeof el.month != "undefined") {
00236                 return el;
00237         } else if (typeof el.parentNode.month != "undefined") {
00238                 return el.parentNode;
00239         }
00240         return null;
00241 };
00242 
00243 Calendar.findYear = function(el) {
00244         if (typeof el.year != "undefined") {
00245                 return el;
00246         } else if (typeof el.parentNode.year != "undefined") {
00247                 return el.parentNode;
00248         }
00249         return null;
00250 };
00251 
00252 Calendar.showMonthsCombo = function () {
00253         var cal = Calendar._C;
00254         if (!cal) {
00255                 return false;
00256         }
00257         var cal = cal;
00258         var cd = cal.activeDiv;
00259         var mc = cal.monthsCombo;
00260         if (cal.hilitedMonth) {
00261                 Calendar.removeClass(cal.hilitedMonth, "hilite");
00262         }
00263         if (cal.activeMonth) {
00264                 Calendar.removeClass(cal.activeMonth, "active");
00265         }
00266         var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
00267         Calendar.addClass(mon, "active");
00268         cal.activeMonth = mon;
00269         var s = mc.style;
00270         s.display = "block";
00271         if (cd.navtype < 0)
00272                 s.left = cd.offsetLeft + "px";
00273         else {
00274                 var mcw = mc.offsetWidth;
00275                 if (typeof mcw == "undefined")
00276                         // Konqueror brain-dead techniques
00277                         mcw = 50;
00278                 s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
00279         }
00280         s.top = (cd.offsetTop + cd.offsetHeight) + "px";
00281 };
00282 
00283 Calendar.showYearsCombo = function (fwd) {
00284         var cal = Calendar._C;
00285         if (!cal) {
00286                 return false;
00287         }
00288         var cal = cal;
00289         var cd = cal.activeDiv;
00290         var yc = cal.yearsCombo;
00291         if (cal.hilitedYear) {
00292                 Calendar.removeClass(cal.hilitedYear, "hilite");
00293         }
00294         if (cal.activeYear) {
00295                 Calendar.removeClass(cal.activeYear, "active");
00296         }
00297         cal.activeYear = null;
00298         var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
00299         var yr = yc.firstChild;
00300         var show = false;
00301         for (var i = 12; i > 0; --i) {
00302                 if (Y >= cal.minYear && Y <= cal.maxYear) {
00303                         yr.innerHTML = Y;
00304                         yr.year = Y;
00305                         yr.style.display = "block";
00306                         show = true;
00307                 } else {
00308                         yr.style.display = "none";
00309                 }
00310                 yr = yr.nextSibling;
00311                 Y += fwd ? cal.yearStep : -cal.yearStep;
00312         }
00313         if (show) {
00314                 var s = yc.style;
00315                 s.display = "block";
00316                 if (cd.navtype < 0)
00317                         s.left = cd.offsetLeft + "px";
00318                 else {
00319                         var ycw = yc.offsetWidth;
00320                         if (typeof ycw == "undefined")
00321                                 // Konqueror brain-dead techniques
00322                                 ycw = 50;
00323                         s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
00324                 }
00325                 s.top = (cd.offsetTop + cd.offsetHeight) + "px";
00326         }
00327 };
00328 
00329 // event handlers
00330 
00331 Calendar.tableMouseUp = function(ev) {
00332         var cal = Calendar._C;
00333         if (!cal) {
00334                 return false;
00335         }
00336         if (cal.timeout) {
00337                 clearTimeout(cal.timeout);
00338         }
00339         var el = cal.activeDiv;
00340         if (!el) {
00341                 return false;
00342         }
00343         var target = Calendar.getTargetElement(ev);
00344         ev || (ev = window.event);
00345         Calendar.removeClass(el, "active");
00346         if (target == el || target.parentNode == el) {
00347                 Calendar.cellClick(el, ev);
00348         }
00349         var mon = Calendar.findMonth(target);
00350         var date = null;
00351         if (mon) {
00352                 date = new Date(cal.date);
00353                 if (mon.month != date.getMonth()) {
00354                         date.setMonth(mon.month);
00355                         cal.setDate(date);
00356                         cal.dateClicked = false;
00357                         cal.callHandler();
00358                 }
00359         } else {
00360                 var year = Calendar.findYear(target);
00361                 if (year) {
00362                         date = new Date(cal.date);
00363                         if (year.year != date.getFullYear()) {
00364                                 date.setFullYear(year.year);
00365                                 cal.setDate(date);
00366                                 cal.dateClicked = false;
00367                                 cal.callHandler();
00368                         }
00369                 }
00370         }
00371         with (Calendar) {
00372                 removeEvent(document, "mouseup", tableMouseUp);
00373                 removeEvent(document, "mouseover", tableMouseOver);
00374                 removeEvent(document, "mousemove", tableMouseOver);
00375                 cal._hideCombos();
00376                 _C = null;
00377                 return stopEvent(ev);
00378         }
00379 };
00380 
00381 Calendar.tableMouseOver = function (ev) {
00382         var cal = Calendar._C;
00383         if (!cal) {
00384                 return;
00385         }
00386         var el = cal.activeDiv;
00387         var target = Calendar.getTargetElement(ev);
00388         if (target == el || target.parentNode == el) {
00389                 Calendar.addClass(el, "hilite active");
00390                 Calendar.addClass(el.parentNode, "rowhilite");
00391         } else {
00392                 if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
00393                         Calendar.removeClass(el, "active");
00394                 Calendar.removeClass(el, "hilite");
00395                 Calendar.removeClass(el.parentNode, "rowhilite");
00396         }
00397         ev || (ev = window.event);
00398         if (el.navtype == 50 && target != el) {
00399                 var pos = Calendar.getAbsolutePos(el);
00400                 var w = el.offsetWidth;
00401                 var x = ev.clientX;
00402                 var dx;
00403                 var decrease = true;
00404                 if (x > pos.x + w) {
00405                         dx = x - pos.x - w;
00406                         decrease = false;
00407                 } else
00408                         dx = pos.x - x;
00409 
00410                 if (dx < 0) dx = 0;
00411                 var range = el._range;
00412                 var current = el._current;
00413                 var count = Math.floor(dx / 10) % range.length;
00414                 for (var i = range.length; --i >= 0;)
00415                         if (range[i] == current)
00416                                 break;
00417                 while (count-- > 0)
00418                         if (decrease) {
00419                                 if (--i < 0)
00420                                         i = range.length - 1;
00421                         } else if ( ++i >= range.length )
00422                                 i = 0;
00423                 var newval = range[i];
00424                 el.innerHTML = newval;
00425 
00426                 cal.onUpdateTime();
00427         }
00428         var mon = Calendar.findMonth(target);
00429         if (mon) {
00430                 if (mon.month != cal.date.getMonth()) {
00431                         if (cal.hilitedMonth) {
00432                                 Calendar.removeClass(cal.hilitedMonth, "hilite");
00433                         }
00434                         Calendar.addClass(mon, "hilite");
00435                         cal.hilitedMonth = mon;
00436                 } else if (cal.hilitedMonth) {
00437                         Calendar.removeClass(cal.hilitedMonth, "hilite");
00438                 }
00439         } else {
00440                 if (cal.hilitedMonth) {
00441                         Calendar.removeClass(cal.hilitedMonth, "hilite");
00442                 }
00443                 var year = Calendar.findYear(target);
00444                 if (year) {
00445                         if (year.year != cal.date.getFullYear()) {
00446                                 if (cal.hilitedYear) {
00447                                         Calendar.removeClass(cal.hilitedYear, "hilite");
00448                                 }
00449                                 Calendar.addClass(year, "hilite");
00450                                 cal.hilitedYear = year;
00451                         } else if (cal.hilitedYear) {
00452                                 Calendar.removeClass(cal.hilitedYear, "hilite");
00453                         }
00454                 } else if (cal.hilitedYear) {
00455                         Calendar.removeClass(cal.hilitedYear, "hilite");
00456                 }
00457         }
00458         return Calendar.stopEvent(ev);
00459 };
00460 
00461 Calendar.tableMouseDown = function (ev) {
00462         if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
00463                 return Calendar.stopEvent(ev);
00464         }
00465 };
00466 
00467 Calendar.calDragIt = function (ev) {
00468         var cal = Calendar._C;
00469         if (!(cal && cal.dragging)) {
00470                 return false;
00471         }
00472         var posX;
00473         var posY;
00474         if (Calendar.is_ie) {
00475                 posY = window.event.clientY + document.body.scrollTop;
00476                 posX = window.event.clientX + document.body.scrollLeft;
00477         } else {
00478                 posX = ev.pageX;
00479                 posY = ev.pageY;
00480         }
00481         cal.hideShowCovered();
00482         var st = cal.element.style;
00483         st.left = (posX - cal.xOffs) + "px";
00484         st.top = (posY - cal.yOffs) + "px";
00485         return Calendar.stopEvent(ev);
00486 };
00487 
00488 Calendar.calDragEnd = function (ev) {
00489         var cal = Calendar._C;
00490         if (!cal) {
00491                 return false;
00492         }
00493         cal.dragging = false;
00494         with (Calendar) {
00495                 removeEvent(document, "mousemove", calDragIt);
00496                 removeEvent(document, "mouseup", calDragEnd);
00497                 tableMouseUp(ev);
00498         }
00499         cal.hideShowCovered();
00500 };
00501 
00502 Calendar.dayMouseDown = function(ev) {
00503         var el = Calendar.getElement(ev);
00504         if (el.disabled) {
00505                 return false;
00506         }
00507         var cal = el.calendar;
00508         cal.activeDiv = el;
00509         Calendar._C = cal;
00510         if (el.navtype != 300) with (Calendar) {
00511                 if (el.navtype == 50) {
00512                         el._current = el.innerHTML;
00513                         addEvent(document, "mousemove", tableMouseOver);
00514                 } else
00515                         addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
00516                 addClass(el, "hilite active");
00517                 addEvent(document, "mouseup", tableMouseUp);
00518         } else if (cal.isPopup) {
00519                 cal._dragStart(ev);
00520         }
00521         if (el.navtype == -1 || el.navtype == 1) {
00522                 if (cal.timeout) clearTimeout(cal.timeout);
00523                 cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
00524         } else if (el.navtype == -2 || el.navtype == 2) {
00525                 if (cal.timeout) clearTimeout(cal.timeout);
00526                 cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
00527         } else {
00528                 cal.timeout = null;
00529         }
00530         return Calendar.stopEvent(ev);
00531 };
00532 
00533 Calendar.dayMouseDblClick = function(ev) {
00534         Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
00535         if (Calendar.is_ie) {
00536                 document.selection.empty();
00537         }
00538 };
00539 
00540 Calendar.dayMouseOver = function(ev) {
00541         var el = Calendar.getElement(ev);
00542         if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
00543                 return false;
00544         }
00545         if (el.ttip) {
00546                 if (el.ttip.substr(0, 1) == "_") {
00547                         el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
00548                 }
00549                 el.calendar.tooltips.innerHTML = el.ttip;
00550         }
00551         if (el.navtype != 300) {
00552                 Calendar.addClass(el, "hilite");
00553                 if (el.caldate) {
00554                         Calendar.addClass(el.parentNode, "rowhilite");
00555                 }
00556         }
00557         return Calendar.stopEvent(ev);
00558 };
00559 
00560 Calendar.dayMouseOut = function(ev) {
00561         with (Calendar) {
00562                 var el = getElement(ev);
00563                 if (isRelated(el, ev) || _C || el.disabled)
00564                         return false;
00565                 removeClass(el, "hilite");
00566                 if (el.caldate)
00567                         removeClass(el.parentNode, "rowhilite");
00568                 if (el.calendar)
00569                         el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
00570                 return stopEvent(ev);
00571         }
00572 };
00573 
00574 /**
00575  *  A generic "click" handler :) handles all types of buttons defined in this
00576  *  calendar.
00577  */
00578 Calendar.cellClick = function(el, ev) {
00579         var cal = el.calendar;
00580         var closing = false;
00581         var newdate = false;
00582         var date = null;
00583         if (typeof el.navtype == "undefined") {
00584                 if (cal.currentDateEl) {
00585                         Calendar.removeClass(cal.currentDateEl, "selected");
00586                         Calendar.addClass(el, "selected");
00587                         closing = (cal.currentDateEl == el);
00588                         if (!closing) {
00589                                 cal.currentDateEl = el;
00590                         }
00591                 }
00592                 cal.date.setDateOnly(el.caldate);
00593                 date = cal.date;
00594                 var other_month = !(cal.dateClicked = !el.otherMonth);
00595                 if (!other_month && !cal.currentDateEl)
00596                         cal._toggleMultipleDate(new Date(date));
00597                 else
00598                         newdate = !el.disabled;
00599                 // a date was clicked
00600                 if (other_month)
00601                         cal._init(cal.firstDayOfWeek, date);
00602         } else {
00603                 if (el.navtype == 200) {
00604                         Calendar.removeClass(el, "hilite");
00605                         cal.callCloseHandler();
00606                         return;
00607                 }
00608                 date = new Date(cal.date);
00609                 if (el.navtype == 0)
00610                         date.setDateOnly(new Date()); // TODAY
00611                 // unless "today" was clicked, we assume no date was clicked so
00612                 // the selected handler will know not to close the calenar when
00613                 // in single-click mode.
00614                 // cal.dateClicked = (el.navtype == 0);
00615                 cal.dateClicked = false;
00616                 var year = date.getFullYear();
00617                 var mon = date.getMonth();
00618                 function setMonth(m) {
00619                         var day = date.getDate();
00620                         var max = date.getMonthDays(m);
00621                         if (day > max) {
00622                                 date.setDate(max);
00623                         }
00624                         date.setMonth(m);
00625                 };
00626                 switch (el.navtype) {
00627                     case 400:
00628                         Calendar.removeClass(el, "hilite");
00629                         var text = Calendar._TT["ABOUT"];
00630                         if (typeof text != "undefined") {
00631                                 text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
00632                         } else {
00633                                 // FIXME: this should be removed as soon as lang files get updated!
00634                                 text = "Help and about box text is not translated into this language.\n" +
00635                                         "If you know this language and you feel generous please update\n" +
00636                                         "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
00637                                         "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n" +
00638                                         "Thank you!\n" +
00639                                         "http://dynarch.com/mishoo/calendar.epl\n";
00640                         }
00641                         alert(text);
00642                         return;
00643                     case -2:
00644                         if (year > cal.minYear) {
00645                                 date.setFullYear(year - 1);
00646                         }
00647                         break;
00648                     case -1:
00649                         if (mon > 0) {
00650                                 setMonth(mon - 1);
00651                         } else if (year-- > cal.minYear) {
00652                                 date.setFullYear(year);
00653                                 setMonth(11);
00654                         }
00655                         break;
00656                     case 1:
00657                         if (mon < 11) {
00658                                 setMonth(mon + 1);
00659                         } else if (year < cal.maxYear) {
00660                                 date.setFullYear(year + 1);
00661                                 setMonth(0);
00662                         }
00663                         break;
00664                     case 2:
00665                         if (year < cal.maxYear) {
00666                                 date.setFullYear(year + 1);
00667                         }
00668                         break;
00669                     case 100:
00670                         cal.setFirstDayOfWeek(el.fdow);
00671                         return;
00672                     case 50:
00673                         var range = el._range;
00674                         var current = el.innerHTML;
00675                         for (var i = range.length; --i >= 0;)
00676                                 if (range[i] == current)
00677                                         break;
00678                         if (ev && ev.shiftKey) {
00679                                 if (--i < 0)
00680                                         i = range.length - 1;
00681                         } else if ( ++i >= range.length )
00682                                 i = 0;
00683                         var newval = range[i];
00684                         el.innerHTML = newval;
00685                         cal.onUpdateTime();
00686                         return;
00687                     case 0:
00688                         // TODAY will bring us here
00689                         if ((typeof cal.getDateStatus == "function") &&
00690                             cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
00691                                 return false;
00692                         }
00693                         break;
00694                 }
00695                 if (!date.equalsTo(cal.date)) {
00696                         cal.setDate(date);
00697                         newdate = true;
00698                 } else if (el.navtype == 0)
00699                         newdate = closing = true;
00700         }
00701         if (newdate) {
00702                 ev && cal.callHandler();
00703         }
00704         if (closing) {
00705                 Calendar.removeClass(el, "hilite");
00706                 ev && cal.callCloseHandler();
00707         }
00708 };
00709 
00710 // END: CALENDAR STATIC FUNCTIONS
00711 
00712 // BEGIN: CALENDAR OBJECT FUNCTIONS
00713 
00714 /**
00715  *  This function creates the calendar inside the given parent.  If _par is
00716  *  null than it creates a popup calendar inside the BODY element.  If _par is
00717  *  an element, be it BODY, then it creates a non-popup calendar (still
00718  *  hidden).  Some properties need to be set before calling this function.
00719  */
00720 Calendar.prototype.create = function (_par) {
00721         var parent = null;
00722         if (! _par) {
00723                 // default parent is the document body, in which case we create
00724                 // a popup calendar.
00725                 parent = document.getElementsByTagName("body")[0];
00726                 this.isPopup = true;
00727         } else {
00728                 parent = _par;
00729                 this.isPopup = false;
00730         }
00731         this.date = this.dateStr ? new Date(this.dateStr) : new Date();
00732 
00733         var table = Calendar.createElement("table");
00734         this.table = table;
00735         table.cellSpacing = 0;
00736         table.cellPadding = 0;
00737         table.calendar = this;
00738         Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
00739 
00740         var div = Calendar.createElement("div");
00741         this.element = div;
00742         div.className = "calendar";
00743         if (this.isPopup) {
00744                 div.style.position = "absolute";
00745                 div.style.display = "none";
00746         }
00747         div.appendChild(table);
00748 
00749         var thead = Calendar.createElement("thead", table);
00750         var cell = null;
00751         var row = null;
00752 
00753         var cal = this;
00754         var hh = function (text, cs, navtype) {
00755                 cell = Calendar.createElement("td", row);
00756                 cell.colSpan = cs;
00757                 cell.className = "button";
00758                 if (navtype != 0 && Math.abs(navtype) <= 2)
00759                         cell.className += " nav";
00760                 Calendar._add_evs(cell);
00761                 cell.calendar = cal;
00762                 cell.navtype = navtype;
00763                 cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
00764                 return cell;
00765         };
00766 
00767         row = Calendar.createElement("tr", thead);
00768         var title_length = 6;
00769         (this.isPopup) && --title_length;
00770         (this.weekNumbers) && ++title_length;
00771 
00772         hh("?", 1, 400).ttip = Calendar._TT["INFO"];
00773         this.title = hh("", title_length, 300);
00774         this.title.className = "title";
00775         if (this.isPopup) {
00776                 this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
00777                 this.title.style.cursor = "move";
00778                 hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
00779         }
00780 
00781         row = Calendar.createElement("tr", thead);
00782         row.className = "headrow";
00783 
00784         this._nav_py = hh("&#x00ab;", 1, -2);
00785         this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
00786 
00787         this._nav_pm = hh("&#x2039;", 1, -1);
00788         this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
00789 
00790         this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
00791         this._nav_now.ttip = Calendar._TT["GO_TODAY"];
00792 
00793         this._nav_nm = hh("&#x203a;", 1, 1);
00794         this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
00795 
00796         this._nav_ny = hh("&#x00bb;", 1, 2);
00797         this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
00798 
00799         // day names
00800         row = Calendar.createElement("tr", thead);
00801         row.className = "daynames";
00802         if (this.weekNumbers) {
00803                 cell = Calendar.createElement("td", row);
00804                 cell.className = "name wn";
00805                 cell.innerHTML = Calendar._TT["WK"];
00806         }
00807         for (var i = 7; i > 0; --i) {
00808                 cell = Calendar.createElement("td", row);
00809                 if (!i) {
00810                         cell.navtype = 100;
00811                         cell.calendar = this;
00812                         Calendar._add_evs(cell);
00813                 }
00814         }
00815         this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
00816         this._displayWeekdays();
00817 
00818         var tbody = Calendar.createElement("tbody", table);
00819         this.tbody = tbody;
00820 
00821         for (i = 6; i > 0; --i) {
00822                 row = Calendar.createElement("tr", tbody);
00823                 if (this.weekNumbers) {
00824                         cell = Calendar.createElement("td", row);
00825                 }
00826                 for (var j = 7; j > 0; --j) {
00827                         cell = Calendar.createElement("td", row);
00828                         cell.calendar = this;
00829                         Calendar._add_evs(cell);
00830                 }
00831         }
00832 
00833         if (this.showsTime) {
00834                 row = Calendar.createElement("tr", tbody);
00835                 row.className = "time";
00836 
00837                 cell = Calendar.createElement("td", row);
00838                 cell.className = "time";
00839                 cell.colSpan = 2;
00840                 cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";
00841 
00842                 cell = Calendar.createElement("td", row);
00843                 cell.className = "time";
00844                 cell.colSpan = this.weekNumbers ? 4 : 3;
00845 
00846                 (function(){
00847                         function makeTimePart(className, init, range_start, range_end) {
00848                                 var part = Calendar.createElement("span", cell);
00849                                 part.className = className;
00850                                 part.innerHTML = init;
00851                                 part.calendar = cal;
00852                                 part.ttip = Calendar._TT["TIME_PART"];
00853                                 part.navtype = 50;
00854                                 part._range = [];
00855                                 if (typeof range_start != "number")
00856                                         part._range = range_start;
00857                                 else {
00858                                         for (var i = range_start; i <= range_end; ++i) {
00859                                                 var txt;
00860                                                 if (i < 10 && range_end >= 10) txt = '0' + i;
00861                                                 else txt = '' + i;
00862                                                 part._range[part._range.length] = txt;
00863                                         }
00864                                 }
00865                                 Calendar._add_evs(part);
00866                                 return part;
00867                         };
00868                         var hrs = cal.date.getHours();
00869                         var mins = cal.date.getMinutes();
00870                         var t12 = !cal.time24;
00871                         var pm = (hrs > 12);
00872                         if (t12 && pm) hrs -= 12;
00873                         var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
00874                         var span = Calendar.createElement("span", cell);
00875                         span.innerHTML = ":";
00876                         span.className = "colon";
00877                         var M = makeTimePart("minute", mins, 0, 59);
00878                         var AP = null;
00879                         cell = Calendar.createElement("td", row);
00880                         cell.className = "time";
00881                         cell.colSpan = 2;
00882                         if (t12)
00883                                 AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
00884                         else
00885                                 cell.innerHTML = "&nbsp;";
00886 
00887                         cal.onSetTime = function() {
00888                                 var pm, hrs = this.date.getHours(),
00889                                         mins = this.date.getMinutes();
00890                                 if (t12) {
00891                                         pm = (hrs >= 12);
00892                                         if (pm) hrs -= 12;
00893                                         if (hrs == 0) hrs = 12;
00894                                         AP.innerHTML = pm ? "pm" : "am";
00895                                 }
00896                                 H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
00897                                 M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
00898                         };
00899 
00900                         cal.onUpdateTime = function() {
00901                                 var date = this.date;
00902                                 var h = parseInt(H.innerHTML, 10);
00903                                 if (t12) {
00904                                         if (/pm/i.test(AP.innerHTML) && h < 12)
00905                                                 h += 12;
00906                                         else if (/am/i.test(AP.innerHTML) && h == 12)
00907                                                 h = 0;
00908                                 }
00909                                 var d = date.getDate();
00910                                 var m = date.getMonth();
00911                                 var y = date.getFullYear();
00912                                 date.setHours(h);
00913                                 date.setMinutes(parseInt(M.innerHTML, 10));
00914                                 date.setFullYear(y);
00915                                 date.setMonth(m);
00916                                 date.setDate(d);
00917                                 this.dateClicked = false;
00918                                 this.callHandler();
00919                         };
00920                 })();
00921         } else {
00922                 this.onSetTime = this.onUpdateTime = function() {};
00923         }
00924 
00925         var tfoot = Calendar.createElement("tfoot", table);
00926 
00927         row = Calendar.createElement("tr", tfoot);
00928         row.className = "footrow";
00929 
00930         cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
00931         cell.className = "ttip";
00932         if (this.isPopup) {
00933                 cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
00934                 cell.style.cursor = "move";
00935         }
00936         this.tooltips = cell;
00937 
00938         div = Calendar.createElement("div", this.element);
00939         this.monthsCombo = div;
00940         div.className = "combo";
00941         for (i = 0; i < Calendar._MN.length; ++i) {
00942                 var mn = Calendar.createElement("div");
00943                 mn.className = Calendar.is_ie ? "label-IEfix" : "label";
00944                 mn.month = i;
00945                 mn.innerHTML = Calendar._SMN[i];
00946                 div.appendChild(mn);
00947         }
00948 
00949         div = Calendar.createElement("div", this.element);
00950         this.yearsCombo = div;
00951         div.className = "combo";
00952         for (i = 12; i > 0; --i) {
00953                 var yr = Calendar.createElement("div");
00954                 yr.className = Calendar.is_ie ? "label-IEfix" : "label";
00955                 div.appendChild(yr);
00956         }
00957 
00958         this._init(this.firstDayOfWeek, this.date);
00959         parent.appendChild(this.element);
00960 };
00961 
00962 /** keyboard navigation, only for popup calendars */
00963 Calendar._keyEvent = function(ev) {
00964         var cal = window._dynarch_popupCalendar;
00965         if (!cal || cal.multiple)
00966                 return false;
00967         (Calendar.is_ie) && (ev = window.event);
00968         var act = (Calendar.is_ie || ev.type == "keypress"),
00969                 K = ev.keyCode;
00970         if (ev.ctrlKey) {
00971                 switch (K) {
00972                     case 37: // KEY left
00973                         act && Calendar.cellClick(cal._nav_pm);
00974                         break;
00975                     case 38: // KEY up
00976                         act && Calendar.cellClick(cal._nav_py);
00977                         break;
00978                     case 39: // KEY right
00979                         act && Calendar.cellClick(cal._nav_nm);
00980                         break;
00981                     case 40: // KEY down
00982                         act && Calendar.cellClick(cal._nav_ny);
00983                         break;
00984                     default:
00985                         return false;
00986                 }
00987         } else switch (K) {
00988             case 32: // KEY space (now)
00989                 Calendar.cellClick(cal._nav_now);
00990                 break;
00991             case 27: // KEY esc
00992                 act && cal.callCloseHandler();
00993                 break;
00994             case 37: // KEY left
00995             case 38: // KEY up
00996             case 39: // KEY right
00997             case 40: // KEY down
00998                 if (act) {
00999                         var prev, x, y, ne, el, step;
01000                         prev = K == 37 || K == 38;
01001                         step = (K == 37 || K == 39) ? 1 : 7;
01002                         function setVars() {
01003                                 el = cal.currentDateEl;
01004                                 var p = el.pos;
01005                                 x = p & 15;
01006                                 y = p >> 4;
01007                                 ne = cal.ar_days[y][x];
01008                         };setVars();
01009                         function prevMonth() {
01010                                 var date = new Date(cal.date);
01011                                 date.setDate(date.getDate() - step);
01012                                 cal.setDate(date);
01013                         };
01014                         function nextMonth() {
01015                                 var date = new Date(cal.date);
01016                                 date.setDate(date.getDate() + step);
01017                                 cal.setDate(date);
01018                         };
01019                         while (1) {
01020                                 switch (K) {
01021                                     case 37: // KEY left
01022                                         if (--x >= 0)
01023                                                 ne = cal.ar_days[y][x];
01024                                         else {
01025                                                 x = 6;
01026                                                 K = 38;
01027                                                 continue;
01028                                         }
01029                                         break;
01030                                     case 38: // KEY up
01031                                         if (--y >= 0)
01032                                                 ne = cal.ar_days[y][x];
01033                                         else {
01034                                                 prevMonth();
01035                                                 setVars();
01036                                         }
01037                                         break;
01038                                     case 39: // KEY right
01039                                         if (++x < 7)
01040                                                 ne = cal.ar_days[y][x];
01041                                         else {
01042                                                 x = 0;
01043                                                 K = 40;
01044                                                 continue;
01045                                         }
01046                                         break;
01047                                     case 40: // KEY down
01048                                         if (++y < cal.ar_days.length)
01049                                                 ne = cal.ar_days[y][x];
01050                                         else {
01051                                                 nextMonth();
01052                                                 setVars();
01053                                         }
01054                                         break;
01055                                 }
01056                                 break;
01057                         }
01058                         if (ne) {
01059                                 if (!ne.disabled)
01060                                         Calendar.cellClick(ne);
01061                                 else if (prev)
01062                                         prevMonth();
01063                                 else
01064                                         nextMonth();
01065                         }
01066                 }
01067                 break;
01068             case 13: // KEY enter
01069                 if (act)
01070                         Calendar.cellClick(cal.currentDateEl, ev);
01071                 break;
01072             default:
01073                 return false;
01074         }
01075         return Calendar.stopEvent(ev);
01076 };
01077 
01078 /**
01079  *  (RE)Initializes the calendar to the given date and firstDayOfWeek
01080  */
01081 Calendar.prototype._init = function (firstDayOfWeek, date) {
01082         var today = new Date(),
01083                 TY = today.getFullYear(),
01084                 TM = today.getMonth(),
01085                 TD = today.getDate();
01086         this.table.style.visibility = "hidden";
01087         var year = date.getFullYear();
01088         if (year < this.minYear) {
01089                 year = this.minYear;
01090                 date.setFullYear(year);
01091         } else if (year > this.maxYear) {
01092                 year = this.maxYear;
01093                 date.setFullYear(year);
01094         }
01095         this.firstDayOfWeek = firstDayOfWeek;
01096         this.date = new Date(date);
01097         var month = date.getMonth();
01098         var mday = date.getDate();
01099         var no_days = date.getMonthDays();
01100 
01101         // calendar voodoo for computing the first day that would actually be
01102         // displayed in the calendar, even if it's from the previous month.
01103         // WARNING: this is magic. ;-)
01104         date.setDate(1);
01105         var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
01106         if (day1 < 0)
01107                 day1 += 7;
01108         date.setDate(-day1);
01109         date.setDate(date.getDate() + 1);
01110 
01111         var row = this.tbody.firstChild;
01112         var MN = Calendar._SMN[month];
01113         var ar_days = this.ar_days = new Array();
01114         var weekend = Calendar._TT["WEEKEND"];
01115         var dates = this.multiple ? (this.datesCells = {}) : null;
01116         for (var i = 0; i < 6; ++i, row = row.nextSibling) {
01117                 var cell = row.firstChild;
01118                 if (this.weekNumbers) {
01119                         cell.className = "day wn";
01120                         cell.innerHTML = date.getWeekNumber();
01121                         cell = cell.nextSibling;
01122                 }
01123                 row.className = "daysrow";
01124                 var hasdays = false, iday, dpos = ar_days[i] = [];
01125                 for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
01126                         iday = date.getDate();
01127                         var wday = date.getDay();
01128                         cell.className = "day";
01129                         cell.pos = i << 4 | j;
01130                         dpos[j] = cell;
01131                         var current_month = (date.getMonth() == month);
01132                         if (!current_month) {
01133                                 if (this.showsOtherMonths) {
01134                                         cell.className += " othermonth";
01135                                         cell.otherMonth = true;
01136                                 } else {
01137                                         cell.className = "emptycell";
01138                                         cell.innerHTML = "&nbsp;";
01139                                         cell.disabled = true;
01140                                         continue;
01141                                 }
01142                         } else {
01143                                 cell.otherMonth = false;
01144                                 hasdays = true;
01145                         }
01146                         cell.disabled = false;
01147                         cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
01148                         if (dates)
01149                                 dates[date.print("%Y%m%d")] = cell;
01150                         if (this.getDateStatus) {
01151                                 var status = this.getDateStatus(date, year, month, iday);
01152                                 if (this.getDateToolTip) {
01153                                         var toolTip = this.getDateToolTip(date, year, month, iday);
01154                                         if (toolTip)
01155                                                 cell.title = toolTip;
01156                                 }
01157                                 if (status === true) {
01158                                         cell.className += " disabled";
01159                                         cell.disabled = true;
01160                                 } else {
01161                                         if (/disabled/i.test(status))
01162                                                 cell.disabled = true;
01163                                         cell.className += " " + status;
01164                                 }
01165                         }
01166                         if (!cell.disabled) {
01167                                 cell.caldate = new Date(date);
01168                                 cell.ttip = "_";
01169                                 if (!this.multiple && current_month
01170                                     && iday == mday && this.hiliteToday) {
01171                                         cell.className += " selected";
01172                                         this.currentDateEl = cell;
01173                                 }
01174                                 if (date.getFullYear() == TY &&
01175                                     date.getMonth() == TM &&
01176                                     iday == TD) {
01177                                         cell.className += " today";
01178                                         cell.ttip += Calendar._TT["PART_TODAY"];
01179                                 }
01180                                 if (weekend.indexOf(wday.toString()) != -1)
01181                                         cell.className += cell.otherMonth ? " oweekend" : " weekend";
01182                         }
01183                 }
01184                 if (!(hasdays || this.showsOtherMonths))
01185                         row.className = "emptyrow";
01186         }
01187         this.title.innerHTML = Calendar._MN[month] + ", " + year;
01188         this.onSetTime();
01189         this.table.style.visibility = "visible";
01190         this._initMultipleDates();
01191         // PROFILE
01192         // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
01193 };
01194 
01195 Calendar.prototype._initMultipleDates = function() {
01196         if (this.multiple) {
01197                 for (var i in this.multiple) {
01198                         var cell = this.datesCells[i];
01199                         var d = this.multiple[i];
01200                         if (!d)
01201                                 continue;
01202                         if (cell)
01203                                 cell.className += " selected";
01204                 }
01205         }
01206 };
01207 
01208 Calendar.prototype._toggleMultipleDate = function(date) {
01209         if (this.multiple) {
01210                 var ds = date.print("%Y%m%d");
01211                 var cell = this.datesCells[ds];
01212                 if (cell) {
01213                         var d = this.multiple[ds];
01214                         if (!d) {
01215                                 Calendar.addClass(cell, "selected");
01216                                 this.multiple[ds] = date;
01217                         } else {
01218                                 Calendar.removeClass(cell, "selected");
01219                                 delete this.multiple[ds];
01220                         }
01221                 }
01222         }
01223 };
01224 
01225 Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
01226         this.getDateToolTip = unaryFunction;
01227 };
01228 
01229 /**
01230  *  Calls _init function above for going to a certain date (but only if the
01231  *  date is different than the currently selected one).
01232  */
01233 Calendar.prototype.setDate = function (date) {
01234         if (!date.equalsTo(this.date)) {
01235                 this._init(this.firstDayOfWeek, date);
01236         }
01237 };
01238 
01239 /**
01240  *  Refreshes the calendar.  Useful if the "disabledHandler" function is
01241  *  dynamic, meaning that the list of disabled date can change at runtime.
01242  *  Just * call this function if you think that the list of disabled dates
01243  *  should * change.
01244  */
01245 Calendar.prototype.refresh = function () {
01246         this._init(this.firstDayOfWeek, this.date);
01247 };
01248 
01249 /** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
01250 Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
01251         this._init(firstDayOfWeek, this.date);
01252         this._displayWeekdays();
01253 };
01254 
01255 /**
01256  *  Allows customization of what dates are enabled.  The "unaryFunction"
01257  *  parameter must be a function object that receives the date (as a JS Date
01258  *  object) and returns a boolean value.  If the returned value is true then
01259  *  the passed date will be marked as disabled.
01260  */
01261 Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
01262         this.getDateStatus = unaryFunction;
01263 };
01264 
01265 /** Customization of allowed year range for the calendar. */
01266 Calendar.prototype.setRange = function (a, z) {
01267         this.minYear = a;
01268         this.maxYear = z;
01269 };
01270 
01271 /** Calls the first user handler (selectedHandler). */
01272 Calendar.prototype.callHandler = function () {
01273         if (this.onSelected) {
01274                 this.onSelected(this, this.date.print(this.dateFormat));
01275         }
01276 };
01277 
01278 /** Calls the second user handler (closeHandler). */
01279 Calendar.prototype.callCloseHandler = function () {
01280         if (this.onClose) {
01281                 this.onClose(this);
01282         }
01283         this.hideShowCovered();
01284 };
01285 
01286 /** Removes the calendar object from the DOM tree and destroys it. */
01287 Calendar.prototype.destroy = function () {
01288         var el = this.element.parentNode;
01289         el.removeChild(this.element);
01290         Calendar._C = null;
01291         window._dynarch_popupCalendar = null;
01292 };
01293 
01294 /**
01295  *  Moves the calendar element to a different section in the DOM tree (changes
01296  *  its parent).
01297  */
01298 Calendar.prototype.reparent = function (new_parent) {
01299         var el = this.element;
01300         el.parentNode.removeChild(el);
01301         new_parent.appendChild(el);
01302 };
01303 
01304 // This gets called when the user presses a mouse button anywhere in the
01305 // document, if the calendar is shown.  If the click was outside the open
01306 // calendar this function closes it.
01307 Calendar._checkCalendar = function(ev) {
01308         var calendar = window._dynarch_popupCalendar;
01309         if (!calendar) {
01310                 return false;
01311         }
01312         var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
01313         for (; el != null && el != calendar.element; el = el.parentNode);
01314         if (el == null) {
01315                 // calls closeHandler which should hide the calendar.
01316                 window._dynarch_popupCalendar.callCloseHandler();
01317                 return Calendar.stopEvent(ev);
01318         }
01319 };
01320 
01321 /** Shows the calendar. */
01322 Calendar.prototype.show = function () {
01323         var rows = this.table.getElementsByTagName("tr");
01324         for (var i = rows.length; i > 0;) {
01325                 var row = rows[--i];
01326                 Calendar.removeClass(row, "rowhilite");
01327                 var cells = row.getElementsByTagName("td");
01328                 for (var j = cells.length; j > 0;) {
01329                         var cell = cells[--j];
01330                         Calendar.removeClass(cell, "hilite");
01331                         Calendar.removeClass(cell, "active");
01332                 }
01333         }
01334         this.element.style.display = "block";
01335         this.hidden = false;
01336         if (this.isPopup) {
01337                 window._dynarch_popupCalendar = this;
01338                 Calendar.addEvent(document, "keydown", Calendar._keyEvent);
01339                 Calendar.addEvent(document, "keypress", Calendar._keyEvent);
01340                 Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
01341         }
01342         this.hideShowCovered();
01343 };
01344 
01345 /**
01346  *  Hides the calendar.  Also removes any "hilite" from the class of any TD
01347  *  element.
01348  */
01349 Calendar.prototype.hide = function () {
01350         if (this.isPopup) {
01351                 Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
01352                 Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
01353                 Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
01354         }
01355         this.element.style.display = "none";
01356         this.hidden = true;
01357         this.hideShowCovered();
01358 };
01359 
01360 /**
01361  *  Shows the calendar at a given absolute position (beware that, depending on
01362  *  the calendar element style -- position property -- this might be relative
01363  *  to the parent's containing rectangle).
01364  */
01365 Calendar.prototype.showAt = function (x, y) {
01366         var s = this.element.style;
01367         s.left = x + "px";
01368         s.top = y + "px";
01369         this.show();
01370 };
01371 
01372 /** Shows the calendar near a given element. */
01373 Calendar.prototype.showAtElement = function (el, opts) {
01374         var self = this;
01375         var p = Calendar.getAbsolutePos(el);
01376         if (!opts || typeof opts != "string") {
01377                 this.showAt(p.x, p.y + el.offsetHeight);
01378                 return true;
01379         }
01380         function fixPosition(box) {
01381                 if (box.x < 0)
01382                         box.x = 0;
01383                 if (box.y < 0)
01384                         box.y = 0;
01385                 var cp = document.createElement("div");
01386                 var s = cp.style;
01387                 s.position = "absolute";
01388                 s.right = s.bottom = s.width = s.height = "0px";
01389                 document.body.appendChild(cp);
01390                 var br = Calendar.getAbsolutePos(cp);
01391                 document.body.removeChild(cp);
01392                 if (Calendar.is_ie) {
01393                         br.y += document.body.scrollTop;
01394                         br.x += document.body.scrollLeft;
01395                 } else {
01396                         br.y += window.scrollY;
01397                         br.x += window.scrollX;
01398                 }
01399                 var tmp = box.x + box.width - br.x;
01400                 if (tmp > 0) box.x -= tmp;
01401                 tmp = box.y + box.height - br.y;
01402                 if (tmp > 0) box.y -= tmp;
01403         };
01404         this.element.style.display = "block";
01405         Calendar.continuation_for_the_fucking_khtml_browser = function() {
01406                 var w = self.element.offsetWidth;
01407                 var h = self.element.offsetHeight;
01408                 self.element.style.display = "none";
01409                 var valign = opts.substr(0, 1);
01410                 var halign = "l";
01411                 if (opts.length > 1) {
01412                         halign = opts.substr(1, 1);
01413                 }
01414                 // vertical alignment
01415                 switch (valign) {
01416                     case "T": p.y -= h; break;
01417                     case "B": p.y += el.offsetHeight; break;
01418                     case "C": p.y += (el.offsetHeight - h) / 2; break;
01419                     case "t": p.y += el.offsetHeight - h; break;
01420                     case "b": break; // already there
01421                 }
01422                 // horizontal alignment
01423                 switch (halign) {
01424                     case "L": p.x -= w; break;
01425                     case "R": p.x += el.offsetWidth; break;
01426                     case "C": p.x += (el.offsetWidth - w) / 2; break;
01427                     case "l": p.x += el.offsetWidth - w; break;
01428                     case "r": break; // already there
01429                 }
01430                 p.width = w;
01431                 p.height = h + 40;
01432                 self.monthsCombo.style.display = "none";
01433                 fixPosition(p);
01434                 self.showAt(p.x, p.y);
01435         };
01436         if (Calendar.is_khtml)
01437                 setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
01438         else
01439                 Calendar.continuation_for_the_fucking_khtml_browser();
01440 };
01441 
01442 /** Customizes the date format. */
01443 Calendar.prototype.setDateFormat = function (str) {
01444         this.dateFormat = str;
01445 };
01446 
01447 /** Customizes the tooltip date format. */
01448 Calendar.prototype.setTtDateFormat = function (str) {
01449         this.ttDateFormat = str;
01450 };
01451 
01452 /**
01453  *  Tries to identify the date represented in a string.  If successful it also
01454  *  calls this.setDate which moves the calendar to the given date.
01455  */
01456 Calendar.prototype.parseDate = function(str, fmt) {
01457         if (!fmt)
01458                 fmt = this.dateFormat;
01459         this.setDate(Date.parseDate(str, fmt));
01460 };
01461 
01462 Calendar.prototype.hideShowCovered = function () {
01463         if (!Calendar.is_ie && !Calendar.is_opera)
01464                 return;
01465         function getVisib(obj){
01466                 var value = obj.style.visibility;
01467                 if (!value) {
01468                         if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
01469                                 if (!Calendar.is_khtml)
01470                                         value = document.defaultView.
01471                                                 getComputedStyle(obj, "").getPropertyValue("visibility");
01472                                 else
01473                                         value = '';
01474                         } else if (obj.currentStyle) { // IE
01475                                 value = obj.currentStyle.visibility;
01476                         } else
01477                                 value = '';
01478                 }
01479                 return value;
01480         };
01481 
01482         var tags = new Array("applet", "iframe", "select");
01483         var el = this.element;
01484 
01485         var p = Calendar.getAbsolutePos(el);
01486         var EX1 = p.x;
01487         var EX2 = el.offsetWidth + EX1;
01488         var EY1 = p.y;
01489         var EY2 = el.offsetHeight + EY1;
01490 
01491         for (var k = tags.length; k > 0; ) {
01492                 var ar = document.getElementsByTagName(tags[--k]);
01493                 var cc = null;
01494 
01495                 for (var i = ar.length; i > 0;) {
01496                         cc = ar[--i];
01497 
01498                         p = Calendar.getAbsolutePos(cc);
01499                         var CX1 = p.x;
01500                         var CX2 = cc.offsetWidth + CX1;
01501                         var CY1 = p.y;
01502                         var CY2 = cc.offsetHeight + CY1;
01503 
01504                         if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
01505                                 if (!cc.__msh_save_visibility) {
01506                                         cc.__msh_save_visibility = getVisib(cc);
01507                                 }
01508                                 cc.style.visibility = cc.__msh_save_visibility;
01509                         } else {
01510                                 if (!cc.__msh_save_visibility) {
01511                                         cc.__msh_save_visibility = getVisib(cc);
01512                                 }
01513                                 cc.style.visibility = "hidden";
01514                         }
01515                 }
01516         }
01517 };
01518 
01519 /** Internal function; it displays the bar with the names of the weekday. */
01520 Calendar.prototype._displayWeekdays = function () {
01521         var fdow = this.firstDayOfWeek;
01522         var cell = this.firstdayname;
01523         var weekend = Calendar._TT["WEEKEND"];
01524         for (var i = 0; i < 7; ++i) {
01525                 cell.className = "day name";
01526                 var realday = (i + fdow) % 7;
01527                 if (i) {
01528                         cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
01529                         cell.navtype = 100;
01530                         cell.calendar = this;
01531                         cell.fdow = realday;
01532                         Calendar._add_evs(cell);
01533                 }
01534                 if (weekend.indexOf(realday.toString()) != -1) {
01535                         Calendar.addClass(cell, "weekend");
01536                 }
01537                 cell.innerHTML = Calendar._SDN[(i + fdow) % 7];
01538                 cell = cell.nextSibling;
01539         }
01540 };
01541 
01542 /** Internal function.  Hides all combo boxes that might be displayed. */
01543 Calendar.prototype._hideCombos = function () {
01544         this.monthsCombo.style.display = "none";
01545         this.yearsCombo.style.display = "none";
01546 };
01547 
01548 /** Internal function.  Starts dragging the element. */
01549 Calendar.prototype._dragStart = function (ev) {
01550         if (this.dragging) {
01551                 return;
01552         }
01553         this.dragging = true;
01554         var posX;
01555         var posY;
01556         if (Calendar.is_ie) {
01557                 posY = window.event.clientY + document.body.scrollTop;
01558                 posX = window.event.clientX + document.body.scrollLeft;
01559         } else {
01560                 posY = ev.clientY + window.scrollY;
01561                 posX = ev.clientX + window.scrollX;
01562         }
01563         var st = this.element.style;
01564         this.xOffs = posX - parseInt(st.left);
01565         this.yOffs = posY - parseInt(st.top);
01566         with (Calendar) {
01567                 addEvent(document, "mousemove", calDragIt);
01568                 addEvent(document, "mouseup", calDragEnd);
01569         }
01570 };
01571 
01572 // BEGIN: DATE OBJECT PATCHES
01573 
01574 /** Adds the number of days array to the Date object. */
01575 Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
01576 
01577 /** Constants used for time computations */
01578 Date.SECOND = 1000 /* milliseconds */;
01579 Date.MINUTE = 60 * Date.SECOND;
01580 Date.HOUR   = 60 * Date.MINUTE;
01581 Date.DAY    = 24 * Date.HOUR;
01582 Date.WEEK   =  7 * Date.DAY;
01583 
01584 Date.parseDate = function(str, fmt) {
01585         var today = new Date();
01586         var y = 0;
01587         var m = -1;
01588         var d = 0;
01589         var a = str.split(/\W+/);
01590         var b = fmt.match(/%./g);
01591         var i = 0, j = 0;
01592         var hr = 0;
01593         var min = 0;
01594         for (i = 0; i < a.length; ++i) {
01595                 if (!a[i])
01596                         continue;
01597                 switch (b[i]) {
01598                     case "%d":
01599                     case "%e":
01600                         d = parseInt(a[i], 10);
01601                         break;
01602 
01603                     case "%m":
01604                         m = parseInt(a[i], 10) - 1;
01605                         break;
01606 
01607                     case "%Y":
01608                     case "%y":
01609                         y = parseInt(a[i], 10);
01610                         (y < 100) && (y += (y > 29) ? 1900 : 2000);
01611                         break;
01612 
01613                     case "%b":
01614                     case "%B":
01615                         for (j = 0; j < 12; ++j) {
01616                                 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
01617                         }
01618                         break;
01619 
01620                     case "%H":
01621                     case "%I":
01622                     case "%k":
01623                     case "%l":
01624                         hr = parseInt(a[i], 10);
01625                         break;
01626 
01627                     case "%P":
01628                     case "%p":
01629                         if (/pm/i.test(a[i]) && hr < 12)
01630                                 hr += 12;
01631                         else if (/am/i.test(a[i]) && hr >= 12)
01632                                 hr -= 12;
01633                         break;
01634 
01635                     case "%M":
01636                         min = parseInt(a[i], 10);
01637                         break;
01638                 }
01639         }
01640         if (isNaN(y)) y = today.getFullYear();
01641         if (isNaN(m)) m = today.getMonth();
01642         if (isNaN(d)) d = today.getDate();
01643         if (isNaN(hr)) hr = today.getHours();
01644         if (isNaN(min)) min = today.getMinutes();
01645         if (y != 0 && m != -1 && d != 0)
01646                 return new Date(y, m, d, hr, min, 0);
01647         y = 0; m = -1; d = 0;
01648         for (i = 0; i < a.length; ++i) {
01649                 if (a[i].search(/[a-zA-Z]+/) != -1) {
01650                         var t = -1;
01651                         for (j = 0; j < 12; ++j) {
01652                                 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
01653                         }
01654                         if (t != -1) {
01655                                 if (m != -1) {
01656                                         d = m+1;
01657                                 }
01658                                 m = t;
01659                         }
01660                 } else if (parseInt(a[i], 10) <= 12 && m == -1) {
01661                         m = a[i]-1;
01662                 } else if (parseInt(a[i], 10) > 31 && y == 0) {
01663                         y = parseInt(a[i], 10);
01664                         (y < 100) && (y += (y > 29) ? 1900 : 2000);
01665                 } else if (d == 0) {
01666                         d = a[i];
01667                 }
01668         }
01669         if (y == 0)
01670                 y = today.getFullYear();
01671         if (m != -1 && d != 0)
01672                 return new Date(y, m, d, hr, min, 0);
01673         return today;
01674 };
01675 
01676 /** Returns the number of days in the current month */
01677 Date.prototype.getMonthDays = function(month) {
01678         var year = this.getFullYear();
01679         if (typeof month == "undefined") {
01680                 month = this.getMonth();
01681         }
01682         if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
01683                 return 29;
01684         } else {
01685                 return Date._MD[month];
01686         }
01687 };
01688 
01689 /** Returns the number of day in the year. */
01690 Date.prototype.getDayOfYear = function() {
01691         var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
01692         var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
01693         var time = now - then;
01694         return Math.floor(time / Date.DAY);
01695 };
01696 
01697 /** Returns the number of the week in year, as defined in ISO 8601. */
01698 Date.prototype.getWeekNumber = function() {
01699         var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
01700         var DoW = d.getDay();
01701         d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
01702         var ms = d.valueOf(); // GMT
01703         d.setMonth(0);
01704         d.setDate(4); // Thu in Week 1
01705         return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
01706 };
01707 
01708 /** Checks date and time equality */
01709 Date.prototype.equalsTo = function(date) {
01710         return ((this.getFullYear() == date.getFullYear()) &&
01711                 (this.getMonth() == date.getMonth()) &&
01712                 (this.getDate() == date.getDate()) &&
01713                 (this.getHours() == date.getHours()) &&
01714                 (this.getMinutes() == date.getMinutes()));
01715 };
01716 
01717 /** Set only the year, month, date parts (keep existing time) */
01718 Date.prototype.setDateOnly = function(date) {
01719         var tmp = new Date(date);
01720         this.setDate(1);
01721         this.setFullYear(tmp.getFullYear());
01722         this.setMonth(tmp.getMonth());
01723         this.setDate(tmp.getDate());
01724 };
01725 
01726 /** Prints the date in a string according to the given format. */
01727 Date.prototype.print = function (str) {
01728         var m = this.getMonth();
01729         var d = this.getDate();
01730         var y = this.getFullYear();
01731         var wn = this.getWeekNumber();
01732         var w = this.getDay();
01733         var s = {};
01734         var hr = this.getHours();
01735         var pm = (hr >= 12);
01736         var ir = (pm) ? (hr - 12) : hr;
01737         var dy = this.getDayOfYear();
01738         if (ir == 0)
01739                 ir = 12;
01740         var min = this.getMinutes();
01741         var sec = this.getSeconds();
01742         s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
01743         s["%A"] = Calendar._DN[w]; // full weekday name
01744         s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
01745         s["%B"] = Calendar._MN[m]; // full month name
01746         // FIXME: %c : preferred date and time representation for the current locale
01747         s["%C"] = 1 + Math.floor(y / 100); // the century number
01748         s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
01749         s["%e"] = d; // the day of the month (range 1 to 31)
01750         // FIXME: %D : american date style: %m/%d/%y
01751         // FIXME: %E, %F, %G, %g, %h (man strftime)
01752         s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
01753         s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
01754         s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
01755         s["%k"] = hr;           // hour, range 0 to 23 (24h format)
01756         s["%l"] = ir;           // hour, range 1 to 12 (12h format)
01757         s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
01758         s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
01759         s["%n"] = "\n";         // a newline character
01760         s["%p"] = pm ? "PM" : "AM";
01761         s["%P"] = pm ? "pm" : "am";
01762         // FIXME: %r : the time in am/pm notation %I:%M:%S %p
01763         // FIXME: %R : the time in 24-hour notation %H:%M
01764         s["%s"] = Math.floor(this.getTime() / 1000);
01765         s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
01766         s["%t"] = "\t";         // a tab character
01767         // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
01768         s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
01769         s["%u"] = w + 1;        // the day of the week (range 1 to 7, 1 = MON)
01770         s["%w"] = w;            // the day of the week (range 0 to 6, 0 = SUN)
01771         // FIXME: %x : preferred date representation for the current locale without the time
01772         // FIXME: %X : preferred time representation for the current locale without the date
01773         s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
01774         s["%Y"] = y;            // year with the century
01775         s["%%"] = "%";          // a literal '%' character
01776 
01777         var re = /%./g;
01778         if (!Calendar.is_ie5 && !Calendar.is_khtml)
01779                 return str.replace(re, function (par) { return s[par] || par; });
01780 
01781         var a = str.match(re);
01782         for (var i = 0; i < a.length; i++) {
01783                 var tmp = s[a[i]];
01784                 if (tmp) {
01785                         re = new RegExp(a[i], 'g');
01786                         str = str.replace(re, tmp);
01787                 }
01788         }
01789 
01790         return str;
01791 };
01792 
01793     if ( Date.prototype.__msh_oldSetFullYear == null )
01794     {
01795         Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
01796     }
01797 //Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
01798 Date.prototype.setFullYear = function(y) {
01799         var d = new Date(this);
01800         d.__msh_oldSetFullYear(y);
01801         if (d.getMonth() != this.getMonth())
01802                 this.setDate(28);
01803         this.__msh_oldSetFullYear(y);
01804 };
01805 
01806 // END: DATE OBJECT PATCHES
01807 
01808 
01809 // global object that remembers the calendar
01810 window._dynarch_popupCalendar = null;
 All Data Structures Namespaces Files Functions Variables Enumerations