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