Main Page | Modules | Directories | File List | File Members

calendar.js

Go to the documentation of this file.
00001 /*
00002  * The Zapatec DHTML Calendar
00003  *
00004  * Copyright (c) 2004 by Zapatec, Inc.
00005  * http://www.zapatec.com
00006  * 1700 MLK Way, Berkeley, California,
00007  * 94709, U.S.A.
00008  * All rights reserved.
00009  *
00010  * Main Calendar file. Creates a popup or flat calendar with various options.
00011  *
00012  * Original version written by Mihai Bazon,
00013  * http://www.bazon.net/mishoo/calendar.epl
00014  */
00015 
00016 // $Id: calendar.js 2177 2006-03-20 14:14:02Z slip $
00017 
00043 Zapatec.Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
00044         // member variables
00045         this.bShowHistoryEvent=false;   // did the History event on Today fire?
00046         this.activeDiv = null;
00047         this.currentDateEl = null;
00048         this.getDateStatus = null;
00049         this.getDateToolTip = null;
00050         this.getDateText = null;
00051         this.timeout = null;
00052         this.onSelected = onSelected || null;
00053         this.onClose = onClose || null;
00054         this.onFDOW = null;
00055         this.dragging = false;
00056         this.hidden = false;
00057         this.minYear = 1970;
00058         this.maxYear = 2050;
00059         this.minMonth = 0;
00060         this.maxMonth = 11;
00061         this.dateFormat = Zapatec.Calendar.i18n("DEF_DATE_FORMAT");
00062         this.ttDateFormat = Zapatec.Calendar.i18n("TT_DATE_FORMAT");
00063         this.historyDateFormat = "%B %d, %Y";
00064         this.isPopup = true;
00065         this.weekNumbers = true;
00066         this.noGrab = false;
00067         if (Zapatec.Calendar.prefs.fdow || (Zapatec.Calendar.prefs.fdow == 0)) {
00068                 this.firstDayOfWeek = parseInt(Zapatec.Calendar.prefs.fdow, 10);
00069         }
00070         else {
00071                 var fd = 0;
00072                 if (typeof firstDayOfWeek == "number") {
00073                         fd = firstDayOfWeek;
00074                 } else if (typeof Zapatec.Calendar._FD == 'number') {
00075                         fd = Zapatec.Calendar._FD;
00076                 }
00077                 this.firstDayOfWeek = fd;
00078         }
00079         this.showsOtherMonths = false;
00080         this.dateStr = dateStr;
00081         this.showsTime = false;
00082         this.sortOrder = "asc"; //Sort for multiple dates in ascending order
00083         this.time24 = true;
00084         this.timeInterval = null; //step for changing time
00085         this.yearStep = 2;
00086         this.hiliteToday = true;
00087         this.multiple = null;
00088         // HTML elements
00089         this.table = null;
00090         this.element = null;
00091         this.tbody = new Array(); //array of rows of months
00092         this.firstdayname = null;
00093         // Combo boxes
00094         this.monthsCombo = null;   // months
00095         this.hilitedMonth = null;
00096         this.activeMonth = null;
00097         this.yearsCombo = null;    // years
00098         this.hilitedYear = null;
00099         this.activeYear = null;
00100         this.histCombo = null;     // history
00101         this.hilitedHist = null;
00102         // Information
00103         this.dateClicked = false;
00104         this.numberMonths = 1; //number of months displayed
00105         this.controlMonth = 1; //the number of month with all the combos to control the date
00106         this.vertical = false; //vertical or horizontal positioning of months
00107         this.monthsInRow = 1; //number of months in one row
00108         this.titles = new Array(); //array of titles for the months
00109         this.rowsOfDayNames = new Array(); //array of rows of day names
00110         this.helpButton = true;
00111         this.disableFdowClick = false;
00112 
00113         // one-time initializations
00114         Zapatec.Calendar._initSDN();
00115 };
00116 
00123 Zapatec.Calendar._initSDN = function() {
00124         if (typeof Zapatec.Calendar._TT._SDN == "undefined") {
00125                 // table of short day names
00126                 if (typeof Zapatec.Calendar._TT._SDN_len == "undefined")
00127                         Zapatec.Calendar._TT._SDN_len = 3;
00128                 var ar = [];
00129                 for (var i = 8; i > 0;) {
00130                         ar[--i] = Zapatec.Calendar._TT._DN[i].substr(0, Zapatec.Calendar._TT._SDN_len);
00131                 }
00132                 Zapatec.Calendar._TT._SDN = ar;
00133                 // table of short month names
00134                 if (typeof Zapatec.Calendar._TT._SMN_len == "undefined")
00135                         Zapatec.Calendar._TT._SMN_len = 3;
00136                 ar = [];
00137                 for (var i = 12; i > 0;) {
00138                         ar[--i] = Zapatec.Calendar._TT._MN[i].substr(0, Zapatec.Calendar._TT._SMN_len);
00139                 }
00140                 Zapatec.Calendar._TT._SMN = ar;
00141         }
00142 };
00143 
00165 Zapatec.Calendar.i18n = function(str, type) {
00166         var tr = '';
00167         if (!type) {
00168                 // normal _TT request
00169                 if (Zapatec.Calendar._TT)
00170                         tr = Zapatec.Calendar._TT[str];
00171                 if (!tr && Zapatec.Calendar._TT_en)
00172                         tr = Zapatec.Calendar._TT_en[str];
00173         } else switch(type) {
00174             case "dn"  : tr = Zapatec.Calendar._TT._DN[str];  break;
00175             case "sdn" : tr = Zapatec.Calendar._TT._SDN[str]; break;
00176             case "mn"  : tr = Zapatec.Calendar._TT._MN[str];  break;
00177             case "smn" : tr = Zapatec.Calendar._TT._SMN[str]; break;
00178         }
00179         if (!tr) tr = "" + str;
00180         return tr;
00181 };
00182 
00183 // ** constants
00184 
00186 Zapatec.Calendar._C = null;
00187 
00189 Zapatec.Calendar.prefs = {
00190         fdow     : null,        
00191         history  : "",          
00192         sortOrder : "asc", 
00193         hsize    : 9            
00194 };
00195 
00196 // BEGIN: CALENDAR STATIC FUNCTIONS
00197 
00201 Zapatec.Calendar.savePrefs = function() {
00202         // FIXME: should we make the domain, path and expiration time configurable?
00203         // I guess these defaults are right though..
00204         Zapatec.Utils.writeCookie("ZP_CAL", Zapatec.Utils.makePref(this.prefs), null, '/', 30);
00205 };
00206 
00210 Zapatec.Calendar.loadPrefs = function() {
00211         var txt = Zapatec.Utils.getCookie("ZP_CAL"), tmp;
00212         if (txt) {
00213                 tmp = Zapatec.Utils.loadPref(txt);
00214                 if (tmp)
00215                         Zapatec.Utils.mergeObjects(this.prefs, tmp);
00216         }
00217         // FIXME: DEBUG!
00218         //this.prefs.history = "1979/03/08,1976/12/28,1978/08/31,1998/09/21";
00219         //this.prefs.history = null;
00220 };
00221 
00227 Zapatec.Calendar._add_evs = function(el) {
00228         var C = Zapatec.Calendar;
00229         Zapatec.Utils.addEvent(el, "mouseover", C.dayMouseOver);
00230         Zapatec.Utils.addEvent(el, "mousedown", C.dayMouseDown);
00231         Zapatec.Utils.addEvent(el, "mouseout", C.dayMouseOut);
00232         if (Zapatec.is_ie)
00233                 Zapatec.Utils.addEvent(el, "dblclick", C.dayMouseDblClick);
00234 };
00235 
00242 Zapatec.Calendar._del_evs = function(el) {
00243         var C = this;
00244         Zapatec.Utils.removeEvent(el, "mouseover", C.dayMouseOver);
00245         Zapatec.Utils.removeEvent(el, "mousedown", C.dayMouseDown);
00246         Zapatec.Utils.removeEvent(el, "mouseout", C.dayMouseOut);
00247         if (Zapatec.is_ie)
00248                 Zapatec.Utils.removeEvent(el, "dblclick", C.dayMouseDblClick);
00249 };
00250 
00258 Zapatec.Calendar.findMonth = function(el) {
00259         if (typeof el.month != "undefined") {
00260                 return el;
00261         } else if (el.parentNode && typeof el.parentNode.month != "undefined") {
00262                 return el.parentNode;
00263         }
00264         return null;
00265 };
00266 
00268 Zapatec.Calendar.findHist = function(el) {
00269         if (typeof el.histDate != "undefined") {
00270                 return el;
00271         } else if (el.parentNode && typeof el.parentNode.histDate != "undefined") {
00272                 return el.parentNode;
00273         }
00274         return null;
00275 };
00276 
00278 Zapatec.Calendar.findYear = function(el) {
00279         if (typeof el.year != "undefined") {
00280                 return el;
00281         } else if (el.parentNode && typeof el.parentNode.year != "undefined") {
00282                 return el.parentNode;
00283         }
00284         return null;
00285 };
00286 
00292 Zapatec.Calendar.showMonthsCombo = function () {
00293         var cal = Zapatec.Calendar._C;
00294         if (!cal) {
00295                 return false;
00296         }
00297         var cd = cal.activeDiv;
00298         var mc = cal.monthsCombo;
00299         var date = cal.date,
00300                 MM = cal.date.getMonth(),
00301                 YY = cal.date.getFullYear(),
00302                 min = (YY == cal.minYear),
00303                 max = (YY == cal.maxYear);
00304         for (var i = mc.firstChild; i; i = i.nextSibling) {
00305                 var m = i.month;
00306                 Zapatec.Utils.removeClass(i, "hilite");
00307                 Zapatec.Utils.removeClass(i, "active");
00308                 Zapatec.Utils.removeClass(i, "disabled");
00309                 i.disabled = false;
00310                 if ((min && m < cal.minMonth) ||
00311                     (max && m > cal.maxMonth)) {
00312                         Zapatec.Utils.addClass(i, "disabled");
00313                         i.disabled = true;
00314                 }
00315                 if (m == MM)
00316                         Zapatec.Utils.addClass(cal.activeMonth = i, "active");
00317         }
00318         var s = mc.style;
00319         s.display = "block";
00320         if (cd.navtype < 0)
00321                 s.left = cd.offsetLeft + "px";
00322         else {
00323                 var mcw = mc.offsetWidth;
00324                 if (typeof mcw == "undefined")
00325                         // Konqueror brain-dead techniques
00326                         mcw = 50;
00327                 s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
00328         }
00329         s.top = (cd.offsetTop + cd.offsetHeight) + "px";
00330         cal.updateWCH(mc);
00331 };
00332 
00337 Zapatec.Calendar.showHistoryCombo = function() {
00338         var cal = Zapatec.Calendar._C, a, h, i, cd, hc, s, tmp, div;
00339         if (!cal)
00340                 return false;
00341         hc = cal.histCombo;
00342         while (hc.firstChild)
00343                 hc.removeChild(hc.lastChild);
00344         if (Zapatec.Calendar.prefs.history) {
00345                 a = Zapatec.Calendar.prefs.history.split(/,/);
00346                 i = 0;
00347                 while (tmp = a[i++]) {
00348                         tmp = tmp.split(/\//);
00349                         h = Zapatec.Utils.createElement("div");
00350                         h.className = Zapatec.is_ie ? "label-IEfix" : "label";
00351                         h.histDate = new Date(parseInt(tmp[0], 10), parseInt(tmp[1], 10)-1, parseInt(tmp[2], 10),
00352                                               tmp[3] ? parseInt(tmp[3], 10) : 0,
00353                                               tmp[4] ? parseInt(tmp[4], 10) : 0);
00354                         h.appendChild(window.document.createTextNode(h.histDate.print(cal.historyDateFormat)));
00355                         hc.appendChild(h);
00356                         if (h.histDate.dateEqualsTo(cal.date))
00357                                 Zapatec.Utils.addClass(h, "active");
00358                 }
00359         }
00360         cd = cal.activeDiv;
00361         s = hc.style;
00362         s.display = "block";
00363         s.left = Math.floor(cd.offsetLeft + (cd.offsetWidth-hc.offsetWidth)/2) + "px";
00364         s.top = (cd.offsetTop + cd.offsetHeight) + "px";
00365         cal.updateWCH(hc);
00366         cal.bEventShowHistory=true;     // Set state the we DID enter History event
00367 };
00368 
00376 Zapatec.Calendar.showYearsCombo = function (fwd) {
00377         var cal = Zapatec.Calendar._C;
00378         if (!cal) {
00379                 return false;
00380         }
00381         var cd = cal.activeDiv;
00382         var yc = cal.yearsCombo;
00383         if (cal.hilitedYear) {
00384                 Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
00385         }
00386         if (cal.activeYear) {
00387                 Zapatec.Utils.removeClass(cal.activeYear, "active");
00388         }
00389         cal.activeYear = null;
00390         var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
00391         var yr = yc.firstChild;
00392         var show = false;
00393         for (var i = 12; i > 0; --i) {
00394                 if (Y >= cal.minYear && Y <= cal.maxYear) {
00395                         yr.firstChild.data = Y;
00396                         yr.year = Y;
00397                         yr.style.display = "block";
00398                         show = true;
00399                 } else {
00400                         yr.style.display = "none";
00401                 }
00402                 yr = yr.nextSibling;
00403                 Y += fwd ? cal.yearStep : -cal.yearStep;
00404         }
00405         if (show) {
00406                 var s = yc.style;
00407                 s.display = "block";
00408                 if (cd.navtype < 0)
00409                         s.left = cd.offsetLeft + "px";
00410                 else {
00411                         var ycw = yc.offsetWidth;
00412                         if (typeof ycw == "undefined")
00413                                 // Konqueror brain-dead techniques
00414                                 ycw = 50;
00415                         s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
00416                 }
00417                 s.top = (cd.offsetTop + cd.offsetHeight) + "px";
00418         }
00419         cal.updateWCH(yc);
00420 };
00421 
00422 // event handlers
00423 
00447 Zapatec.Calendar.tableMouseUp = function(ev) {
00448         var cal = Zapatec.Calendar._C;
00449         if (!cal) {
00450                 return false;
00451         }
00452         if (cal.timeout) {
00453                 clearTimeout(cal.timeout);
00454         }
00455         var el = cal.activeDiv;
00456         if (!el) {
00457                 return false;
00458         }
00459         var target = Zapatec.Utils.getTargetElement(ev);
00460         if (typeof(el.navtype) == "undefined") {
00461                 while(!target.calendar) {
00462                         target = target.parentNode;
00463                 }       
00464         }
00465         ev || (ev = window.event);
00466         Zapatec.Utils.removeClass(el, "active");
00467         if (target == el || target.parentNode == el) {
00468                 Zapatec.Calendar.cellClick(el, ev);
00469         }
00470         var mon = Zapatec.Calendar.findMonth(target);
00471         var date = null;
00472         if (mon) {
00473                 if (!mon.disabled) {
00474                         date = new Date(cal.date);
00475                         if (mon.month != date.getMonth()) {
00476                                 date.setMonth(mon.month);
00477                                 cal.setDate(date, true);
00478                                 cal.dateClicked = false;
00479                                 cal.callHandler();
00480                         }
00481                 }
00482         } else {
00483                 var year = Zapatec.Calendar.findYear(target);
00484                 if (year) {
00485                         date = new Date(cal.date);
00486                         if (year.year != date.getFullYear()) {
00487                                 date.setFullYear(year.year);
00488                                 cal.setDate(date, true);
00489                                 cal.dateClicked = false;
00490                                 cal.callHandler();
00491                         }
00492                 } else {
00493                         var hist = Zapatec.Calendar.findHist(target);
00494                         if (hist && !hist.histDate.dateEqualsTo(cal.date)) {
00495                                 //(date = new Date(cal.date)).setDateOnly(hist.histDate);
00496                                 date = new Date(hist.histDate);
00497                                 cal._init(cal.firstDayOfWeek, cal.date = date);
00498                                 cal.dateClicked = false;
00499                                 cal.callHandler();
00500                                 cal.updateHistory();
00501                         }
00502                 }
00503         }
00504         Zapatec.Utils.removeEvent(window.document, "mouseup", Zapatec.Calendar.tableMouseUp);
00505         Zapatec.Utils.removeEvent(window.document, "mouseover", Zapatec.Calendar.tableMouseOver);
00506         Zapatec.Utils.removeEvent(window.document, "mousemove", Zapatec.Calendar.tableMouseOver);
00507         cal._hideCombos();
00508         Zapatec.Calendar._C = null;
00509         return Zapatec.Utils.stopEvent(ev);
00510 };
00511 
00527 Zapatec.Calendar.tableMouseOver = function (ev) {
00528         var cal = Zapatec.Calendar._C;
00529         if (!cal) {
00530                 return;
00531         }
00532         var el = cal.activeDiv;
00533         var target = Zapatec.Utils.getTargetElement(ev);
00534         if (target == el || target.parentNode == el) {
00535                 Zapatec.Utils.addClass(el, "hilite active");
00536                 Zapatec.Utils.addClass(el.parentNode, "rowhilite");
00537         } else {
00538                 if (typeof el.navtype == "undefined" ||
00539                     (el.navtype != 50 && ((el.navtype == 0 && !cal.histCombo) || Math.abs(el.navtype) > 2)))
00540                         Zapatec.Utils.removeClass(el, "active");
00541                 Zapatec.Utils.removeClass(el, "hilite");
00542                 Zapatec.Utils.removeClass(el.parentNode, "rowhilite");
00543         }
00544         ev || (ev = window.event);
00545         if (el.navtype == 50 && target != el) {
00546                 var pos = Zapatec.Utils.getAbsolutePos(el);
00547                 var w = el.offsetWidth;
00548                 var x = ev.clientX;
00549                 var dx;
00550                 var decrease = true;
00551                 if (x > pos.x + w) {
00552                         dx = x - pos.x - w;
00553                         decrease = false;
00554                 } else
00555                         dx = pos.x - x;
00556 
00557                 if (dx < 0) dx = 0;
00558                 var range = el._range;
00559                 var current = el._current;
00560                 var date = cal.date;
00561                 var pm = (date.getHours() >= 12);
00562                 var old = el.firstChild.data;  // old value of the element
00563                 var count = Math.floor(dx / 10) % range.length;
00564                 for (var i = range.length; --i >= 0;)
00565                         if (range[i] == current)
00566                                 break;
00567                 while (count-- > 0)
00568                         if (decrease) {
00569                                 if (--i < 0) {
00570                                         i = range.length - 1;
00571                                 }
00572                         } else if ( ++i >= range.length ) {
00573                                 i = 0;
00574                         }
00575 
00576                 //ALLOWED TIME CHECK
00577                 if (cal.getDateStatus) { 
00578                         //Current time is changing, check with the callback to see if it's in range of allowed times
00579                         // Fills the "minute" and "hour" variables with the time that user wants to set, to pass them to the dateStatusHandler for verification.
00580                         // As the script passes hours in 24 format, we need to convert input values if they are not in the needed format.
00581                         var minute = null; // minutes to be passed
00582                         var hour = null; // hours to be passed
00583                         var new_date = new Date(date); // as we pass date element to the handler, we need to create new one and fill it with new minutes or hours (depending on what had changed)
00584                         // if "ampm" was clicked
00585                         if (el.className.indexOf("ampm", 0) != -1) {
00586                            minute = date.getMinutes(); // minutes didn't change
00587                            // if the "ampm" value has changed we need to correct hours (add 12 or exclude 12 or set it to zero)
00588                            if (old != range[i]) {
00589                               hour = (range[i] == "pm") ? ((date.getHours() == 0) ? (12) : (date.getHours() + 12)) : (date.getHours() - 12);
00590                            } else {
00591                               hour = date.getHours();
00592                            }
00593                            // updates our new Date object that will be passed to the handler
00594                            new_date.setHours(hour);
00595                         }
00596                         // if hours were clicked
00597                         if (el.className.indexOf("hour", 0) != -1) {
00598                            minute = date.getMinutes(); // minutes didn't change
00599                            hour = (!cal.time24) ? ((pm) ? ((range[i] != 12) ? (parseInt(range[i], 10) + 12) : (12)) : ((range[i] != 12) ? (range[i]) : (0))) : (range[i]); // new value of hours
00600                            new_date.setHours(hour);
00601                         }
00602                         // if minutes were clicked
00603                         if (el.className.indexOf("minute", 0) != -1) {
00604                                 hour = date.getHours(); // hours didn't change
00605                                 minute = range[i]; // new value of minutes
00606                                 new_date.setMinutes(minute);
00607                         }
00608                 }
00609                 var status = false;
00610                 // if the handler is set, we pass new values and retrieve result in "status" variable
00611                 if (cal.getDateStatus) {
00612                    status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
00613                 }
00614                 // if time is enabled, we set new value
00615                 if (status == false) {
00616                    if ( !((!cal.time24) && (range[i] == "pm") && (hour > 23)) ) {
00617                       el.firstChild.data = range[i];
00618                    }
00619                 }
00620                 cal.onUpdateTime();
00621                 //END OF ALLOWED TIME CHECK
00622         }
00623         var mon = Zapatec.Calendar.findMonth(target);
00624         if (mon) {
00625                 if (!mon.disabled) {
00626                         if (mon.month != cal.date.getMonth()) {
00627                                 if (cal.hilitedMonth) {
00628                                         Zapatec.Utils.removeClass(cal.hilitedMonth, "hilite");
00629                                 }
00630                                 Zapatec.Utils.addClass(mon, "hilite");
00631                                 cal.hilitedMonth = mon;
00632                         } else if (cal.hilitedMonth) {
00633                                 Zapatec.Utils.removeClass(cal.hilitedMonth, "hilite");
00634                         }
00635                 }
00636         } else {
00637                 if (cal.hilitedMonth) {
00638                         Zapatec.Utils.removeClass(cal.hilitedMonth, "hilite");
00639                 }
00640                 var year = Zapatec.Calendar.findYear(target);
00641                 if (year) {
00642                         if (year.year != cal.date.getFullYear()) {
00643                                 if (cal.hilitedYear) {
00644                                         Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
00645                                 }
00646                                 Zapatec.Utils.addClass(year, "hilite");
00647                                 cal.hilitedYear = year;
00648                         } else if (cal.hilitedYear) {
00649                                 Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
00650                         }
00651                 } else {
00652                         if (cal.hilitedYear) {
00653                                 Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
00654                         }
00655                         var hist = Zapatec.Calendar.findHist(target);
00656                         if (hist) {
00657                                 if (!hist.histDate.dateEqualsTo(cal.date)) {
00658                                         if (cal.hilitedHist) {
00659                                                 Zapatec.Utils.removeClass(cal.hilitedHist, "hilite");
00660                                         }
00661                                         Zapatec.Utils.addClass(hist, "hilite");
00662                                         cal.hilitedHist = hist;
00663                                 } else if (cal.hilitedHist) {
00664                                         Zapatec.Utils.removeClass(cal.hilitedHist, "hilite");
00665                                 }
00666                         } else if (cal.hilitedHist) {
00667                                 Zapatec.Utils.removeClass(cal.hilitedHist, "hilite");
00668                         }
00669                 }
00670         }
00671         return Zapatec.Utils.stopEvent(ev);
00672 };
00673 
00682 Zapatec.Calendar.tableMouseDown = function (ev) {
00683         if (Zapatec.Utils.getTargetElement(ev) == Zapatec.Utils.getElement(ev)) {
00684                 return Zapatec.Utils.stopEvent(ev);
00685         }
00686 };
00687 
00702 Zapatec.Calendar.calDragIt = function (ev) {
00703         ev || (ev = window.event);
00704         var cal = Zapatec.Calendar._C;
00705         if (!(cal && cal.dragging)) {
00706                 return false;
00707         }
00708         var posX = ev.clientX + window.document.body.scrollLeft;
00709         var posY = ev.clientY + window.document.body.scrollTop;
00710         cal.hideShowCovered();
00711         var st = cal.element.style, L = posX - cal.xOffs, T = posY - cal.yOffs;
00712         st.left = L + "px";
00713         st.top = T + "px";
00714         Zapatec.Utils.setupWCH(cal.WCH, L, T);
00715         return Zapatec.Utils.stopEvent(ev);
00716 };
00717 
00729 Zapatec.Calendar.calDragEnd = function (ev) {
00730         var cal = Zapatec.Calendar._C;
00731         if (!cal) {
00732                 return false;
00733         }
00734         cal.dragging = false;
00735         Zapatec.Utils.removeEvent(window.document, "mousemove", Zapatec.Calendar.calDragIt);
00736         Zapatec.Utils.removeEvent(window.document, "mouseover", Zapatec.Calendar.calDragIt);
00737         Zapatec.Utils.removeEvent(window.document, "mouseup", Zapatec.Calendar.calDragEnd);
00738         Zapatec.Calendar.tableMouseUp(ev);
00739         cal.hideShowCovered();
00740 };
00742 
00759 Zapatec.Calendar.dayMouseDown = function(ev) {
00760         var canDrag = true;
00761         var el = Zapatec.Utils.getElement(ev);
00762         if (el.disabled) {
00763                 return false;
00764         }
00765         var cal = el.calendar;
00766         //BEGIN: fix for the extra information bug in IE
00767         while(!cal) {
00768                 el = el.parentNode;
00769                 cal = el.calendar;
00770         }       
00771         //END
00772         cal.bEventShowHistory=false;    // Set state the we DID NOT enter History event
00773         cal.activeDiv = el;
00774         Zapatec.Calendar._C = cal;
00775         if (el.navtype != 300) {
00776                 if (el.navtype == 50) {
00777                         //turns off changing the time by dragging if timeInterval is set
00778                         if (!((cal.timeInterval == null) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1)))) {canDrag = false;}
00779                         el._current = el.firstChild.data;
00780                         if (canDrag) {Zapatec.Utils.addEvent(window.document, "mousemove", Zapatec.Calendar.tableMouseOver);}
00781                 } else {
00782                         if (((el.navtype == 201) || (el.navtype == 202)) && (cal.timeInterval > 30) && (el.timePart.className.indexOf("minute", 0) != -1)) {canDrag = false;}
00783                         if (canDrag) {Zapatec.Utils.addEvent(window.document, Zapatec.is_ie5 ? "mousemove" : "mouseover", Zapatec.Calendar.tableMouseOver);}
00784                 }
00785                 if (canDrag) {Zapatec.Utils.addClass(el, "hilite active");}
00786                 Zapatec.Utils.addEvent(window.document, "mouseup", Zapatec.Calendar.tableMouseUp);
00787         } else if (cal.isPopup) {
00788                 cal._dragStart(ev);
00789         } else {
00790                 Zapatec.Calendar._C = null;
00791         }
00792         if (el.navtype == -1 || el.navtype == 1) {
00793                 if (cal.timeout) clearTimeout(cal.timeout);
00794                 cal.timeout = setTimeout("Zapatec.Calendar.showMonthsCombo()", 250);
00795         } else if (el.navtype == -2 || el.navtype == 2) {
00796                 if (cal.timeout) clearTimeout(cal.timeout);
00797                 cal.timeout = setTimeout((el.navtype > 0) ? "Zapatec.Calendar.showYearsCombo(true)" : "Zapatec.Calendar.showYearsCombo(false)", 250);
00798         } else if (el.navtype == 0 && Zapatec.Calendar.prefs.history) {
00799                 if (cal.timeout) clearTimeout(cal.timeout);
00800                 cal.timeout = setTimeout("Zapatec.Calendar.showHistoryCombo()", 250);
00801         } else {
00802                 cal.timeout = null;
00803         }
00804         return Zapatec.Utils.stopEvent(ev);
00805 };
00806 
00812 Zapatec.Calendar.dayMouseDblClick = function(ev) {
00813         Zapatec.Calendar.cellClick(Zapatec.Utils.getElement(ev), ev || window.event);
00814         if (Zapatec.is_ie)
00815                 window.document.selection.empty();
00816 };
00817 
00828 Zapatec.Calendar.dayMouseOver = function(ev) {
00829         var el = Zapatec.Utils.getElement(ev),
00830                 caldate = el.caldate;
00831         //BEGIN: fix for the extra information bug in IE
00832         while (!el.calendar) {
00833                 el = el.parentNode;
00834                 caldate = el.caldate;
00835         }
00836         //END
00837         var cal = el.calendar;
00838         var cel = el.timePart;
00839         if (caldate) {
00840                 caldate = new Date(caldate[0], caldate[1], caldate[2]);
00841                 if (caldate.getDate() != el.caldate[2]) caldate.setDate(el.caldate[2]);
00842         }
00843         if (Zapatec.Utils.isRelated(el, ev) || Zapatec.Calendar._C || el.disabled) {
00844                 return false;
00845         }
00846         if (el.ttip) {
00847                 if (el.ttip.substr(0, 1) == "_") {
00848                         el.ttip = caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
00849                 }
00850                 el.calendar.showHint(el.ttip);
00851         }
00852         if (el.navtype != 300) {
00853                 //turns off highliting of the time part which can not be changed by dragging
00854                 if (!((cal.timeInterval == null) || (el.className.indexOf("ampm", 0) != -1) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1))) && (el.navtype == 50)) {return Zapatec.Utils.stopEvent(ev);}
00855                 if (((el.navtype == 201) || (el.navtype == 202)) && (cal.timeInterval > 30) && (cel.className.indexOf("minute", 0) != -1)) {return Zapatec.Utils.stopEvent(ev);}
00856                 Zapatec.Utils.addClass(el, "hilite");
00857                 if (caldate) {
00858                         Zapatec.Utils.addClass(el.parentNode, "rowhilite");
00859                 }
00860         }
00861         return Zapatec.Utils.stopEvent(ev);
00862 };
00863 
00872 Zapatec.Calendar.dayMouseOut = function(ev) {
00873         var el = Zapatec.Utils.getElement(ev);
00874         //BEGIN: fix for the extra information bug in IE
00875         while (!el.calendar) {
00876                 el = el.parentNode;
00877                 caldate = el.caldate;
00878         }
00879         //END
00880         if (Zapatec.Utils.isRelated(el, ev) || Zapatec.Calendar._C || el.disabled)
00881                 return false;
00882         Zapatec.Utils.removeClass(el, "hilite");
00883         if (el.caldate)
00884                 Zapatec.Utils.removeClass(el.parentNode, "rowhilite");
00885         if (el.calendar)
00886                 el.calendar.showHint(Zapatec.Calendar.i18n("SEL_DATE"));
00887         return Zapatec.Utils.stopEvent(ev);
00888 };
00889 
00927 Zapatec.Calendar.cellClick = function(el, ev) {
00928         var cal = el.calendar;
00929         var closing = false;
00930         var newdate = false;
00931         var date = null;
00932         //BEGIN: fix for the extra information bug in IE        
00933         while(!cal) {
00934                 el = el.parentNode;
00935                 cal = el.calendar;
00936         }
00937         //END
00938         if (typeof el.navtype == "undefined") {
00939                 if (cal.currentDateEl) {
00940                         Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
00941                         Zapatec.Utils.addClass(el, "selected");
00942                         closing = (cal.currentDateEl == el);
00943                         if (!closing) {
00944                                 cal.currentDateEl = el;
00945                         }
00946                 }
00947                 var tmpDate = new Date(el.caldate[0], el.caldate[1], el.caldate[2]);
00948                 if (tmpDate.getDate() != el.caldate[2]) {
00949                         tmpDate.setDate(el.caldate[2]);
00950                 }
00951                 cal.date.setDateOnly(tmpDate);
00952                 cal.currentDate.setDateOnly(tmpDate);
00953                 date = cal.date;
00954                 var other_month = !(cal.dateClicked = !el.otherMonth);
00955                 if (!other_month && cal.multiple)
00956                         cal._toggleMultipleDate(new Date(date));
00957                 newdate = true;
00958                 // a date was clicked
00959                 if (other_month)
00960                         cal._init(cal.firstDayOfWeek, date);
00961                 cal.onSetTime();
00962         } else {
00963                 if (el.navtype == 200) {
00964                         Zapatec.Utils.removeClass(el, "hilite");
00965                         cal.callCloseHandler();
00966                         return;
00967                 }
00968                 date = new Date(cal.date);
00969                 if (el.navtype == 0 && !cal.bEventShowHistory)
00970                         // Set date to Today if Today clicked AND History NOT shown
00971                         date.setDateOnly(new Date()); // TODAY
00972                 // unless "today" was clicked, we assume no date was clicked so
00973                 // the selected handler will know not to close the calenar when
00974                 // in single-click mode.
00975                 // cal.dateClicked = (el.navtype == 0);
00976                 cal.dateClicked = false;
00977                 var year = date.getFullYear();
00978                 var mon = date.getMonth();
00979                 function setMonth(m) {
00980                         var day = date.getDate();
00981                         var max = date.getMonthDays(m);
00982                         if (day > max) {
00983                                 date.setDate(max);
00984                         }
00985                         date.setMonth(m);
00986                 };
00987                 switch (el.navtype) {
00988                     case 400:
00989                         Zapatec.Utils.removeClass(el, "hilite");
00990                         var text = Zapatec.Calendar.i18n("ABOUT");
00991                         if (typeof text != "undefined") {
00992                                 text += cal.showsTime ? Zapatec.Calendar.i18n("ABOUT_TIME") : "";
00993                         } else {
00994                                 // FIXME: this should be removed as soon as lang files get updated!
00995                                 text = "Help and about box text is not translated into this language.\n" +
00996                                         "If you know this language and you feel generous please update\n" +
00997                                         "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
00998                                         "and send it back to <support@zapatec.com> to get it into the distribution  ;-)\n\n" +
00999                                         "Thank you!\n" +
01000                                         "http://www.zapatec.com\n";
01001                         }
01002                         alert(text);
01003                         return;
01004                     case -2:
01005                         if (year > cal.minYear) {
01006                                 date.setFullYear(year - 1);
01007                         }
01008                         break;
01009                     case -1:
01010                         if (mon > 0) {
01011                                 setMonth(mon - 1);
01012                         } else if (year-- > cal.minYear) {
01013                                 date.setFullYear(year);
01014                                 setMonth(11);
01015                         }
01016                         break;
01017                     case 1:
01018                         if (mon < 11) {
01019                                 setMonth(mon + 1);
01020                         } else if (year < cal.maxYear) {
01021                                 date.setFullYear(year + 1);
01022                                 setMonth(0);
01023                         }
01024                         break;
01025                     case 2:
01026                         if (year < cal.maxYear) {
01027                                 date.setFullYear(year + 1);
01028                         }
01029                         break;
01030                     case 100:
01031                         cal.setFirstDayOfWeek(el.fdow);
01032                         Zapatec.Calendar.prefs.fdow = cal.firstDayOfWeek;
01033                         Zapatec.Calendar.savePrefs();
01034                         if (cal.onFDOW)
01035                                 cal.onFDOW(cal.firstDayOfWeek);
01036                         return;
01037                     case 50:
01038                         //turns off time changing if timeInterval is set with special value
01039                         var date = cal.currentDate;
01040                         if (el.className.indexOf("ampm", 0) >= 0)
01041                                 // always check ampm changes
01042                                 ;
01043                         else
01044                         if (!((cal.timeInterval == null) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1)))) {break;}
01045                         var range = el._range;
01046                         var current = el.firstChild.data;
01047                         var pm = (date.getHours() >= 12);
01048                         for (var i = range.length; --i >= 0;)
01049                                 if (range[i] == current)
01050                                         break;
01051                         if (ev && ev.shiftKey) {
01052                                 if (--i < 0) {
01053                                         i = range.length - 1;
01054                                 }
01055                         } else if ( ++i >= range.length ) {
01056                                         i = 0;
01057                                 }
01058 
01059                 //ALLOWED TIME CHECK
01060                         if (cal.getDateStatus) { //Current time is changing, check with the callback to see if it's in range
01061                                 // Fills "minute" and "hour" variables with the time that user wants to set, to pass them to the dateStatusHandler.
01062                                 // As the script passes hours in 24 format, we need to convert inputed values if they are not in the needed format                      
01063                                 var minute = null; // minutes to be passed
01064                                 var hour = null; // hours to be passed
01065                                 // as we pass date element to the handler, we need to create new one and fill it with new minutes or hours (depending on what had changed)
01066                                 var new_date = new Date(date);
01067                                 // if "ampm" was clicked
01068                                 if (el.className.indexOf("ampm", 0) != -1) {
01069                                         minute = date.getMinutes(); // minutes didn't change
01070                                         // if the "ampm" value has changed we need to correct hours (add 12 or exclude 12 or set it to zero)
01071                                         hour = (range[i] == "pm") ? ((date.getHours() == 12) ? (date.getHours()) : (date.getHours() + 12)) : (date.getHours() - 12);
01072                                         // if the time is disabled we seek the first one disabled.
01073                                         // It fixes the bug when you can not change from 'am' to 'pm' or vice versa for the dates that have restrictions for time.
01074                                         // This part of code is very easy to understand, so it don't need much comments
01075                                         if ( cal.getDateStatus && cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10)) ) {
01076                                            var dirrect;
01077                                            if (range[i] == "pm") {
01078                                               dirrect = -5;
01079                                            } else {
01080                                               dirrect = 5;
01081                                            }
01082                                            hours = hour;
01083                                            minutes = minute;
01084                                            do {
01085                                               minutes += dirrect;
01086                                               if (minutes >=60) {
01087                                                  minutes -= 60;
01088                                                  ++hours;
01089                                                  if (hours >= 24) hours -= 24;
01090                                                  new_date.setHours(hours);
01091                                               }
01092                                               if (minutes < 0) {
01093                                                  minutes += 60;
01094                                                  --hours;
01095                                                  if (hours < 0) hours += 24;
01096                                                  new_date.setHours(hours);
01097                                               }
01098                                               new_date.setMinutes(minutes);
01099                                               if (!cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hours, 10), parseInt(minutes, 10))) {
01100                                                  hour = hours;
01101                                                  minute = minutes;
01102                                                  if (hour > 12) i = 1; else i = 0;
01103                                                  cal.date.setHours(hour);
01104                                                  cal.date.setMinutes(minute);
01105                                                  cal.onSetTime();
01106                                               }
01107                                            } while ((hour != hours) || (minute != minutes));
01108                                         }
01109                                         // updates our new Date object that will be passed to the handler
01110                                         new_date.setHours(hour);
01111                                 }
01112                                 // if hours were clicked
01113                                 if (el.className.indexOf("hour", 0) != -1) {
01114                                    minute = date.getMinutes(); // minutes didn't change
01115                                    hour = (!cal.time24) ? ((pm) ? ((range[i] != 12) ? (parseInt(range[i], 10) + 12) : (12)) : ((range[i] != 12) ? (range[i]) : (0))) : (range[i]);  // new value of hours
01116                                    new_date.setHours(hour);
01117                                 }
01118                                 // if minutes were clicked
01119                                 if (el.className.indexOf("minute", 0) != -1) {
01120                                    hour = date.getHours(); // hours didn't change
01121                                    minute = range[i]; // new value of minutes
01122                                    new_date.setMinutes(minute);
01123                                 }
01124                         }
01125                         var status = false;
01126                         // if the handler is set, we pass new values and retreive result in "status" variable
01127                         if (cal.getDateStatus) {
01128                            status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
01129                         }
01130                         if (!status) {
01131                            el.firstChild.data = range[i];
01132                         }
01133                         //END OF ALLOWED TIME CHECK
01134 
01135                         cal.onUpdateTime();
01136                         return;
01137                     case 201: // timepart, UP
01138                     case 202: // timepart, DOWN
01139                         var cel = el.timePart;
01140                         //turns off time changing if timeInterval is set with special value
01141                         var date = cal.currentDate;
01142                         if ((cel.className.indexOf("minute", 0) != -1) && (cal.timeInterval > 30)) {break;}
01143                         var val = parseInt(cel.firstChild.data, 10);
01144                         var pm = (date.getHours() >= 12);
01145                         var range = cel._range;
01146                         for (var i = range.length; --i >= 0;)
01147                                 if (val == range[i]) {
01148                                         val = i;
01149                                         break;
01150                                 }
01151                         var step = cel._step;
01152                         if (el.navtype == 201) {
01153                                 val = step*Math.floor(val/step);
01154                                 val += step;
01155                                 if (val >= range.length)
01156                                         val = 0;
01157                         } else {
01158                                 val = step*Math.ceil(val/step);
01159                                 val -= step;
01160                                 if (val < 0)
01161                                         val = range.length-step;
01162                         }
01163 
01164                         //ALLOWED TIME CHECK
01165                         if (cal.getDateStatus) { //Current time is changing, check with the callback to see if it's in range of allowed times
01166                            // Fills "minute" and "hour" variables with the time that user wants to set, to pass them to the dateStatusHandler.
01167                            // As the script passes hours in 24 format, we need to convert inputed values if they are not in the needed format                   
01168                            var minute = null; // minutes to be passed
01169                            var hour = null; // hours to be passed
01170                            // as we pass date element to the handler, we need to create new one and fill it with new minutes or hours (depending on what had changed)
01171                            var new_date = new Date(date);
01172                            // if hours were changed
01173                            if (cel.className == "hour") {
01174                               minute = date.getMinutes();
01175                               hour = (!cal.time24) ? ((pm) ? ((range[val] != 12) ? (parseInt(range[val], 10) + 12) : (12)) : ((range[val] != 12) ? (range[val]) : (0))) : (range[val]);
01176                               new_date.setHours(hour);
01177                            }
01178                            // if minutes were changed
01179                            if (cel.className == "minute") {
01180                               hour = date.getHours();
01181                               minute = val;
01182                               new_date.setMinutes(range[val]);
01183                            }
01184                         }
01185                         var status = false;
01186                         // if the handler is set, we pass new values and retreive result in "status" variable
01187                         if (cal.getDateStatus) {
01188                            status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
01189                         }   
01190                         if (!status) {
01191                            cel.firstChild.data = range[val];
01192                         }
01193                         cal.onUpdateTime();
01194                         //END OF ALLOWED TIME CHECK
01195                         return;
01196                     case 0:
01197                         // TODAY will bring us here
01198                         //fix for the today bug for the special dates
01199                         // remember, "date" was previously set to new
01200                         // Date() if TODAY was clicked; thus, it
01201                         // contains today date.
01202                         if (cal.getDateStatus && ((cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate()) == true) || (cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate()) == "disabled"))) {
01203                                 return false;
01204                         }
01205                         break;
01206                 }
01207                 if (!date.equalsTo(cal.date)) {
01208                         if ((el.navtype >= -2 && el.navtype <=2) && (el.navtype != 0)) {
01209                                 cal._init(cal.firstDayOfWeek, date, true);
01210                                 return;
01211                         }
01212                         cal.setDate(date);
01213                         newdate = !(el.navtype && (el.navtype >= -2 && el.navtype <=2));
01214                 }
01215         }
01216         if (newdate) {
01217                 cal.callHandler();
01218         }
01219         if (closing) {
01220                 Zapatec.Utils.removeClass(el, "hilite");
01221                 cal.callCloseHandler();
01222         }
01223 };
01224 
01225 // END: CALENDAR STATIC FUNCTIONS
01226 
01227 // BEGIN: CALENDAR OBJECT FUNCTIONS
01228 
01256 Zapatec.Calendar.prototype.create = function (_par) {
01257         var parent = null;
01258         if (! _par) {
01259                 // default parent is the document body, in which case we create
01260                 // a popup calendar.
01261                 parent = window.document.getElementsByTagName("body")[0];
01262                 this.isPopup = true;
01263                 this.WCH = Zapatec.Utils.createWCH();
01264         } else {
01265                 parent = _par;
01266                 this.isPopup = false;
01267         }
01268         this.currentDate = this.date = this.dateStr ? new Date(this.dateStr) : new Date();
01269 
01270         var table = Zapatec.Utils.createElement("table");
01271         this.table = table;
01272         table.cellSpacing = 0;
01273         table.cellPadding = 0;
01274         table.calendar = this;
01275         Zapatec.Utils.addEvent(table, "mousedown", Zapatec.Calendar.tableMouseDown);
01276 
01277         var div = Zapatec.Utils.createElement("div");
01278         this.element = div;
01279         div.className = "calendar";
01280         //FIX for Opera's bug with row highlighting
01281         if (Zapatec.is_opera) {
01282                 table.style.width = (this.monthsInRow * ((this.weekNumbers) ? (8) : (7)) * 2 + 4.4 * this.monthsInRow) + "em";
01283         }
01284         if (this.isPopup) {
01285                 div.style.position = "absolute";
01286                 div.style.display = "none";
01287         }
01288         div.appendChild(table);
01289 
01290         var cell = null;
01291         var row = null;
01292 
01293         var cal = this;
01294         var hh = function (text, cs, navtype) {
01295                 cell = Zapatec.Utils.createElement("td", row);
01296                 cell.colSpan = cs;
01297                 cell.className = "button";
01298                 if (Math.abs(navtype) <= 2)
01299                         cell.className += " nav";
01300                 Zapatec.Calendar._add_evs(cell);
01301                 cell.calendar = cal;
01302                 cell.navtype = navtype;
01303                 if (text.substr(0, 1) != "&") {
01304                         cell.appendChild(document.createTextNode(text));
01305                 }
01306                 else {
01307                         // FIXME: dirty hack for entities
01308                         cell.innerHTML = text;
01309                 }
01310                 return cell;
01311         };
01312         //Creating all the controls on the top
01313         var title_length = ((this.weekNumbers) ? (8) : (7)) * this.monthsInRow - 2;
01314         var thead = Zapatec.Utils.createElement("thead", table);
01315         if (this.numberMonths == 1) {
01316                 this.title = thead;
01317         }
01318         row = Zapatec.Utils.createElement("tr", thead);
01319         if (this.helpButton) {
01320                 hh("?", 1, 400).ttip = Zapatec.Calendar.i18n("INFO");
01321         } else {
01322                 cell = Zapatec.Utils.createElement("td", row);
01323                 cell.colSpan = 1;
01324                 cell.className = "button";
01325                 cell.innerHTML = "<p>&nbsp</p>";
01326         }
01327         this.title = hh("", title_length, 300);
01328         this.title.className = "title";
01329         if (this.isPopup) {
01330                 this.title.ttip = Zapatec.Calendar.i18n("DRAG_TO_MOVE");
01331                 this.title.style.cursor = "move";
01332                 hh("&#x00d7;", 1, 200).ttip = Zapatec.Calendar.i18n("CLOSE");
01333         } else {
01334                 cell = Zapatec.Utils.createElement("td", row);
01335                 cell.colSpan = 1;
01336                 cell.className = "button";
01337                 cell.innerHTML = "<p>&nbsp</p>";
01338         }
01339         if (this.params && this.params.titleHtml)
01340                 this.title.innerHTML=this.params.titleHtml
01341 
01342         row = Zapatec.Utils.createElement("tr", thead);
01343         this._nav_py = hh("&#x00ab;", 1, -2);
01344         this._nav_py.ttip = Zapatec.Calendar.i18n("PREV_YEAR");
01345         this._nav_pm = hh("&#x2039;", 1, -1);
01346         this._nav_pm.ttip = Zapatec.Calendar.i18n("PREV_MONTH");
01347         this._nav_now = hh(Zapatec.Calendar.i18n("TODAY"), title_length - 2, 0);
01348         this._nav_now.ttip = Zapatec.Calendar.i18n("GO_TODAY");
01349         this._nav_nm = hh("&#x203a;", 1, 1);
01350         this._nav_nm.ttip = Zapatec.Calendar.i18n("NEXT_MONTH");
01351         this._nav_ny = hh("&#x00bb;", 1, 2);
01352         this._nav_ny.ttip = Zapatec.Calendar.i18n("NEXT_YEAR");
01353 
01354         //Here we calculate the number of rows for multimonth calendar
01355         var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
01356         if (this.numberMonths % this.monthsInRow > 0) {
01357                 ++rowsOfMonths;
01358         }
01359         //Every iteration of this cycle creates a row of months in the calendar
01360         for (var l = 1; l <= rowsOfMonths; ++l) {
01361                 var thead = Zapatec.Utils.createElement("thead", table);
01362                 //Fix for the Operas bug, this is a workaround which makes Opera display THEAD elements as TBODY el.
01363                 //The problem is that Opera displays all the THEAD elements in the table first, and only then TBODY elements (an ugly look!).
01364                 if (Zapatec.is_opera) {thead.style.display = "table-row-group";}
01365                 if (this.numberMonths != 1) {
01366                         row = Zapatec.Utils.createElement("tr", thead);
01367                         var title_length = 5;
01368                         this.weekNumbers && ++title_length;
01369                         //creating the titles for the months
01370                         this.titles[l] = new Array();
01371                         for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
01372                                 cell = Zapatec.Utils.createElement("td", row);
01373                                 cell.colSpan = 1;
01374                                 cell.className = "button";
01375                                 cell.innerHTML = "<p>&nbsp</p>";
01376                                 this.titles[l][k] = hh("", title_length, 300);
01377                                 this.titles[l][k].className = "title";
01378                                 cell = Zapatec.Utils.createElement("td", row);
01379                                 cell.colSpan = 1;
01380                                 cell.className = "button";
01381                                 cell.innerHTML = "<p>&nbsp</p>";
01382                         }
01383                 }
01384         // day names
01385                 row = Zapatec.Utils.createElement("tr", thead);
01386                 row.className = "daynames";
01387                 for (k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
01388                         if (this.weekNumbers) {
01389                                 cell = Zapatec.Utils.createElement("td", row);
01390                                 cell.className = "name wn";
01391                                 cell.appendChild(window.document.createTextNode(Zapatec.Calendar.i18n("WK")));
01392                                 if (k > 1) {
01393                                         Zapatec.Utils.addClass(cell, "month-left-border");
01394                                 }
01395                                 var cal_wk = Zapatec.Calendar.i18n("WK")
01396                                         if (cal_wk == null) {
01397                                                 //if it's not defined in the language file, leave it blank
01398                                                 cal_wk = "";
01399                                         }
01400                 
01401                         }
01402                         for (var i = 7; i > 0; --i) {
01403                                 cell = Zapatec.Utils.createElement("td", row);
01404                                 cell.appendChild(window.document.createTextNode(""));
01405                         }
01406                 }
01407                 this.firstdayname = row.childNodes[this.weekNumbers?1:0];
01408                 this.rowsOfDayNames[l] = this.firstdayname; 
01409                 this._displayWeekdays();
01410 
01411                 var tbody = Zapatec.Utils.createElement("tbody", table);
01412                 this.tbody[l] = tbody;
01413                 
01414                 for (i = 6; i > 0; --i) {
01415                         //creating a row of days for all the months in the row
01416                         row = Zapatec.Utils.createElement("tr", tbody);
01417                         for (k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
01418                                 if (this.weekNumbers) {
01419                                         cell = Zapatec.Utils.createElement("td", row);
01420                                         cell.appendChild(document.createTextNode(""));
01421                                 }
01422                                 for (var j = 7; j > 0; --j) {
01423                                         cell = Zapatec.Utils.createElement("td", row);
01424                                         cell.appendChild(document.createTextNode(""));
01425                                         cell.calendar = this;
01426                                         Zapatec.Calendar._add_evs(cell);
01427                                 }
01428                         }
01429                 }
01430         }
01431 
01432         var tfoot = Zapatec.Utils.createElement("tfoot", table);
01433 
01434         if (this.showsTime) {
01435                 row = Zapatec.Utils.createElement("tr", tfoot);
01436                 row.className = "time";
01437                 //empty area for positioning the time controls under the control month
01438                 var emptyColspan;
01439                 if (this.monthsInRow != 1) {
01440                         cell = Zapatec.Utils.createElement("td", row);
01441                         emptyColspan = cell.colSpan = Math.ceil((((this.weekNumbers) ? 8 : 7) * (this.monthsInRow - 1)) / 2);
01442                         cell.className = "timetext";
01443                         cell.innerHTML = "&nbsp";
01444                 }                                               
01445 
01446                 cell = Zapatec.Utils.createElement("td", row);
01447                 cell.className = "timetext";
01448                 cell.colSpan = this.weekNumbers ? 2 : 1;
01449                 cell.innerHTML = Zapatec.Calendar.i18n("TIME") || "&nbsp;";
01450 
01451                 (function() {
01452                         function makeTimePart(className, init, range_start, range_end) {
01453                                 var table, tbody, tr, tr2, part;
01454                                 if (range_end) {
01455                                         cell = Zapatec.Utils.createElement("td", row);
01456                                         cell.colSpan = 1;
01457                                         if (cal.showsTime != "seconds") {
01458                                                 ++cell.colSpan;
01459                                         }
01460                                         cell.className = "parent-" + className;
01461                                         table = Zapatec.Utils.createElement("table", cell);
01462                                         table.cellSpacing = table.cellPadding = 0;
01463                                         if (className == "hour")
01464                                                 table.align = "right";
01465                                         table.className = "calendar-time-scroller";
01466                                         tbody = Zapatec.Utils.createElement("tbody", table);
01467                                         tr    = Zapatec.Utils.createElement("tr", tbody);
01468                                         tr2   = Zapatec.Utils.createElement("tr", tbody);
01469                                 } else
01470                                         tr = row;
01471                                 part = Zapatec.Utils.createElement("td", tr);
01472                                 part.className = className;
01473                                 part.appendChild(window.document.createTextNode(init));
01474                                 part.calendar = cal;
01475                                 part.ttip = Zapatec.Calendar.i18n("TIME_PART");
01476                                 part.navtype = 50;
01477                                 part._range = [];
01478                                 if (!range_end)
01479                                         part._range = range_start;
01480                                 else {
01481                                         part.rowSpan = 2;
01482                                         for (var i = range_start; i <= range_end; ++i) {
01483                                                 var txt;
01484                                                 if (i < 10 && range_end >= 10) txt = '0' + i;
01485                                                 else txt = '' + i;
01486                                                 part._range[part._range.length] = txt;
01487                                         }
01488                                         var up = Zapatec.Utils.createElement("td", tr);
01489                                         up.className = "up";
01490                                         up.navtype = 201;
01491                                         up.calendar = cal;
01492                                         up.timePart = part;
01493                                         if (Zapatec.is_khtml)
01494                                                 up.innerHTML = "&nbsp;";
01495                                         Zapatec.Calendar._add_evs(up);
01496 
01497                                         var down = Zapatec.Utils.createElement("td", tr2);
01498                                         down.className = "down";
01499                                         down.navtype = 202;
01500                                         down.calendar = cal;
01501                                         down.timePart = part;
01502                                         if (Zapatec.is_khtml)
01503                                                 down.innerHTML = "&nbsp;";
01504                                         Zapatec.Calendar._add_evs(down);
01505                                 }
01506                                 Zapatec.Calendar._add_evs(part);
01507                                 return part;
01508                         };
01509                         var hrs = cal.currentDate.getHours();
01510                         var mins = cal.currentDate.getMinutes();
01511                         if (cal.showsTime == "seconds") {
01512                                 var secs = cal.currentDate.getSeconds();
01513                         }
01514                         var t12 = !cal.time24;
01515                         var pm = (hrs > 12);
01516                         if (t12 && pm) hrs -= 12;
01517                         var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
01518                         //calculating of the step for hours
01519                         H._step = (cal.timeInterval > 30) ? (cal.timeInterval / 60) : 1;
01520                         cell = Zapatec.Utils.createElement("td", row);
01521                         cell.innerHTML = ":";
01522                         cell.className = "colon";
01523                         var M = makeTimePart("minute", mins, 0, 59);
01524                         //calculating of the step for minutes
01525                         M._step = ((cal.timeInterval) && (cal.timeInterval < 60)) ? (cal.timeInterval) : 5; // FIXME: make this part configurable
01526                         if (cal.showsTime == "seconds") {
01527                                 cell = Zapatec.Utils.createElement("td", row);
01528                                 cell.innerHTML = ":";
01529                                 cell.className = "colon";
01530                                 var S = makeTimePart("minute", secs, 0, 59);
01531                                 S._step = 5;
01532                         }
01533                         var AP = null;
01534                         if (t12) {
01535                                 AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
01536                                 AP.className += " button";
01537                         } else
01538                                 Zapatec.Utils.createElement("td", row).innerHTML = "&nbsp;";
01539 
01540                         cal.onSetTime = function() {
01541                                 var hrs = this.currentDate.getHours();
01542                                 var mins = this.currentDate.getMinutes();
01543                                 if (this.showsTime == "seconds") {
01544                                         var secs = cal.currentDate.getSeconds();
01545                                 }
01546                                 if (this.timeInterval) {
01547                                         mins += this.timeInterval - ((mins - 1 + this.timeInterval) % this.timeInterval) - 1;
01548                                 }
01549                                 while (mins >= 60) {
01550                                         mins -= 60;
01551                                         ++hrs;
01552                                 }
01553                                 if (this.timeInterval > 60) {
01554                                         var interval = this.timeInterval / 60;
01555                                         if (hrs % interval != 0) {
01556                                                 hrs += interval - ((hrs - 1 + interval) % interval) - 1;
01557                                         }
01558                                         if (hrs >= 24) {hrs -= 24;}
01559                                 }
01560                         //ALLOWED TIME CHECK
01561                                 // This part of code seeks for the first enabled time value for this date. 
01562                                 // It is written for the cases when you change day, month or year and the time value is disabled for the new date.
01563                                 // So if you only allow 8:00 - 17:00 on Mondays and you change the date to a Monday but the time is 7:00 it will
01564                                 // automatically move forward to 8:00.
01565                                 var new_date = new Date(this.currentDate);
01566                                 if (this.getDateStatus && this.getDateStatus(this.currentDate, this.currentDate.getFullYear(), this.currentDate.getMonth(), this.currentDate.getDate(), hrs, mins)) {
01567                                    hours = hrs;
01568                                    minutes = mins;
01569                                    do {
01570                                      if (this.timeInterval) {
01571                                                 if (this.timeInterval < 60) {
01572                                                         minutes += this.timeInterval;
01573                                                 } else {
01574                                                         hrs += this.timeInterval / 60;
01575                                                 }
01576                                          } else {
01577                                                 minutes += 5;
01578                                          }
01579                                      if (minutes >=60) {
01580                                                 minutes -= 60;
01581                                                 hours += 1;
01582                                      }
01583                                      if (hours >= 24) {hours -= 24;}
01584                                          new_date.setMinutes(minutes);
01585                                      new_date.setHours(hours);
01586                                      if (!this.getDateStatus(new_date, this.currentDate.getFullYear(), this.currentDate.getMonth(), this.currentDate.getDate(), hours, minutes)) {
01587                                                 hrs = hours;
01588                                                 mins = minutes;
01589                                      }
01590                                    } while ((hrs != hours) || (mins != minutes));
01591                                 }
01592                         //END OF ALLOWED TIME CHECK
01593                                 this.currentDate.setMinutes(mins);
01594                                 this.currentDate.setHours(hrs);
01595                                 var pm = (hrs >= 12);
01596                                 if (pm && t12 && hrs != 12) hrs -= 12;
01597                                 if (!pm && t12 && hrs == 0) hrs = 12;
01598                                 H.firstChild.data = (hrs < 10) ? ("0" + hrs) : hrs;
01599                                 M.firstChild.data = (mins < 10) ? ("0" + mins) : mins;
01600                                 if (this.showsTime == "seconds") {
01601                                         S.firstChild.data = (secs < 10) ? ("0" + secs) : secs;
01602                                 }
01603                                 if (t12)
01604                                    AP.firstChild.data = pm ? "pm" : "am";
01605                         };
01606 
01607                         cal.onUpdateTime = function() {
01608                                 var date = this.currentDate;
01609                                 var h = parseInt(H.firstChild.data, 10);
01610                                 if (t12) {
01611                                         if (/pm/i.test(AP.firstChild.data) && h < 12)
01612                                                 h += 12;
01613                                         else if (/am/i.test(AP.firstChild.data) && h == 12)
01614                                                 h = 0;
01615                                 }
01616                                 var d = date.getDate();
01617                                 var m = date.getMonth();
01618                                 var y = date.getFullYear();
01619                                 date.setHours(h);
01620                                 date.setMinutes(parseInt(M.firstChild.data, 10));
01621                                 if (this.showsTime == "seconds") {
01622                                         date.setSeconds(parseInt(S.firstChild.data, 10));
01623                                 }
01624                                 date.setFullYear(y);
01625                                 date.setMonth(m);
01626                                 date.setDate(d);
01627                                 this.dateClicked = false;
01628                                 this.callHandler();
01629                         };
01630                 })();
01631                 //empty area after the time controls
01632                 if (this.monthsInRow != 1) {
01633                         cell = Zapatec.Utils.createElement("td", row);
01634                         cell.colSpan = ((this.weekNumbers) ? 8 : 7) * (this.monthsInRow - 1) - Math.ceil(emptyColspan);
01635                         cell.className = "timetext";
01636                         cell.innerHTML = "&nbsp";
01637                 }                                               
01638         } else {
01639                 this.onSetTime = this.onUpdateTime = function() {};
01640         }
01641 
01642         row = Zapatec.Utils.createElement("tr", tfoot);
01643         row.className = "footrow";
01644 
01645         cell = hh(Zapatec.Calendar.i18n("SEL_DATE"), this.weekNumbers ? (8 * this.numberMonths) : (7 * this.numberMonths), 300);
01646         cell.className = "ttip";
01647         if (this.isPopup) {
01648                 cell.ttip = Zapatec.Calendar.i18n("DRAG_TO_MOVE");
01649                 cell.style.cursor = "move";
01650         }
01651         this.tooltips = cell;
01652 
01653         div = this.monthsCombo = Zapatec.Utils.createElement("div", this.element);
01654         div.className = "combo";
01655         for (i = 0; i < 12; ++i) {
01656                 var mn = Zapatec.Utils.createElement("div");
01657                 mn.className = Zapatec.is_ie ? "label-IEfix" : "label";
01658                 mn.month = i;
01659                 mn.appendChild(window.document.createTextNode(Zapatec.Calendar.i18n(i, "smn")));
01660                 div.appendChild(mn);
01661         }
01662 
01663         div = this.yearsCombo = Zapatec.Utils.createElement("div", this.element);
01664         div.className = "combo";
01665         for (i = 12; i > 0; --i) {
01666                 var yr = Zapatec.Utils.createElement("div");
01667                 yr.className = Zapatec.is_ie ? "label-IEfix" : "label";
01668                 yr.appendChild(window.document.createTextNode(""));
01669                 div.appendChild(yr);
01670         }
01671 
01672         div = this.histCombo = Zapatec.Utils.createElement("div", this.element);
01673         div.className = "combo history";
01674 
01675         this._init(this.firstDayOfWeek, this.date);
01676         parent.appendChild(this.element);
01677 };
01678 
01687 Zapatec.Calendar._keyEvent = function(ev) {
01688         if (!window.calendar) {
01689                 return false;
01690         }
01691         (Zapatec.is_ie) && (ev = window.event);
01692         var cal = window.calendar;
01693         var act = (Zapatec.is_ie || ev.type == "keypress");
01694         var K = ev.keyCode;
01695         var date  = new Date(cal.date);
01696         if (ev.ctrlKey) {
01697                 switch (K) {
01698                     case 37: // KEY left
01699                         act && Zapatec.Calendar.cellClick(cal._nav_pm);
01700                         break;
01701                     case 38: // KEY up
01702                         act && Zapatec.Calendar.cellClick(cal._nav_py);
01703                         break;
01704                     case 39: // KEY right
01705                         act && Zapatec.Calendar.cellClick(cal._nav_nm);
01706                         break;
01707                     case 40: // KEY down
01708                         act && Zapatec.Calendar.cellClick(cal._nav_ny);
01709                         break;
01710                     default:
01711                         return false;
01712                 }
01713         } else switch (K) {
01714             case 32: // KEY space (now)
01715                 Zapatec.Calendar.cellClick(cal._nav_now);
01716                 break;
01717             case 27: // KEY esc
01718                 act && cal.callCloseHandler();
01719                 break;
01720             //Fix for the key navigation
01721                 case 37: // KEY left
01722                         if (act && !cal.multiple) {
01723                                 date.setTime(date.getTime() - 86400000);
01724                                 cal.setDate(date);
01725                         }
01726                         break;
01727             case 38: // KEY up
01728                         if (act && !cal.multiple) {
01729                                 date.setTime(date.getTime() - 7 * 86400000);
01730                                 cal.setDate(date);
01731                         }
01732                         break;
01733             case 39: // KEY right
01734                         if (act && !cal.multiple) {
01735                                 date.setTime(date.getTime() + 86400000);
01736                                 cal.setDate(date);
01737                         }
01738                         break;
01739             case 40: // KEY down
01740                         if (act && !cal.multiple) {
01741                                 date.setTime(date.getTime() + 7 * 86400000);
01742                                 cal.setDate(date);
01743                         }
01744                         break;
01745             case 13: // KEY enter
01746                 if (act) {
01747                         //FIX for Enter key!
01748                         Zapatec.Calendar.cellClick(cal.currentDateEl);
01749                 }
01750                 break;
01751             default:
01752                 return false;
01753         }
01754         return Zapatec.Utils.stopEvent(ev);
01755 };
01756 
01782 Zapatec.Calendar.prototype._init = function (firstDayOfWeek, date, last) {
01783         var
01784                 today = new Date(),
01785                 TD = today.getDate(),
01786                 TY = today.getFullYear(),
01787                 TM = today.getMonth();
01788         //this.table.style.visibility = "hidden";
01789         if (this.getDateStatus && !last) {
01790                 var status = this.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate());
01791                 var backupDate = new Date(date);
01792                 while (((status == true) || (status == "disabled")) && (backupDate.getMonth() == date.getMonth())) {
01793                         date.setTime(date.getTime() + 86400000);
01794                         var status = this.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate());
01795                 }
01796                 if (backupDate.getMonth() != date.getMonth()) {
01797                         date = new Date(backupDate);
01798                         while (((status == true) || (status == "disabled")) && (backupDate.getMonth() == date.getMonth())) {
01799                                 date.setTime(date.getTime() - 86400000);
01800                                 var status = this.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate());
01801                         }
01802                 }
01803                 if (backupDate.getMonth() != date.getMonth()) {
01804                         last = true;
01805                         date = new Date(backupDate);
01806                 }
01807         }
01808         var year = date.getFullYear();
01809         var month = date.getMonth();
01810         var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
01811         var minMonth;
01812         var diffMonth, last_row, before_control;
01813         if (!this.vertical) {
01814                 diffMonth = (this.controlMonth - 1);
01815                 minMonth = month - diffMonth;
01816         } else {
01817                 last_row = ((this.numberMonths - 1) % this.monthsInRow) + 1;
01818                 before_control = (this.controlMonth - 1) % this.monthsInRow;
01819                 bottom = (before_control >= (last_row) ? (last_row) : (before_control));
01820                 diffMonth = (before_control) * (rowsOfMonths - 1) + Math.floor((this.controlMonth - 1) / this.monthsInRow) + bottom;
01821                 minMonth = month - diffMonth;
01822         }
01823         var minYear = year;
01824         if (minMonth < 0) {
01825                 minMonth += 12;
01826                 --minYear;
01827         }
01828         var maxMonth = minMonth + this.numberMonths - 1;
01829         var maxYear = minYear;
01830         if (maxMonth > 11) {
01831                 maxMonth -= 12;
01832                 ++maxYear;
01833         }
01834         function disableControl(ctrl) {
01835                 Zapatec.Calendar._del_evs(ctrl);
01836                 ctrl.disabled = true;
01837                 ctrl.className = "button";
01838                 ctrl.innerHTML = "<p>&nbsp</p>";
01839         }
01840         function enableControl(ctrl, sign) {
01841                 Zapatec.Calendar._add_evs(ctrl);
01842                 ctrl.disabled = false;
01843                 ctrl.className = "button nav";
01844                 ctrl.innerHTML = sign;
01845         }
01846         if (minYear <= this.minYear) {
01847                 if (!this._nav_py.disabled) {
01848                         disableControl(this._nav_py);
01849                 }
01850         } else {
01851                 if (this._nav_py.disabled) {    
01852                         enableControl(this._nav_py, "&#x00ab;");
01853                 }
01854         }
01855 
01856         if (maxYear >= this.maxYear) {
01857                 if (!this._nav_ny.disabled) {
01858                         disableControl(this._nav_ny);
01859                 }
01860         } else {
01861                 if (this._nav_ny.disabled) {
01862                         enableControl(this._nav_ny, "&#x00bb;");
01863                 }
01864         }
01865 
01866         if (((minYear == this.minYear) && (minMonth <= this.minMonth)) || (minYear < this.minYear)) {
01867                 if (!this._nav_pm.disabled) {
01868                         disableControl(this._nav_pm);
01869                 }
01870         } else {
01871                 if (this._nav_pm.disabled) {    
01872                         enableControl(this._nav_pm, "&#x2039;");
01873                 }
01874         }
01875         if (((maxYear == this.maxYear) && (maxMonth >= this.maxMonth)) || (maxYear > this.maxYear)) {
01876                 if (!this._nav_nm.disabled) {
01877                         disableControl(this._nav_nm);
01878                 }
01879         } else {
01880                 if (this._nav_nm.disabled) {    
01881                         enableControl(this._nav_nm, "&#x203a;");
01882                 }
01883         }
01884         
01885         //FIX for range checking : spreading of the range on 1 month on the both sides;
01886         upperMonth = this.maxMonth + 1;
01887         upperYear = this.maxYear;
01888         if (upperMonth > 11) {
01889                 upperMonth -= 12;
01890                 ++upperYear;
01891         }
01892         bottomMonth = this.minMonth - 1;
01893         bottomYear = this.minYear;
01894         if (bottomMonth < 0) {
01895                 bottomMonth += 12;
01896                 --bottomYear;
01897         }
01898         maxDate1 = new Date(maxYear, maxMonth, date.getMonthDays(maxMonth), 23, 59, 59, 999);
01899         maxDate2 = new Date(upperYear, upperMonth, 1, 0, 0, 0, 0);
01900         minDate1 = new Date(minYear, minMonth, 1, 0, 0, 0, 0);
01901         minDate2 = new Date(bottomYear, bottomMonth, date.getMonthDays(bottomMonth), 23, 59, 59, 999);
01902         if (maxDate1.getTime() > maxDate2.getTime()) {
01903                 date.setTime(date.getTime() - (maxDate1.getTime() - maxDate2.getTime()));
01904         }
01905         if (minDate1.getTime() < minDate2.getTime()) {
01906                 date.setTime(date.getTime() + (minDate2.getTime() - minDate1.getTime()));
01907         }
01908         delete maxDate1; delete maxDate2; delete minDate1; delete minDate2;
01909         this.firstDayOfWeek = firstDayOfWeek;
01910         if (!last) {
01911                 this.currentDate = date;
01912         }
01913         this.date = date;
01914         (this.date = new Date(this.date)).setDateOnly(date);
01915         year = this.date.getFullYear();
01916         month = this.date.getMonth();
01917         var initMonth = date.getMonth();
01918         var mday = this.date.getDate();
01919         var no_days = date.getMonthDays();
01920 
01921         // calendar voodoo for computing the first day that would actually be
01922         // displayed in the calendar, even if it's from the previous month.
01923         // WARNING: this is magic. ;-)
01924         var months = new Array();
01925         if (this.numberMonths % this.monthsInRow > 0) {
01926                 ++rowsOfMonths;
01927         }
01928         //creating of the array of date objects for every month
01929         for (var l = 1; l <= rowsOfMonths; ++l) {
01930                 months[l] = new Array();
01931                 for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
01932                         var tmpDate = new Date(date);
01933                         if (this.vertical) {
01934                                 var validMonth = date.getMonth() - diffMonth + ((k - 1) * (rowsOfMonths - 1) + (l - 1) + ((last_row < k) ? (last_row) : (k - 1)));
01935                         } else {
01936                                 var validMonth = date.getMonth() - diffMonth + (l - 1) * this.monthsInRow + k - 1;
01937                         }
01938                         if (validMonth < 0) {
01939                                 tmpDate.setFullYear(tmpDate.getFullYear() - 1);
01940                                 validMonth = 12 + validMonth;
01941                         }
01942                         if (validMonth > 11) {
01943                                 tmpDate.setFullYear(tmpDate.getFullYear() + 1);
01944                                 validMonth = validMonth - 12;
01945                         }
01946                         tmpDate.setDate(1);
01947                         tmpDate.setMonth(validMonth);
01948                         var day1 = (tmpDate.getDay() - this.firstDayOfWeek) % 7;
01949                         if (day1 < 0)
01950                                 day1 += 7;
01951                         tmpDate.setDate(-day1);
01952                         tmpDate.setDate(tmpDate.getDate() + 1);
01953                         months[l][k] = tmpDate;
01954                 }
01955         }
01956 
01957         var MN = Zapatec.Calendar.i18n(month, "smn");
01958         var weekend = Zapatec.Calendar.i18n("WEEKEND");
01959         var dates = this.multiple ? (this.datesCells = {}) : null;
01960         var DATETXT = this.getDateText;
01961         //every iteration of the cycle fills one row of months with values;
01962         for (var l = 1; l <= rowsOfMonths; ++l) {
01963                 var row = this.tbody[l].firstChild;
01964                 for (var i = 7; --i > 0; row = row.nextSibling) {
01965                         var cell = row.firstChild;
01966                         var hasdays = false;
01967                         //this fills one row of days for all the months in the row
01968                         for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
01969                                 date = months[l][k];
01970                                 if (this.weekNumbers) {
01971                                         cell.className = " day wn";
01972                                         cell.innerHTML = date.getWeekNumber();
01973                                         if (k > 1) {
01974                                                 Zapatec.Utils.addClass(cell, "month-left-border");
01975                                         }
01976                                         cell = cell.nextSibling;
01977                                 }
01978                                 row.className = "daysrow";
01979                                 var iday;
01980                                 for (j = 7; cell && (iday = date.getDate()) && (j > 0); date.setDate(iday+1), ((date.getDate() == iday) ? (date.setHours(1) && date.setDate(iday + 1)) : (false)), cell = cell.nextSibling, --j) {
01981                                         var
01982                                                 wday = date.getDay(),
01983                                                 dmonth = date.getMonth(),
01984                                                 dyear = date.getFullYear();
01985                                         cell.className = " day";
01986                                         if ((!this.weekNumbers) && (j == 7) && (k != 1)) {
01987                                                 Zapatec.Utils.addClass(cell, "month-left-border");
01988                                         }
01989                                         if ((j == 1) && (k != this.monthsInRow)) {
01990                                                 Zapatec.Utils.addClass(cell, "month-right-border");
01991                                         }
01992                                         if (this.vertical) {
01993                                                 validMonth = initMonth - diffMonth + ((k - 1) * (rowsOfMonths - 1) + (l - 1) + ((last_row < k) ? (last_row) : (k - 1)));
01994                                         } else {
01995                                                 validMonth = initMonth - diffMonth + ((l - 1) * this.monthsInRow + k - 1);
01996                                         }
01997                                         if (validMonth < 0) {
01998                                                 validMonth = 12 + validMonth;
01999                                         }
02000                                         if (validMonth > 11) {
02001                                                 validMonth = validMonth - 12;
02002                                         }
02003                                         var current_month = !(cell.otherMonth = !(dmonth == validMonth));
02004                                         if (!current_month) {
02005                                                 if (this.showsOtherMonths)
02006                                                         cell.className += " othermonth";
02007                                                 else {
02008                                                         //cell.className = "emptycell";
02009                                                         cell.innerHTML = "<p>&nbsp;</p>";
02010                                                         cell.disabled = true;
02011                                                         continue;
02012                                                 }
02013                                         } else
02014                                                 hasdays = true;
02015                                         cell.disabled = false;
02016                                         cell.innerHTML = DATETXT ? DATETXT(date, dyear, dmonth, iday) : iday;
02017                                         dates && (dates[date.print("%Y%m%d")] = cell);
02018                                         if (this.getDateStatus) {
02019                                                 var status = this.getDateStatus(date, dyear, dmonth, iday);
02020                                                 if (this.getDateToolTip) {
02021                                                         var toolTip = this.getDateToolTip(date, dyear, dmonth, iday);
02022                                                         if (toolTip)
02023                                                                 cell.title = toolTip;
02024                                                 }
02025                                                 if (status == true) {
02026                                                         cell.className += " disabled";
02027                                                         cell.disabled = true;
02028                                                 } else {
02029                                                         if (/disabled/i.test(status))
02030                                                                 cell.disabled = true;
02031                                                         cell.className += " " + status;
02032                                                 }
02033                                         }
02034                                         if (!cell.disabled) {
02035                                                 cell.caldate = [dyear, dmonth, iday];
02036                                                 cell.ttip = "_";
02037                                                 if (!this.multiple && current_month && iday == this.currentDate.getDate() && this.hiliteToday && (dmonth == this.currentDate.getMonth()) && (dyear == this.currentDate.getFullYear())) {
02038                                                         cell.className += " selected";
02039                                                         this.currentDateEl = cell;
02040                                                 }
02041                                                 if (dyear == TY && dmonth == TM && iday == TD) {
02042                                                         cell.className += " today";
02043                                                         cell.ttip += Zapatec.Calendar.i18n("PART_TODAY");
02044                                                 }
02045                                                 if ((weekend != null) && (weekend.indexOf(wday.toString()) != -1)) {
02046                                                         cell.className += cell.otherMonth ? " oweekend" : " weekend";
02047                                                 }
02048                                         }
02049                                 }
02050                                 if (!(hasdays || this.showsOtherMonths))
02051                                         row.className = "emptyrow";
02052                         }
02053                         if ((i == 1) && (l < rowsOfMonths)) {
02054                                 if (row.className == "emptyrow") {
02055                                         row = row.previousSibling;
02056                                 }
02057                                 cell = row.firstChild;
02058                                 while (cell != null) {
02059                                         Zapatec.Utils.addClass(cell, "month-bottom-border");
02060                                         cell = cell.nextSibling;
02061                                 }
02062                         }
02063 
02064                 }
02065         }
02066         //this.title.firstChild.data = Zapatec.Calendar.i18n(month, "mn") + ", " + year;
02067         //filling of all titles for the months
02068         if (this.numberMonths == 1) {
02069                 this.title.innerHTML = Zapatec.Calendar.i18n(month, "mn") + ", " + year;
02070         } else {
02071                 for (var l = 1; l <= rowsOfMonths; ++l) {
02072                         for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
02073                                 if (this.vertical) {
02074                                         validMonth = month - diffMonth + ((k - 1) * (rowsOfMonths - 1) + (l - 1) + ((last_row < k) ? (last_row) : (k - 1)));
02075                                 } else {
02076                                         validMonth = month - diffMonth + (l - 1) * this.monthsInRow + k - 1;
02077                                 }
02078                                 validYear = year;
02079                                 if (validMonth < 0) {
02080                                         --validYear;
02081                                         validMonth = 12 + validMonth;
02082                                 }
02083                                 if (validMonth > 11) {
02084                                         ++validYear;
02085                                         validMonth = validMonth - 12;
02086                                 }
02087                                 this.titles[l][k].innerHTML = Zapatec.Calendar.i18n(validMonth, "mn") + ", " + validYear;
02088                         }
02089                 }
02090         }       
02091         this.onSetTime();
02092         //this.table.style.visibility = "visible";
02093         this._initMultipleDates();
02094         this.updateWCH();
02095         // PROFILE
02096         // this.showHint("Generated in " + ((new Date()) - today) + " ms");
02097 };
02098 
02104 Zapatec.Calendar.prototype._initMultipleDates = function() {
02105         if (this.multiple) {
02106                 for (var i in this.multiple) {
02107                         var cell = this.datesCells[i];
02108                         var d = this.multiple[i];
02109                         if (!d)
02110                                 continue;
02111                         if (cell)
02112                                 cell.className += " selected";
02113                 }
02114         }
02115 };
02116 
02126 Zapatec.Calendar.prototype._toggleMultipleDate = function(date) {
02127         if (this.multiple) {
02128                 var ds = date.print("%Y%m%d");
02129                 var cell = this.datesCells[ds];
02130                 if (cell) {
02131                         var d = this.multiple[ds];
02132                         if (!d) {
02133                                 Zapatec.Utils.addClass(cell, "selected");
02134                                 this.multiple[ds] = date;
02135                         } else {
02136                                 Zapatec.Utils.removeClass(cell, "selected");
02137                                 delete this.multiple[ds];
02138                         }
02139                 }
02140         }
02141 };
02142 
02164 Zapatec.Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
02165         this.getDateToolTip = unaryFunction;
02166 };
02167 
02176 Zapatec.Calendar.prototype.setDate = function (date, justInit) {
02177         // workaround for some bugs in our parseDate code
02178         if (!date)
02179                 date = new Date();
02180         if (!date.equalsTo(this.date)) {
02181                 var year = date.getFullYear(), m = date.getMonth();
02182                 if (year == this.minYear && m < this.minMonth)
02183                         this.showHint("<div class='error'>" + Zapatec.Calendar.i18n("E_RANGE") + " »»»</div>");
02184                 else if (year == this.maxYear && m > this.maxMonth)
02185                         this.showHint("<div class='error'>««« " + Zapatec.Calendar.i18n("E_RANGE") + "</div>");
02186                 this._init(this.firstDayOfWeek, date, justInit);
02187         }
02188 };
02189 
02195 Zapatec.Calendar.prototype.showHint = function(text) {
02196         this.tooltips.innerHTML = text;
02197 };
02198 
02208 Zapatec.Calendar.prototype.reinit = function() {
02209         this._init(this.firstDayOfWeek, this.date);
02210 };
02211 
02218 Zapatec.Calendar.prototype.refresh = function() {
02219         var p = this.isPopup ? null : this.element.parentNode;
02220         var x = parseInt(this.element.style.left);
02221         var y = parseInt(this.element.style.top);
02222         this.destroy();
02223         this.dateStr = this.date;
02224         this.create(p);
02225         if (this.isPopup)
02226                 this.showAt(x, y);
02227         else
02228                 this.show();
02229 };
02230 
02236 Zapatec.Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
02237         if (this.firstDayOfWeek != firstDayOfWeek) {
02238                 this._init(firstDayOfWeek, this.date);
02239                 //displaying the day names for all the rows of the months
02240                 var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
02241                 if (this.numberMonths % this.monthsInRow > 0) {
02242                         ++rowsOfMonths;
02243                 }
02244                 for (var l = 1; l <= rowsOfMonths; ++l) {
02245                         this.firstdayname = this.rowsOfDayNames[l];
02246                         this._displayWeekdays();
02247                 }               
02248         }
02249 };
02250 
02324 Zapatec.Calendar.prototype.setDateStatusHandler = Zapatec.Calendar.prototype.setDisabledHandler = function (unaryFunction) {
02325         this.getDateStatus = unaryFunction;
02326 };
02327 
02354 Zapatec.Calendar.prototype.setRange = function (A, Z) {
02355         var m,
02356                 a = Math.min(A, Z),
02357                 z = Math.max(A, Z);
02358         this.minYear = m = Math.floor(a);
02359         this.minMonth = (m == a) ? 0 : Math.ceil((a-m)*100-1);
02360         this.maxYear = m = Math.floor(z);
02361         this.maxMonth = (m == z) ? 11 : Math.ceil((z-m)*100-1);
02362 };
02363 
02371 Zapatec.Calendar.prototype.setMultipleDates = function(multiple) {
02372         if (!multiple || typeof multiple == "undefined") return;
02373         
02374         this.multiple = {};
02375         for (var i = multiple.length; --i >= 0;) {
02376             var d = multiple[i];
02377             var ds = d.print("%Y%m%d");
02378             this.multiple[ds] = d;
02379         }
02380 };
02381 
02387 Zapatec.Calendar.prototype.submitFlatDates = function()
02388 {
02389         if (typeof this.params.flatCallback == "function") {
02390            //Specify the pre-set sorting so that Zapatec.Utils will sort the multiple dates accordingly. 
02391            //Default to "asc" if it does not equal to "asc" or "desc".
02392            Zapatec.Utils.sortOrder = (this.sortOrder!="asc" && this.sortOrder!="desc" && this.sortOrder!="none") ? "none" : this.sortOrder;
02393                         
02394            if ( this.multiple && (Zapatec.Utils.sortOrder != "none") ) {
02395                         var dateArray = new Array();
02396                         
02397                         for (var i in this.multiple) {
02398                                 var currentDate = this.multiple[i];
02399                                 // sometimes the date is not actually selected, that's why we need to check.
02400                                 if (currentDate) {
02401                                         // and push it in the "dateArray", in case one triggers the calendar again.
02402                                         dateArray[dateArray.length] = currentDate;
02403                                 }
02404                                 //Now let's sort the dateArray array
02405                             dateArray.sort(Zapatec.Utils.compareDates);
02406                         }
02407 
02408                         this.multiple = {};
02409                         for (var i = 0; i < dateArray.length; i++) {
02410                                 var d = dateArray[i];
02411                                 var ds = d.print("%Y%m%d");
02412                                 this.multiple[ds] = d;
02413                         }
02414            } //Else no need to sort the multiple dates.
02415            this.params.flatCallback(this);
02416         }
02417 }
02418 
02424 Zapatec.Calendar.prototype.callHandler = function () {
02425         if (this.onSelected) {
02426                 this.onSelected(this, this.date.print(this.dateFormat));
02427         }
02428 };
02429 
02437 Zapatec.Calendar.prototype.updateHistory = function () {
02438         var a, i, d, tmp, s, str = "", len = Zapatec.Calendar.prefs.hsize - 1;
02439         if (Zapatec.Calendar.prefs.history) {
02440                 a = Zapatec.Calendar.prefs.history.split(/,/);
02441                 i = 0;
02442                 while (i < len && (tmp = a[i++])) {
02443                         s = tmp.split(/\//);
02444                         d = new Date(parseInt(s[0], 10), parseInt(s[1], 10)-1, parseInt(s[2], 10),
02445                                      parseInt(s[3], 10), parseInt(s[4], 10));
02446                         if (!d.dateEqualsTo(this.date))
02447                                 str += "," + tmp;
02448                 }
02449         }
02450         Zapatec.Calendar.prefs.history = this.date.print("%Y/%m/%d/%H/%M") + str;
02451         Zapatec.Calendar.savePrefs();
02452 };
02453 
02458 Zapatec.Calendar.prototype.callCloseHandler = function () {
02459         if (this.dateClicked) {
02460                 this.updateHistory();
02461         }
02462         if (this.onClose) {
02463                 this.onClose(this);
02464         }
02465         this.hideShowCovered();
02466 };
02467 
02469 Zapatec.Calendar.prototype.destroy = function () {
02470         this.hide();            // this also removes keyboard events :-\
02471         Zapatec.Utils.destroy(this.element);
02472         Zapatec.Utils.destroy(this.WCH);
02473         Zapatec.Calendar._C = null;
02474         window.calendar = null;
02475 };
02476 
02483 Zapatec.Calendar.prototype.reparent = function (new_parent) {
02484         var el = this.element;
02485         el.parentNode.removeChild(el);
02486         new_parent.appendChild(el);
02487 };
02488 
02497 Zapatec.Calendar._checkCalendar = function(ev) {
02498         if (!window.calendar) {
02499                 return false;
02500         }
02501         var el = Zapatec.is_ie ? Zapatec.Utils.getElement(ev) : Zapatec.Utils.getTargetElement(ev);
02502         for (; el != null && el != calendar.element; el = el.parentNode);
02503         if (el == null) {
02504                 // calls closeHandler which should hide the calendar.
02505                 window.calendar.callCloseHandler();
02506                 return Zapatec.Utils.stopEvent(ev);
02507         }
02508 };
02509 
02519 Zapatec.Calendar.prototype.updateWCH = function(other_el) {
02520         Zapatec.Utils.setupWCH_el(this.WCH, this.element, other_el);
02521 };
02522 
02534 Zapatec.Calendar.prototype.show = function () {
02535         var rows = this.table.getElementsByTagName("tr");
02536         for (var i = rows.length; i > 0;) {
02537                 var row = rows[--i];
02538                 Zapatec.Utils.removeClass(row, "rowhilite");
02539                 var cells = row.getElementsByTagName("td");
02540                 for (var j = cells.length; j > 0;) {
02541                         var cell = cells[--j];
02542                         Zapatec.Utils.removeClass(cell, "hilite");
02543                         Zapatec.Utils.removeClass(cell, "active");
02544                 }
02545         }
02546         this.element.style.display = "block";
02547         this.hidden = false;
02548         if (this.isPopup) {
02549                 this.updateWCH();
02550                 window.calendar = this;
02551                 if (!this.noGrab) {
02552                         Zapatec.Utils.addEvent(window.document, "keydown", Zapatec.Calendar._keyEvent);
02553                         Zapatec.Utils.addEvent(window.document, "keypress", Zapatec.Calendar._keyEvent);
02554                         Zapatec.Utils.addEvent(window.document, "mousedown", Zapatec.Calendar._checkCalendar);
02555                 }
02556         }
02557         this.hideShowCovered();
02558 };
02559 
02565 Zapatec.Calendar.prototype.hide = function () {
02566         if (this.isPopup) {
02567                 Zapatec.Utils.removeEvent(window.document, "keydown", Zapatec.Calendar._keyEvent);
02568                 Zapatec.Utils.removeEvent(window.document, "keypress", Zapatec.Calendar._keyEvent);
02569                 Zapatec.Utils.removeEvent(window.document, "mousedown", Zapatec.Calendar._checkCalendar);
02570         }
02571         this.element.style.display = "none";
02572         Zapatec.Utils.hideWCH(this.WCH);
02573         this.hidden = true;
02574         this.hideShowCovered();
02575 };
02576 
02585 Zapatec.Calendar.prototype.showAt = function (x, y) {
02586         var s = this.element.style;
02587         s.left = x + "px";
02588         s.top = y + "px";
02589         this.show();
02590 };
02591 
02620 Zapatec.Calendar.prototype.showAtElement = function (el, opts) {
02621         var self = this;
02622         var p = Zapatec.Utils.getAbsolutePos(el);
02623         if (!opts || typeof opts != "string") {
02624                 this.showAt(p.x, p.y + el.offsetHeight);
02625                 return true;
02626         }
02627         this.element.style.display = "block";
02628         var w = self.element.offsetWidth;
02629         var h = self.element.offsetHeight;
02630         self.element.style.display = "none";
02631         var valign = opts.substr(0, 1);
02632         var halign = "l";
02633         if (opts.length > 1) {
02634                 halign = opts.substr(1, 1);
02635         }
02636         // vertical alignment
02637         switch (valign) {
02638             case "T": p.y -= h; break;
02639             case "B": p.y += el.offsetHeight; break;
02640             case "C": p.y += (el.offsetHeight - h) / 2; break;
02641             case "t": p.y += el.offsetHeight - h; break;
02642             case "b": break; // already there
02643         }
02644         // horizontal alignment
02645         switch (halign) {
02646             case "L": p.x -= w; break;
02647             case "R": p.x += el.offsetWidth; break;
02648             case "C": p.x += (el.offsetWidth - w) / 2; break;
02649             case "l": p.x += el.offsetWidth - w; break;
02650             case "r": break; // already there
02651         }
02652         p.width = w;
02653         p.height = h + 40;
02654         self.monthsCombo.style.display = "none";
02655         Zapatec.Utils.fixBoxPosition(p);
02656         self.showAt(p.x, p.y);
02657 };
02658 
02665 Zapatec.Calendar.prototype.setDateFormat = function (str) {
02666         this.dateFormat = str;
02667 };
02668 
02675 Zapatec.Calendar.prototype.setTtDateFormat = function (str) {
02676         this.ttDateFormat = str;
02677 };
02678 
02686 Zapatec.Calendar.prototype.parseDate = function (str, fmt) {
02687         // Konqueror
02688         if (!str)
02689                 return this.setDate(this.date);
02690         if (!fmt)
02691                 fmt = this.dateFormat;
02692         var date = Date.parseDate(str, fmt);
02693         return this.setDate(date);
02694 };
02695 
02714 Zapatec.Calendar.prototype.hideShowCovered = function () {
02715         if (!Zapatec.is_ie5)
02716                 return;
02717         var self = this;
02718         function getVisib(obj){
02719                 var value = obj.style.visibility;
02720                 if (!value) {
02721                         if (window.document.defaultView && typeof (window.document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
02722                                 if (!Zapatec.is_khtml)
02723                                         value = window.document.defaultView.
02724                                                 getComputedStyle(obj, "").getPropertyValue("visibility");
02725                                 else
02726                                         value = '';
02727                         } else if (obj.currentStyle) { // IE
02728                                 value = obj.currentStyle.visibility;
02729                         } else
02730                                 value = '';
02731                 }
02732                 return value;
02733         };
02734 
02735         var tags = ["applet", "iframe", "select"];
02736         var el = self.element;
02737 
02738         var p = Zapatec.Utils.getAbsolutePos(el);
02739         var EX1 = p.x;
02740         var EX2 = el.offsetWidth + EX1;
02741         var EY1 = p.y;
02742         var EY2 = el.offsetHeight + EY1;
02743 
02744         for (var k = tags.length; k > 0; ) {
02745                 var ar = window.document.getElementsByTagName(tags[--k]);
02746                 var cc = null;
02747 
02748                 for (var i = ar.length; i > 0;) {
02749                         cc = ar[--i];
02750 
02751                         p = Zapatec.Utils.getAbsolutePos(cc);
02752                         var CX1 = p.x;
02753                         var CX2 = cc.offsetWidth + CX1;
02754                         var CY1 = p.y;
02755                         var CY2 = cc.offsetHeight + CY1;
02756 
02757                         if (self.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
02758                                 if (!cc.__msh_save_visibility) {
02759                                         cc.__msh_save_visibility = getVisib(cc);
02760                                 }
02761                                 cc.style.visibility = cc.__msh_save_visibility;
02762                         } else {
02763                                 if (!cc.__msh_save_visibility) {
02764                                         cc.__msh_save_visibility = getVisib(cc);
02765                                 }
02766                                 cc.style.visibility = "hidden";
02767                         }
02768                 }
02769         }
02770 };
02771 
02776 Zapatec.Calendar.prototype._displayWeekdays = function () {
02777         var fdow = this.firstDayOfWeek;
02778         var cell = this.firstdayname;
02779         var weekend = Zapatec.Calendar.i18n("WEEKEND");
02780         //displaying one row of day names for every month in the row
02781         for (k = 1; (k <= this.monthsInRow) && (cell); ++k) {
02782                 for (var i = 0; i < 7; ++i) {
02783                         cell.className = " day name";
02784                         if ((!this.weekNumbers) && (i == 0) && (k != 1)) {
02785                                 Zapatec.Utils.addClass(cell, "month-left-border");
02786                         }
02787                         if ((i == 6) && (k != this.monthsInRow)) {
02788                                 Zapatec.Utils.addClass(cell, "month-right-border");
02789                         }
02790                         var realday = (i + fdow) % 7;
02791                         
02792                         if ((!this.disableFdowClick) && ((this.params && this.params.fdowClick) || i)) {
02793                                 if (Zapatec.Calendar.i18n("DAY_FIRST") != null) {
02794                                         cell.ttip = Zapatec.Calendar.i18n("DAY_FIRST").replace("%s", Zapatec.Calendar.i18n(realday, "dn"));
02795                                 }
02796                                 cell.navtype = 100;
02797                                 cell.calendar = this;
02798                                 cell.fdow = realday;
02799                                 Zapatec.Calendar._add_evs(cell);
02800                         }
02801                         if ((weekend != null) && (weekend.indexOf(realday.toString()) != -1)) {
02802                                                         Zapatec.Utils.addClass(cell, "weekend");
02803                         }
02804                         cell.innerHTML = Zapatec.Calendar.i18n((i + fdow) % 7, "sdn");
02805                         cell = cell.nextSibling;
02806                 }
02807                 if (this.weekNumbers && cell) {
02808                         cell = cell.nextSibling;
02809                 }
02810         }
02811 };
02812 
02813 
02822 Zapatec.Utils.compareDates = function(date1, date2)
02823 {
02824         if (Zapatec.Calendar.prefs.sortOrder == "asc")
02825                 return date1 - date2;
02826         else //"desc"ending order
02827                 return date2 - date1;
02828 }
02829 
02831 Zapatec.Calendar.prototype._hideCombos = function () {
02832         this.monthsCombo.style.display = "none";
02833         this.yearsCombo.style.display = "none";
02834         this.histCombo.style.display = "none";
02835         this.updateWCH();
02836 };
02837 
02839 Zapatec.Calendar.prototype._dragStart = function (ev) {
02840         ev || (ev = window.event);
02841         if (this.dragging) {
02842                 return;
02843         }
02844         this.dragging = true;
02845         var posX = ev.clientX + window.document.body.scrollLeft;
02846         var posY = ev.clientY + window.document.body.scrollTop;
02847         var st = this.element.style;
02848         this.xOffs = posX - parseInt(st.left);
02849         this.yOffs = posY - parseInt(st.top);
02850         Zapatec.Utils.addEvent(window.document, "mousemove", Zapatec.Calendar.calDragIt);
02851         Zapatec.Utils.addEvent(window.document, "mouseover", Zapatec.Calendar.calDragIt);
02852         Zapatec.Utils.addEvent(window.document, "mouseup", Zapatec.Calendar.calDragEnd);
02853 };
02854 
02855 // BEGIN: DATE OBJECT PATCHES
02856 
02861 
02862 Date._MD = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 
02864 Date.SECOND = 1000;             
02865 Date.MINUTE = 60 * Date.SECOND; 
02866 Date.HOUR   = 60 * Date.MINUTE; 
02867 Date.DAY    = 24 * Date.HOUR;   
02868 Date.WEEK   =  7 * Date.DAY;    
02876 Date.prototype.getMonthDays = function(month) {
02877         var year = this.getFullYear();
02878         if (typeof month == "undefined") {
02879                 month = this.getMonth();
02880         }
02881         if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
02882                 return 29;
02883         } else {
02884                 return Date._MD[month];
02885         }
02886 };
02887 
02889 Date.prototype.getDayOfYear = function() {
02890         var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
02891         var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
02892         var time = now - then;
02893         return Math.floor(time / Date.DAY);
02894 };
02895 
02897 Date.prototype.getWeekNumber = function() {
02898         var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
02899         var DoW = d.getDay();
02900         d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
02901         var ms = d.valueOf(); // GMT
02902         d.setMonth(0);
02903         d.setDate(4); // Thu in Week 1
02904         return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
02905 };
02906 
02908 Date.prototype.equalsTo = function(date) {
02909         return ((this.getFullYear() == date.getFullYear()) &&
02910                 (this.getMonth() == date.getMonth()) &&
02911                 (this.getDate() == date.getDate()) &&
02912                 (this.getHours() == date.getHours()) &&
02913                 (this.getMinutes() == date.getMinutes()));
02914 };
02915 
02917 Date.prototype.dateEqualsTo = function(date) {
02918         return ((this.getFullYear() == date.getFullYear()) &&
02919                 (this.getMonth() == date.getMonth()) &&
02920                 (this.getDate() == date.getDate()));
02921 };
02922 
02924 Date.prototype.setDateOnly = function(date) {
02925         var tmp = new Date(date);
02926         this.setDate(1);
02927         this.setFullYear(tmp.getFullYear());
02928         this.setMonth(tmp.getMonth());
02929         this.setDate(tmp.getDate());
02930 };
02931 
02967 Date.prototype.print = function (str) {
02968         var m = this.getMonth();
02969         var d = this.getDate();
02970         var y = this.getFullYear();
02971         var wn = this.getWeekNumber();
02972         var w = this.getDay();
02973         var s = {};
02974         var hr = this.getHours();
02975         var pm = (hr >= 12);
02976         var ir = (pm) ? (hr - 12) : hr;
02977         var dy = this.getDayOfYear();
02978         if (ir == 0)
02979                 ir = 12;
02980         var min = this.getMinutes();
02981         var sec = this.getSeconds();
02982         s["%a"] = Zapatec.Calendar.i18n(w, "sdn"); // abbreviated weekday name [FIXME: I18N]
02983         s["%A"] = Zapatec.Calendar.i18n(w, "dn"); // full weekday name
02984         s["%b"] = Zapatec.Calendar.i18n(m, "smn"); // abbreviated month name [FIXME: I18N]
02985         s["%B"] = Zapatec.Calendar.i18n(m, "mn"); // full month name
02986         // FIXME: %c : preferred date and time representation for the current locale
02987         s["%C"] = 1 + Math.floor(y / 100); // the century number
02988         s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
02989         s["%e"] = d; // the day of the month (range 1 to 31)
02990         // FIXME: %D : american date style: %m/%d/%y
02991         // FIXME: %E, %F, %G, %g, %h (man strftime)
02992         s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
02993         s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
02994         s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
02995         s["%k"] = hr ? hr :  "0"; // hour, range 0 to 23 (24h format)
02996         s["%l"] = ir;           // hour, range 1 to 12 (12h format)
02997         s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
02998         s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
02999         s["%n"] = "\n";         // a newline character
03000         s["%p"] = pm ? "PM" : "AM";
03001         s["%P"] = pm ? "pm" : "am";
03002         // FIXME: %r : the time in am/pm notation %I:%M:%S %p
03003         // FIXME: %R : the time in 24-hour notation %H:%M
03004         s["%s"] = Math.floor(this.getTime() / 1000);
03005         s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
03006         s["%t"] = "\t";         // a tab character
03007         // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
03008         s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
03009   s["%u"] = (w == 0) ? 7 : w; // the day of the week (range 1 to 7, 1 = MON)
03010         s["%w"] = w ? w : "0";          // the day of the week (range 0 to 6, 0 = SUN)
03011         // FIXME: %x : preferred date representation for the current locale without the time
03012         // FIXME: %X : preferred time representation for the current locale without the date
03013         s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
03014         s["%Y"] = y;            // year with the century
03015         s["%%"] = "%";          // a literal '%' character
03016 
03017         var re = /%./g;
03018         if (!Zapatec.is_ie5 && !Zapatec.is_khtml && !Zapatec.is_mac_ie)
03019                 return str.replace(re, function (par) { return s[par] || par; });
03020 
03021         var a = str.match(re);
03022         for (var i = 0; i < a.length; i++) {
03023                 var tmp = s[a[i]];
03024                 if (tmp) {
03025                         re = new RegExp(a[i], 'g');
03026                         str = str.replace(re, tmp);
03027                 }
03028         }
03029 
03030         return str;
03031 };
03032 
03042 Date.parseDate = function (str, fmt) {
03043         // Konqueror
03044         if (!str)
03045                 return new Date();
03046         var y = 0;
03047         var m = -1;
03048         var d = 0;
03049         var a = str.split(/\W+/);
03050         var b = fmt.match(/%./g);
03051         var i = 0, j = 0;
03052         var hr = 0;
03053         var min = 0;
03054         for (i = 0; i < a.length; ++i) {
03055                 if (!a[i])
03056                         continue;
03057                 switch (b[i]) {
03058                     case "%d":
03059                     case "%e":
03060                         d = parseInt(a[i], 10);
03061                         break;
03062 
03063                     case "%m":
03064                         m = parseInt(a[i], 10) - 1;
03065                         break;
03066 
03067                     case "%Y":
03068                     case "%y":
03069                         y = parseInt(a[i], 10);
03070                         (y < 100) && (y += (y > 29) ? 1900 : 2000);
03071                         break;
03072 
03073                     case "%b":
03074                     case "%B":
03075                         for (j = 0; j < 12; ++j)
03076                                 if (Zapatec.Calendar.i18n(j, "mn").substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) {
03077                                         m = j;
03078                                         break;
03079                                 }
03080                         break;
03081 
03082                     case "%H":
03083                     case "%I":
03084                     case "%k":
03085                     case "%l":
03086                         hr = parseInt(a[i], 10);
03087                         break;
03088 
03089                     case "%P":
03090                     case "%p":
03091                         if (/pm/i.test(a[i]) && hr < 12)
03092                                 hr += 12;
03093                         if (/am/i.test(a[i]) && hr == 12)
03094                                 hr = 0;
03095                         break;
03096 
03097                     case "%M":
03098                         min = parseInt(a[i], 10);
03099                         break;
03100                 }
03101         }
03102         //Fix for the mm/dd/yy bug
03103         var validDate = !isNaN(y) && !isNaN(m) && !isNaN(d) && !isNaN(hr) && !isNaN(min);
03104         if (!validDate) {return null;}
03105         if (y != 0 && m != -1 && d != 0)
03106                 return new Date(y, m, d, hr, min, 0);
03107         y = 0; m = -1; d = 0;
03108         for (i = 0; i < a.length; ++i) {
03109                 if (a[i].search(/[a-zA-Z]+/) != -1) {
03110                         var t = -1;
03111                         for (j = 0; j < 12; ++j)
03112                                 if (Zapatec.Calendar.i18n(j, "mn").substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) {
03113                                         t = j;
03114                                         break;
03115                                 }
03116                         if (t != -1) {
03117                                 if (m != -1)
03118                                         d = m+1;
03119                                 m = t;
03120                         }
03121                 } else if (parseInt(a[i], 10) <= 12 && m == -1) {
03122                         m = a[i]-1;
03123                 } else if (parseInt(a[i], 10) > 31 && y == 0) {
03124                         y = parseInt(a[i], 10);
03125                         (y < 100) && (y += (y > 29) ? 1900 : 2000);
03126                 } else if (d == 0) {
03127                         d = a[i];
03128                 }
03129         }
03130         if (y == 0) {
03131                 var today = new Date();
03132                 y = today.getFullYear();
03133         }
03134         if (m != -1 && d != 0)
03135                 return new Date(y, m, d, hr, min, 0);
03136         return null;
03137 };
03138 
03139 Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear; 
03149 Date.prototype.setFullYear = function(y) {
03150         var d = new Date(this);
03151         d.__msh_oldSetFullYear(y);
03152         if (d.getMonth() != this.getMonth())
03153                 this.setDate(28);
03154         this.__msh_oldSetFullYear(y);
03155 };
03156 
03165 Date.prototype.compareDatesOnly = function (date1,date2) { 
03166         var year1 = date1.getYear();
03167         var year2 = date2.getYear(); 
03168         var month1 = date1.getMonth(); 
03169         var month2 = date2.getMonth(); 
03170         var day1 = date1.getDate(); 
03171         var day2 = date2.getDate(); 
03172         if (year1 > year2) { return -1; } 
03173         if (year2 > year1) { return 1; } //years are equal 
03174         if (month1 > month2) { return -1; } 
03175         if (month2 > month1) { return 1; } //years and months are equal 
03176         if (day1 > day2) { return -1; } 
03177         if (day2 > day1) { return 1; } //days are equal 
03178         return 0; 
03179 }
03180 
03182 
03183 // END: DATE OBJECT PATCHES
03184 
03185 window.calendar = null;         
03187 // initialize the preferences object;
03188 // embed it in a try/catch so we don't have any surprises
03189 try {
03190         Zapatec.Calendar.loadPrefs();
03191 } catch(e) {};