1/*----------------------------------------------------------------------------\ 2| Date Picker 1.06 | 3|-----------------------------------------------------------------------------| 4| Created by Erik Arvidsson | 5| (http://webfx.eae.net/contact.html#erik) | 6| For WebFX (http://webfx.eae.net/) | 7|-----------------------------------------------------------------------------| 8| A DOM based Date Picker | 9|-----------------------------------------------------------------------------| 10| Copyright (c) 1999, 2002, 2002, 2003, 2004, 2006 Erik Arvidsson | 11|-----------------------------------------------------------------------------| 12| Licensed under the Apache License, Version 2.0 (the "License"); you may not | 13| use this file except in compliance with the License. You may obtain a copy | 14| of the License at http://www.apache.org/licenses/LICENSE-2.0 | 15| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 16| Unless required by applicable law or agreed to in writing, software | 17| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 18| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 19| License for the specific language governing permissions and limitations | 20| under the License. | 21|-----------------------------------------------------------------------------| 22| Dependencies: datepicker.css Date picker style declarations | 23|-----------------------------------------------------------------------------| 24| 2002-02-10 | Changed _update method to only update the text nodes instead | 25| | rewriting the entire table. Also added support for mouse wheel | 26| | in IE6. | 27| 2002-01-14 | Cleaned up for 1.0 public version | 28| 2002-01-15 | Replace all innerHTML calls with DOM1 methods | 29| 2002-01-18 | Minor IE6 bug that occured when dragging the mouse | 30| 2002-01-19 | Added a popup that is shown when the user clicks on the month. | 31| | This allows navigation to 6 adjacent months. | 32| 2002-04-10 | Fixed a bug that occured in the popup when a date was selected | 33| | that caused surroundung months to "overflow" | 34| | This had the effect that one could get two October months | 35| | listed. | 36| 2002-09-06 | I had missed one place were window was used instead of | 37| | doc.parentWindow | 38| 2003-08-28 | Added support for ensurin no date overflow when changing | 39| | months. | 40| 2004-01-10 | Adding type on the buttons to ensure they are not submit | 41| | buttons. Minor CSS change for CSS2 | 42| 2006-05-28 | Changed license to Apache Software License 2.0. | 43|-----------------------------------------------------------------------------| 44| Created 2001-10-?? | All changes are in the log above. | Updated 2006-05-28 | 45\----------------------------------------------------------------------------*/ 46 47// The DatePicker constructor 48// oDate : Date Optional argument representing the date to select 49function DatePicker( oDate ) { 50 // check arguments 51 if ( arguments.length == 0 ) { 52 this._selectedDate = new Date; 53 this._none = false; 54 } 55 else { 56 this._selectedDate = oDate || new Date(); 57 this._none = oDate == null; 58 } 59 60 this._matrix = [[],[],[],[],[],[],[]]; 61 this._showNone = true; 62 this._showToday = true; 63 this._firstWeekDay = 0; // start week with monday according to standards 64 this._redWeekDay = 6; // sunday is the default red day. 65 66 this._dontChangeNone = false; 67 this._hideCalendar = false; 68} 69 70// two static fields describing the name of the months abd days 71DatePicker.months = [ 72 "January", "February", "March", "April", 73 "May", "June", "July", "August",
74 "September", "October", "November", "December"]; 75DatePicker.days = ["m", "t", "w", "t", "f", "s", "s"]; 76 77 78DatePicker.prototype.onchange = function () {}; 79 80// create the nodes inside the date picker 81DatePicker.prototype.create = function ( doc ) { 82 if ( doc == null ) doc = document; 83 84 this._document = doc; 85 86 // create elements 87 this._el = doc.createElement( "div" ); 88 this._el.className = "datePicker"; 89 90 // header 91 var div = doc.createElement( "div" ); 92 div.className = "dateheader"; 93 this._el.appendChild( div ); 94 95 var headerTable = doc.createElement( "table" ); 96 headerTable.className = "headerTable"; 97 headerTable.cellSpacing = 0; 98 div.appendChild( headerTable ); 99 100 var tBody = doc.createElement( "tbody" ); 101 headerTable.appendChild( tBody ); 102 103 var tr = doc.createElement( "tr" ); 104 tBody.appendChild( tr ); 105 106 var td = doc.createElement( "td" ); 107 this._previousMonth = doc.createElement( "button" ); 108 this._previousMonth.className = "previousButton"; 109 this._previousMonth.setAttribute("type", "button"); 110 td.appendChild( this._previousMonth ); 111 tr.appendChild( td ); 112 113 td = doc.createElement( "td" ); 114 td.className = "labelContainer"; 115 tr.appendChild( td ); 116 117 this._topLabel = doc.createElement( "a" ); 118 this._topLabel.className = "topLabel"; 119 this._topLabel.href = "#"; 120 this._topLabel.appendChild( doc.createTextNode( String.fromCharCode( 160 ) ) ); 121 td.appendChild( this._topLabel ); 122 123 this._labelPopup = doc.createElement( "div" ); 124 this._labelPopup.className = "labelPopup"; 125 // no insertion 126 127 td = doc.createElement( "td" ); 128 this._nextMonth = doc.createElement( "button" ); 129 this._nextMonth.className = "nextButton"; 130 this._nextMonth.setAttribute("type", "button"); 131 td.appendChild( this._nextMonth ); 132 tr.appendChild( td ); 133 134 // grid 135 div = doc.createElement( "div" ); 136 div.className = "grid"; 137 this._el.appendChild( div ); 138 this._table = div; 139 140 // footer 141 div = doc.createElement( "div" ); 142 div.className = "datefooter"; 143 this._el.appendChild( div ); 144 145 var footerTable = doc.createElement( "table" ); 146 footerTable.className = "footerTable"; 147 footerTable.cellSpacing = 0; 148 div.appendChild( footerTable ); 149 150 tBody = doc.createElement( "tbody" ); 151 footerTable.appendChild( tBody ); 152 153 tr = doc.createElement( "tr" ); 154 tBody.appendChild( tr ); 155 156 td = doc.createElement( "td" ); 157 this._todayButton = doc.createElement( "button" ); 158 this._todayButton.className = "todayButton"; 159 this._todayButton.setAttribute("type", "button"); 160 this._todayButton.appendChild( doc.createTextNode( "Today" ) ); 161 td.appendChild( this._todayButton ); 162 tr.appendChild( td ); 163 164 td = doc.createElement( "td" ); 165 td.className = "filler"; 166 td.appendChild( doc.createTextNode( String.fromCharCode( 160 ) ) ); 167 tr.appendChild( td ); 168 169 td = doc.createElement( "td" ); 170 this._noneButton = doc.createElement( "button" ); 171 this._noneButton.className = "noneButton"; 172 this._noneButton.setAttribute("type", "button"); 173 this._noneButton.appendChild( doc.createTextNode( "None" ) ); 174 td.appendChild( this._noneButton ); 175 tr.appendChild( td ); 176 177 178 179 this._createTable( doc ); 180 181 this._updateTable(); 182 this._setTopLabel(); 183 184 if ( !this._showNone ) 185 this._noneButton.style.visibility = "hidden"; 186 if ( !this._showToday ) 187 this._todayButton.style.visibility = "hidden"; 188 189 // IE55+ extension 190 this._previousMonth.hideFocus = true; 191 this._nextMonth.hideFocus = true; 192 this._todayButton.hideFocus = true; 193 this._noneButton.hideFocus = true; 194 // end IE55+ extension 195 196 // hook up events 197 var dp = this; 198 // buttons 199 this._previousMonth.onclick = function () { 200 dp._hideCalendar = false; 201 dp._dontChangeNone = true; 202 dp.goToPreviousMonth(); 203 dp._dontChangeNone = false; 204 }; 205 this._nextMonth.onclick = function () { 206 this._hideCalendar = false; 207 dp._dontChangeNone = true; 208 dp.goToNextMonth(); 209 dp._dontChangeNone = false; 210 }; 211 this._todayButton.onclick = function () { 212 dp.goToToday(); 213 }; 214 this._noneButton.onclick = function () { 215 dp.setDate( null ); 216 }; 217 218 this._el.onselectstart = function () { 219 return false; 220 }; 221 222 this._table.onclick = function ( e ) { 223 // find event 224 if ( e == null ) e = doc.parentWindow.event; 225
226 // find td 227 var el = e.target != null ? e.target : e.srcElement; 228 while ( el.nodeType != 1 ) 229 el = el.parentNode; 230 while ( el != null && el.tagName && el.tagName.toLowerCase() != "td" ) 231 el = el.parentNode; 232 233 // if no td found, return 234 if ( el == null || el.tagName == null || el.tagName.toLowerCase() != "td" ) 235 return; 236 237 var d = new Date( dp._selectedDate ); 238 var n = Number( el.firstChild.data ); 239 if ( isNaN( n ) || n <= 0 || n == null ) 240 return; 241 242 dp._hideCalendar = true; 243 d.setDate( n ); 244 dp.setDate( d ); 245 }; 246 247 // show popup 248 this._topLabel.onclick = function ( e ) { 249 dp._hideCalendar = false; 250 dp._showLabelPopup(); 251 return false; 252 }; 253 254 this._el.onkeydown = function ( e ) { 255 if ( e == null ) e = doc.parentWindow.event; 256 var kc = e.keyCode != null ? e.keyCode : e.charCode; 257 258 if ( kc < 37 || kc > 40 ) return true; 259 260 var d = new Date( dp._selectedDate ).valueOf(); 261 if ( kc == 37 ) // left 262 d -= 24 * 60 * 60 * 1000; 263 else if ( kc == 39 ) // right 264 d += 24 * 60 * 60 * 1000; 265 else if ( kc == 38 ) // up 266 d -= 7 * 24 * 60 * 60 * 1000; 267 else if ( kc == 40 ) // down 268 d += 7 * 24 * 60 * 60 * 1000; 269 dp._hideCalendar = true; 270 dp.setDate( new Date( d ) ); 271 return false; 272 } 273 274 // ie6 extension 275 this._el.onmousewheel = function ( e ) { 276 if ( e == null ) e = doc.parentWindow.event; 277 var n = - e.wheelDelta / 120; 278 var d = new Date( dp._selectedDate ); 279 var m = d.getMonth() + n; 280 d.setMonth( m ); 281 282 283 dp._dontChangeNone = true; 284 dp.setDate( d ); 285 dp._dontChangeNone = false; 286 287 return false; 288 } 289 290 return this._el; 291}; 292 293DatePicker.prototype.setDate = function ( oDate ) { 294 295 this._hideLabelPopup(); 296 297 // if null then set None 298 if ( oDate == null ) { 299 if ( !this._none ) { 300 this._none = true; 301 this._setTopLabel(); 302 this._updateTable(); 303 304 if ( typeof this.onchange == "function" ) 305 this.onchange(); 306 } 307 return; 308 } 309 310 // if string or number create a Date object 311 if ( typeof oDate == "string" || typeof oDate == "number" ) { 312 oDate = new Date( oDate ); 313 } 314 315 316 // do not update if not really changed 317 if ( this._selectedDate.getDate() != oDate.getDate() || 318 this._selectedDate.getMonth() != oDate.getMonth() || 319 this._selectedDate.getFullYear() != oDate.getFullYear() || 320 this._none ) { 321 322 if ( !this._dontChangeNone ) 323 this._none = false; 324 325 this._selectedDate = new Date( oDate ); 326 327 this._setTopLabel(); 328 this._updateTable(); 329 330 331 } 332 if ( typeof this.onchange == "function" && this._hideCalendar == true ){ 333 334 this.onchange(); 335 } 336 if ( !this._dontChangeNone ) 337 this._none = false; 338} 339 340 341DatePicker.prototype.getDate = function () { 342 if ( this._none ) return null; 343 return new Date( this._selectedDate ); // create a new instance 344} 345 346// creates the table elements and inserts them into the date picker 347DatePicker.prototype._createTable = function ( doc ) { 348 var str, i; 349 var rows = 6; 350 var cols = 7; 351 var currentWeek = 0; 352 353 var table = doc.createElement( "table" ); 354 table.className = "gridTable"; 355 table.cellSpacing = 0; 356 357 var tBody = doc.createElement( "tbody" ); 358 table.appendChild( tBody ); 359 360 // days row 361 var tr = doc.createElement( "tr" ); 362 tr.className = "daysRow"; 363 364 var td, tn; 365 var nbsp = String.fromCharCode( 160 ); 366 for ( i = 0; i < cols; i++ ) { 367 td = doc.createElement( "td" ); 368 td.appendChild( doc.createTextNode( nbsp ) ); 369 tr.appendChild( td ); 370 } 371 tBody.appendChild( tr ); 372 373 // upper line 374 tr = doc.createElement( "tr" ); 375 td = doc.createElement( "td" ); 376 td.className = "upperLine"; 377 td.colSpan = 7; 378 tr.appendChild( td ); 379 tBody.appendChild( tr ); 380 381 // rest 382 for ( i = 0; i < rows; i++ ) { 383 tr = doc.createElement( "tr" ); 384 for ( var j = 0; j < cols; j++ ) { 385 td = doc.createElement( "td" ); 386 td.appendChild( doc.createTextNode( nbsp ) ); 387 tr.appendChild( td ); 388 } 389 tBody.appendChild( tr ); 390 } 391 str += "</table>"; 392 393 if ( this._table != null ) 394 this._table.appendChild( table ) 395}; 396// this method updates all the text nodes inside the table as well 397// as all the classNames on the tds 398DatePicker.prototype._updateTable = function () { 399 // if no element no need to continue 400 if ( this._table == null ) return; 401 402 var i; 403 var str = ""; 404 var rows = 6; 405 var cols = 7; 406 var currentWeek = 0; 407 408 var cells = new Array( rows ); 409 this._matrix = new Array( rows ) 410 for ( i = 0; i < rows; i++ ) { 411 cells[i] = new Array( cols ); 412 this._matrix[i] = new Array( cols ); 413 } 414 415 // Set the tmpDate to this month 416 var tmpDate = new Date( this._selectedDate.getFullYear(), 417 this._selectedDate.getMonth(), 1 ); 418 var today = new Date(); 419 // go thorugh all days this month and store the text 420 // and the class name in the cells matrix 421 for ( i = 1; i < 32; i++ ) { 422 tmpDate.setDate( i ); 423 // convert to ISO, Monday is 0 and 6 is Sunday 424 var weekDay = ( tmpDate.getDay() + 6 ) % 7; 425 var colIndex = ( weekDay - this._firstWeekDay + 7 ) % 7; 426 if ( tmpDate.getMonth() == this._selectedDate.getMonth() ) { 427 428 var isToday = tmpDate.getDate() == today.getDate() && 429 tmpDate.getMonth() == today.getMonth() && 430 tmpDate.getFullYear() == today.getFullYear(); 431 432 cells[currentWeek][colIndex] = { text: "", className: "" }; 433 434 if ( this._selectedDate.getDate() == tmpDate.getDate() && !this._none ) 435 cells[currentWeek][colIndex].className += "selected "; 436 if ( isToday ) 437 cells[currentWeek][colIndex].className += "today "; 438 if ( ( tmpDate.getDay() + 6 ) % 7 == this._redWeekDay ) // ISO 439 cells[currentWeek][colIndex].className += "red"; 440 441 cells[currentWeek][colIndex].text = 442 this._matrix[currentWeek][colIndex] = tmpDate.getDate(); 443 444 if ( colIndex == 6 ) 445 currentWeek++; 446 } 447 } 448 449 // fix day letter order if not standard 450 var weekDays = DatePicker.days; 451 if (this._firstWeekDay != 0) { 452 weekDays = new Array(7); 453 for ( i = 0; i < 7; i++) 454 weekDays[i] = DatePicker.days[ (i + this._firstWeekDay) % 7]; 455 } 456 457 // update text in days row 458 var tds = this._table.firstChild.tBodies[0].rows[0].cells; 459 for ( i = 0; i < cols; i++ ) 460 tds[i].firstChild.data = weekDays[i]; 461
462 // update the text nodes and class names 463 var trs = this._table.firstChild.tBodies[0].rows; 464 var tmpCell; 465 var nbsp = String.fromCharCode( 160 ); 466 for ( var y = 0; y < rows; y++ ) { 467 for (var x = 0; x < cols; x++) { 468 tmpCell = trs[y + 2].cells[x]; 469 if ( typeof cells[y][x] != "undefined" ) { 470 tmpCell.className = cells[y][x].className; 471 tmpCell.firstChild.data = cells[y][x].text; 472 } 473 else { 474 tmpCell.className = ""; 475 tmpCell.firstChild.data = nbsp; 476 } 477 } 478 } 479} 480 481// sets the label showing the year and selected month 482DatePicker.prototype._setTopLabel = function () { 483 var str = this._selectedDate.getFullYear() + " " + DatePicker.months[ this._selectedDate.getMonth() ]; 484 if ( this._topLabel != null ) 485 this._topLabel.lastChild.data = str; 486} 487 488DatePicker.prototype.goToNextMonth = function () { 489 var d = new Date( this._selectedDate ); 490 d.setDate( Math.min(d.getDate(), DatePicker.getDaysPerMonth(d.getMonth() + 1, 491 d.getFullYear())) ); // no need to catch dec -> jan for the year 492 d.setMonth( d.getMonth() + 1 ); 493 this.setDate( d ); 494} 495 496DatePicker.prototype.goToPreviousMonth = function () { 497 var d = new Date( this._selectedDate ); 498 d.setDate( Math.min(d.getDate(), DatePicker.getDaysPerMonth(d.getMonth() - 1, 499 d.getFullYear())) ); // no need to catch jan -> dec for the year 500 d.setMonth( d.getMonth() - 1 ); 501 this.setDate( d ); 502} 503 504DatePicker.prototype.goToToday = function () { 505 if ( this._none ) 506 // change the selectedDate to force update if none was true 507 this._selectedDate = new Date( this._selectedDate + 10000000000 ); 508 this._none = false; 509 this.setDate( new Date() ); 510} 511 512DatePicker.prototype.setShowToday = function ( bShowToday ) { 513 if ( typeof bShowToday == "string" ) 514 bShowToday = !/false|0|no/i.test( bShowToday ); 515 516 if ( this._todayButton != null ) 517 this._todayButton.style.visibility = bShowToday ? "visible" : "hidden"; 518 this._showToday = bShowToday; 519} 520 521DatePicker.prototype.getShowToday = function () { 522 return this._showToday; 523} 524 525DatePicker.prototype.setShowNone = function ( bShowNone ) { 526 if ( typeof bShowNone == "string" ) 527 bShowNone = !/false|0|no/i.test( bShowNone ); 528 529 if ( this._noneButton != null ) 530 this._noneButton.style.visibility = bShowNone ? "visible" : "hidden"; 531 this._showNone = bShowNone; 532} 533 534DatePicker.prototype.getShowNone = function () { 535 return this._showNone; 536} 537 538// 0 is monday and 6 is sunday as in the ISO standard 539DatePicker.prototype.setFirstWeekDay = function ( nFirstWeekDay ) { 540 if ( this._firstWeekDay != nFirstWeekDay ) { 541 this._firstWeekDay = nFirstWeekDay; 542 this._updateTable(); 543 } 544} 545 546DatePicker.prototype.getFirstWeekDay = function () { 547 return this._firstWeekDay; 548} 549 550// 0 is monday and 6 is sunday as in the ISO standard 551DatePicker.prototype.setRedWeekDay = function ( nRedWeekDay ) { 552 if ( this._redWeekDay != nRedWeekDay ) { 553 this._redWeekDay = nRedWeekDay; 554 this._updateTable(); 555 } 556} 557 558DatePicker.prototype.getRedWeekDay = function () { 559 return this._redWeekDay; 560} 561 562 563DatePicker.prototype._showLabelPopup = function () { 564 565 /* 566 this._labelPopup document.createElement( "DIV" ); 567 div.className = "month-popup"; 568 div.noWrap = true; 569 el.unselectable = div.unselectable = "on"; 570 el.onselectstart = div.onselectstart = function () { return false; }; 571 */ 572 573 var dateContext = function ( dp, d ) { 574 return function ( e ) { 575 dp._dontChangeNone = true; 576 dp._hideLabelPopup(); 577 dp.setDate( d ); 578 dp._dontChangeNone = false; 579 return false; 580 }; 581 }; 582 583 var dp = this; 584 585 // clear all old elements in the popup 586 while ( this._labelPopup.hasChildNodes() ) 587 this._labelPopup.removeChild( this._labelPopup.firstChild ); 588 589 var a, tmp, tmp2; 590 for ( var i = -3; i < 4; i++ ) { 591 tmp = new Date( this._selectedDate ); 592 tmp2 = new Date( this._selectedDate ); // need another tmp to catch year change when checking leap 593 tmp2.setDate(1); 594 tmp2.setMonth( tmp2.getMonth() + i ); 595 tmp.setDate( Math.min(tmp.getDate(), DatePicker.getDaysPerMonth(tmp.getMonth() + i, 596 tmp2.getFullYear())) ); 597 tmp.setMonth( tmp.getMonth() + i ); 598 599 a = this._document.createElement( "a" ); 600 a.href = "javascript:void 0;"; 601 a.onclick = dateContext( dp, tmp ); 602 a.appendChild( this._document.createTextNode( tmp.getFullYear() + " " + 603 DatePicker.months[ tmp.getMonth() ] ) ); 604 if ( i == 0 ) 605 a.className = "selected"; 606 this._labelPopup.appendChild( a ); 607 } 608 609 this._topLabel.parentNode.insertBefore( this._labelPopup, this._topLabel.parentNode.firstChild ); 610};
611 612DatePicker.prototype._hideLabelPopup = function () { 613 if ( this._labelPopup.parentNode ) 614 this._labelPopup.parentNode.removeChild( this._labelPopup ); 615}; 616 617DatePicker._daysPerMonth = [31,28,31,30,31,30,31,31,30,31,30,31]; 618DatePicker.getDaysPerMonth = function (nMonth, nYear) { 619 nMonth = (nMonth + 12) % 12; 620 var res = DatePicker._daysPerMonth[nMonth]; 621 if (nMonth == 1) { 622 res += nYear % 4 == 0 && !(nYear % 400 == 0) ? 1 : 0; 623 } 624 return res; 625}; 626 627function getFullISODateString(d) 628{ 629 var year = d.getFullYear(); 630 var month = leadingZero(d.getMonth()+1); 631 var day = leadingZero(d.getDate()); 632 633 var date = year+"-"+month+"-"+day; 634 return date; 635} 636 637function leadingZero(x){ 638 return (x>9)?x:'0'+x; 639}
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.