1/** 2 * datepicker.js - MooTools Datepicker class 3 * @version 1.17 4 * 5 * by MonkeyPhysics.com 6 * 7 * Source/Documentation available at: 8 * http://www.monkeyphysics.com/mootools/script/2/datepicker 9 * 10 * -- 11 * 12 * Smoothly animating, very configurable and easy to install. 13 * No Ajax, pure Javascript. 4 skins available out of the box. 14 * 15 * -- 16 * 17 * Some Rights Reserved 18 * http://creativecommons.org/licenses/by-sa/3.0/ 19 * 20 */ 21 22var DatePicker = new Class({ 23 24 Implements: Options, 25 26 // working date, which we will keep modifying to render the calendars 27 d: '', 28 29 // just so that we need not request it over and over 30 today: '', 31 32 // current user-choice in date object format 33 choice: {}, 34 35 // size of body, used to animate the sliding 36 bodysize: {}, 37 38 // to check availability of next/previous buttons 39 limit: {}, 40 41 // element references: 42 attachTo: null, // selector for target inputs 43 picker: null, // main datepicker container 44 slider: null, // slider that contains both oldContents and newContents, used to animate between 2 different views 45 oldContents: null, // used in animating from-view to new-view 46 newContents: null, // used in animating from-view to new-view 47 input: null, // original input element (used for input/output) 48 visual: null, // visible input (used for rendering) 49 50 options: { 51 pickerClass: 'datepicker', 52 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 53 months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 54 dayShort: 2, 55 monthShort: 3, 56 startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin 57 timePicker: false, 58 timePickerOnly: false, 59 yearPicker: true, 60 yearsPerPage: 20, 61 format: 'd-m-Y', 62 allowEmpty: false, 63 inputOutputFormat: 'U', // default to unix timestamp 64 animationDuration: 400, 65 useFadeInOut: !Browser.Engine.trident, // dont animate fade-in/fade-out for IE 66 startView: 'month', // allowed values: {time, month, year, decades} 67 positionOffset: { x: 0, y: 0 }, 68 minDate: null, // { date: '[date-string]', format: '[date-string-interpretation-format]' } 69 maxDate: null, // same as minDate 70 debug: false, 71 toggleElements: null, 72 73 // and some event hooks: 74 onShow: $empty, // triggered when the datepicker pops up 75 onClose: $empty, // triggered after the datepicker is closed (destroyed) 76 onSelect: $empty // triggered when a date is selected 77 }, 78 79 initialize: function(attachTo, options) { 80 this.attachTo = attachTo; 81 this.setOptions(options).attach(); 82 if (this.options.timePickerOnly) { 83 this.options.timePicker = true; 84 this.options.startView = 'time'; 85 } 86 this.formatMinMaxDates(); 87 document.addEvent('mousedown', this.close.bind(this)); 88 }, 89 90 formatMinMaxDates: function() { 91 if (this.options.minDate && this.options.minDate.format) { 92 this.options.minDate = this.unformat(this.options.minDate.date, this.options.minDate.format); 93 } 94 if (this.options.maxDate && this.options.maxDate.format) { 95 this.options.maxDate = this.unformat(this.options.maxDate.date, this.options.maxDate.format); 96 this.options.maxDate.setHours(23); 97 this.options.maxDate.setMinutes(59); 98 this.options.maxDate.setSeconds(59); 99 } 100 }, 101 102 attach: function() { 103 // toggle the datepicker through a separate element? 104 if ($chk(this.options.toggleElements)) { 105 var togglers = $$(this.options.toggleElements); 106 document.addEvents({ 107 'keydown': function(e) { 108 if (e.key == "tab") { 109 this.close(null, true); 110 } 111 }.bind(this) 112 }); 113 }; 114 115 // attach functionality to the inputs 116 $$(this.attachTo).each(function(item, index) { 117 118 // never double attach 119 if (item.retrieve('datepicker')) return; 120 121 // determine starting value(s) 122 if ($chk(item.get('value'))) { 123 var init_clone_val = this.format(new Date(this.unformat(item.get('value'), this.options.inputOutputFormat)), this.options.format); 124 } else if (!this.options.allowEmpty) { 125 var init_clone_val = this.format(new Date(), this.options.format); 126 //max hack 127 var init_orig_val = this.format(new Date(), this.options.inputOutputFormat); 128 item.set('value', init_orig_val); 129 //end max hack 130 } else { 131 var init_clone_val = ''; 132 } 133 134 // create clone 135 var display = item.getStyle('display'); 136 //max hack for date validation 137 if($chk(item.get('id'))){ 138 var new_id = item.get('id')+'_clone_id'; 139 }else{ 140 var new_id = item.get('name')+'_clone_id'; 141 } 142 //max hack for date validation 143 var display = item.getStyle('display'); 144 var clone = item 145 .setStyle('display', this.options.debug ? display : 'none') 146 .store('datepicker', true) // to prevent double attachment... 147 .clone() 148 .set('id', new_id)//max hack for date validation 149 .store('datepicker', true) // ...even for the clone (!) 150 .removeProperty('name') // secure clean (form)submission 151 .setStyle('display', display) 152 .set('value', init_clone_val) 153 .inject(item, 'after'); 154 155 // events 156 if ($chk(this.options.toggleElements)) { 157 togglers[index] 158 .setStyle('cursor', 'pointer') 159 .addEvents({ 160 'click': function(e) { 161 this.onFocus(item, clone); 162 }.bind(this) 163 }); 164 clone.addEvents({ 165 'blur': function() { 166 item.set('value', clone.get('value')); 167 } 168 }); 169 } else { 170 clone.addEvents({ 171 'keydown': function(e) { 172 if (this.options.allowEmpty && (e.key == "delete" || e.key == "backspace")) { 173 item.set('value', ''); 174 e.target.set('value', ''); 175 this.close(null, true); 176 } else if (e.key == "tab") { 177 this.close(null, true); 178 } else { 179 e.stop(); 180 } 181 }.bind(this),
182 'focus': function(e) { 183 this.onFocus(item, clone); 184 }.bind(this) 185 }); 186 } 187 }.bind(this)); 188 }, 189 190 onFocus: function(original_input, visual_input) { 191 var init_visual_date, d = visual_input.getCoordinates(); 192 193 if ($chk(original_input.get('value'))) { 194 init_visual_date = this.unformat(original_input.get('value'), this.options.inputOutputFormat).valueOf(); 195 } else { 196 init_visual_date = new Date(); 197 if ($chk(this.options.maxDate) && init_visual_date.valueOf() > this.options.maxDate.valueOf()) { 198 init_visual_date = new Date(this.options.maxDate.valueOf()); 199 } 200 if ($chk(this.options.minDate) && init_visual_date.valueOf() < this.options.minDate.valueOf()) { 201 init_visual_date = new Date(this.options.minDate.valueOf()); 202 } 203 } 204 205 this.show({ left: d.left + this.options.positionOffset.x, top: d.top + d.height + this.options.positionOffset.y }, init_visual_date); 206 this.input = original_input; 207 this.visual = visual_input; 208 this.options.onShow(); 209 }, 210 211 dateToObject: function(d) { 212 return { 213 year: d.getFullYear(), 214 month: d.getMonth(), 215 day: d.getDate(), 216 hours: d.getHours(), 217 minutes: d.getMinutes(), 218 seconds: d.getSeconds() 219 }; 220 }, 221 222 dateFromObject: function(values) { 223 var d = new Date(); 224 d.setDate(1); 225 ['year', 'month', 'day', 'hours', 'minutes', 'seconds'].each(function(type) { 226 var v = values[type]; 227 if (!$chk(v)) return; 228 switch (type) { 229 case 'day': d.setDate(v); break; 230 case 'month': d.setMonth(v); break; 231 case 'year': d.setFullYear(v); break; 232 case 'hours': d.setHours(v); break; 233 case 'minutes': d.setMinutes(v); break; 234 case 'seconds': d.setSeconds(v); break; 235 } 236 }); 237 return d; 238 }, 239 240 show: function(position, timestamp) { 241 this.formatMinMaxDates(); 242 if ($chk(timestamp)) { 243 this.d = new Date(timestamp); 244 } else { 245 this.d = new Date(); 246 } 247 this.today = new Date(); 248 this.choice = this.dateToObject(this.d); 249 this.mode = (this.options.startView == 'time' && !this.options.timePicker) ? 'month' : this.options.startView; 250 this.render(); 251 this.picker.setStyles(position); 252 }, 253 254 render: function(fx) { 255 if (!$chk(this.picker)) { 256 this.constructPicker(); 257 } else { 258 // swap contents so we can fill the newContents again and animate 259 var o = this.oldContents; 260 this.oldContents = this.newContents; 261 this.newContents = o; 262 this.newContents.empty(); 263 } 264 265 // remember current working date 266 var startDate = new Date(this.d.getTime()); 267 268 // intially assume both left and right are allowed 269 this.limit = { right: false, left: false }; 270 271 // render! booty! 272 if (this.mode == 'decades') { 273 this.renderDecades(); 274 } else if (this.mode == 'year') { 275 this.renderYear(); 276 } else if (this.mode == 'time') { 277 this.renderTime(); 278 this.limit = { right: true, left: true }; // no left/right in timeview 279 } else { 280 this.renderMonth(); 281 } 282 283 this.picker.getElement('.previous').setStyle('visibility', this.limit.left ? 'hidden' : 'visible'); 284 this.picker.getElement('.next').setStyle('visibility', this.limit.right ? 'hidden' : 'visible'); 285 this.picker.getElement('.titleText').setStyle('cursor', this.allowZoomOut() ? 'pointer' : 'default'); 286 287 // restore working date 288 this.d = startDate; 289 290 // if ever the opacity is set to '0' it was only to have us fade it in here 291 // refer to the constructPicker() function, which instantiates the picker at opacity 0 when fading is desired 292 if (this.picker.getStyle('opacity') == 0) { 293 this.picker.tween('opacity', 0, 1); 294 } 295 296 // animate 297 if ($chk(fx)) this.fx(fx); 298 }, 299 300 fx: function(fx) { 301 if (fx == 'right') { 302 this.oldContents.setStyles({ left: 0, opacity: 1 }); 303 this.newContents.setStyles({ left: this.bodysize.x, opacity: 1 }); 304 this.slider.setStyle('left', 0).tween('left', 0, -this.bodysize.x); 305 } else if (fx == 'left') { 306 this.oldContents.setStyles({ left: this.bodysize.x, opacity: 1 }); 307 this.newContents.setStyles({ left: 0, opacity: 1 }); 308 this.slider.setStyle('left', -this.bodysize.x).tween('left', -this.bodysize.x, 0); 309 } else if (fx == 'fade') { 310 this.slider.setStyle('left', 0); 311 this.oldContents.setStyle('left', 0).set('tween', { duration: this.options.animationDuration / 2 }
311).tween('opacity', 1, 0); 312 this.newContents.setStyles({ opacity: 0, left: 0}).set('tween', { duration: this.options.animationDuration }).tween('opacity', 0, 1); 313 } 314 }, 315 316 constructPicker: function() { 317 this.picker = new Element('div', { 'class': this.options.pickerClass }).inject(document.body); 318 if (this.options.useFadeInOut) { 319 this.picker.setStyle('opacity', 0).set('tween', { duration: this.options.animationDuration }); 320 } 321 322 var h = new Element('div', { 'class': 'header' }).inject(this.picker); 323 var titlecontainer = new Element('div', { 'class': 'title' }).inject(h); 324 new Element('div', { 'class': 'previous' }).addEvent('click', this.previous.bind(this)).set('text', '«').inject(h); 325 new Element('div', { 'class': 'next' }).addEvent('click', this.next.bind(this)).set('text', '»').inject(h); 326 new Element('div', { 'class': 'closeButton' }).addEvent('click', this.close.bindWithEvent(this, true)).set('text', 'x').inject(h); 327 new Element('span', { 'class': 'titleText' }).addEvent('click', this.zoomOut.bind(this)).inject(titlecontainer); 328 329 var b = new Element('div', { 'class': 'body' }).inject(this.picker); 330 this.bodysize = b.getSize(); 331 this.slider = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: 2 * this.bodysize.x, height: this.bodysize.y }}) 332 .set('tween', { duration: this.options.animationDuration, transition: Fx.Transitions.Quad.easeInOut }).inject(b); 333 this.oldContents = new Element('div', { styles: { position: 'absolute', top: 0, left: this.bodysize.x, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider); 334 this.newContents = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider); 335 }, 336 337 renderTime: function() { 338 var container = new Element('div', { 'class': 'time' }).inject(this.newContents); 339 340 if (this.options.timePickerOnly) { 341 this.picker.getElement('.titleText').set('text', 'Select a time'); 342 } else { 343 this.picker.getElement('.titleText').set('text', this.format(this.d, 'j M, Y')); 344 } 345 346 new Element('input', { type: 'text', 'class': 'hour' }) 347 .set('value', this.leadZero(this.d.getHours())) 348 .addEvents({ 349 mousewheel: function(e) { 350 var i = e.target, v = i.get('value').toInt(); 351 i.focus(); 352 if (e.wheel > 0) { 353 v = (v < 23) ? v + 1 : 0; 354 } else { 355 v = (v > 0) ? v - 1 : 23; 356 } 357 i.set('value', this.leadZero(v)); 358 e.stop(); 359 }.bind(this) 360 }) 361 .set('maxlength', 2) 362 .inject(container); 363 364 new Element('input', { type: 'text', 'class': 'minutes' }) 365 .set('value', this.leadZero(this.d.getMinutes())) 366 .addEvents({ 367 mousewheel: function(e) { 368 var i = e.target, v = i.get('value').toInt(); 369 i.focus(); 370 if (e.wheel > 0) { 371 v = (v < 59) ? v + 1 : 0; 372 } else { 373 v = (v > 0) ? v - 1 : 59; 374 } 375 i.set('value', this.leadZero(v)); 376 e.stop(); 377 }.bind(this) 378 }) 379 .set('maxlength', 2) 380 .inject(container); 381 382 new Element('div', { 'class': 'separator' }).set('text', ':').inject(container); 383 384 new Element('input', { type: 'submit', value: 'OK', 'class': 'ok' }) 385 .addEvents({ 386 click: function(e) { 387 e.stop(); 388 this.select($merge(this.dateToObject(this.d), { hours: this.picker.getElement('.hour').get('value').toInt(), minutes: this.picker.getElement('.minutes').get('value').toInt() })); 389 }.bind(this) 390 }) 391 .set('maxlength', 2) 392 .inject(container); 393 }, 394 395 renderMonth: function() { 396 var month = this.d.getMonth(); 397 398 this.picker.getElement('.titleText').set('text', this.options.months[month] + ' ' + this.d.getFullYear()); 399 400 this.d.setDate(1); 401 while (this.d.getDay() != this.options.startDay) { 402 this.d.setDate(this.d.getDate() - 1); 403 } 404 405 var container = new Element('div', { 'class': 'days' }).inject(this.newContents); 406 var titles = new Element('div', { 'class': 'titles' }).inject(container); 407 var d, i, classes, e, weekcontainer; 408 409 for (d = this.options.startDay; d < (this.options.startDay + 7); d++) {
410 new Element('div', { 'class': 'title day day' + (d % 7) }).set('text', this.options.days[(d % 7)].substring(0,this.options.dayShort)).inject(titles); 411 } 412 413 var available = false; 414 var t = this.today.toDateString(); 415 var currentChoice = this.dateFromObject(this.choice).toDateString(); 416 417 for (i = 0; i < 42; i++) { 418 classes = []; 419 classes.push('day'); 420 classes.push('day'+this.d.getDay()); 421 if (this.d.toDateString() == t) classes.push('today'); 422 if (this.d.toDateString() == currentChoice) classes.push('selected'); 423 if (this.d.getMonth() != month) classes.push('otherMonth'); 424 425 if (i % 7 == 0) { 426 weekcontainer = new Element('div', { 'class': 'week week'+(Math.floor(i/7)) }).inject(container); 427 } 428 429 e = new Element('div', { 'class': classes.join(' ') }).set('text', this.d.getDate()).inject(weekcontainer); 430 if (this.limited('date')) { 431 e.addClass('unavailable'); 432 if (available) { 433 this.limit.right = true; 434 } else if (this.d.getMonth() == month) { 435 this.limit.left = true; 436 } 437 } else { 438 available = true; 439 e.addEvent('click', function(e, d) { 440 if (this.options.timePicker) { 441 this.d.setDate(d.day); 442 this.d.setMonth(d.month); 443 this.mode = 'time'; 444 this.render('fade'); 445 } else { 446 this.select(d); 447 } 448 }.bindWithEvent(this, { day: this.d.getDate(), month: this.d.getMonth(), year: this.d.getFullYear() })); 449 } 450 this.d.setDate(this.d.getDate() + 1); 451 } 452 if (!available) this.limit.right = true; 453 }, 454 455 renderYear: function() { 456 var month = this.today.getMonth(); 457 var thisyear = this.d.getFullYear() == this.today.getFullYear(); 458 var selectedyear = this.d.getFullYear() == this.choice.year; 459 460 this.picker.getElement('.titleText').set('text', this.d.getFullYear()); 461 this.d.setMonth(0); 462 463 var i, e; 464 var available = false; 465 var container = new Element('div', { 'class': 'months' }).inject(this.newContents); 466 467 for (i = 0; i <= 11; i++) { 468 e = new Element('div', { 'class': 'month month'+(i+1)+(i == month && thisyear ? ' today' : '')+(i == this.choice.month && selectedyear ? ' selected' : '') }) 469 .set('text', this.options.monthShort ? this.options.months[i].substring(0, this.options.monthShort) : this.options.months[i]).inject(container); 470 471 if (this.limited('month')) { 472 e.addClass('unavailable'); 473 if (available) { 474 this.limit.right = true; 475 } else { 476 this.limit.left = true; 477 } 478 } else { 479 available = true; 480 e.addEvent('click', function(e, d) { 481 this.d.setDate(1); 482 this.d.setMonth(d); 483 this.mode = 'month'; 484 this.render('fade'); 485 }.bindWithEvent(this, i)); 486 } 487 this.d.setMonth(i); 488 } 489 if (!available) this.limit.right = true; 490 }, 491 492 renderDecades: function() { 493 // start neatly at interval (eg. 1980 instead of 1987) 494 while (this.d.getFullYear() % this.options.yearsPerPage > 0) { 495 this.d.setFullYear(this.d.getFullYear() - 1); 496 } 497 498 this.picker.getElement('.titleText').set('text', this.d.getFullYear() + '-' + (this.d.getFullYear() + this.options.yearsPerPage - 1)); 499 500 var i, y, e; 501 var available = false; 502 var container = new Element('div', { 'class': 'years' }).inject(this.newContents); 503 504 if ($chk(this.options.minDate) && this.d.getFullYear() <= this.options.minDate.getFullYear()) { 505 this.limit.left = true; 506 } 507 508 for (i = 0; i < this.options.yearsPerPage; i++) { 509 y = this.d.getFullYear(); 510 e = new Element('div', { 'class': 'year year' + i + (y == this.today.getFullYear() ? ' today' : '') + (y == this.choice.year ? ' selected' : '') }).set('text', y).inject(container); 511 512 if (this.limited('year')) { 513 e.addClass('unavailable'); 514 if (available) { 515 this.limit.right = true; 516 } else { 517 this.limit.left = true; 518 } 519 } else { 520 available = true; 521 e.addEvent('click', function(e, d) { 522 this.d.setFullYear(d); 523 this.mode = 'year'; 524 this.render('fade'); 525 }.bindWithEvent(this, y)); 526 } 527 this.d.setFullYear(this.d.getFullYear() + 1); 528 } 529 if (!available) { 530 this.limit.right = true; 531 } 532 if ($chk(this.options.maxDate) && this.d.getFullYear() >= this.options.maxDate.getFullYear()) { 533 this.limit.right = true; 534 } 535 }, 536 537 limited: function(type) { 538 var cs = $chk(this.options.minDate); 539 var ce = $chk(this.options.maxDate); 540 if (!cs && !ce) return false;
541 542 switch (type) { 543 case 'year': 544 return (cs && this.d.getFullYear() < this.options.minDate.getFullYear()) || (ce && this.d.getFullYear() > this.options.maxDate.getFullYear()); 545 546 case 'month': 547 // todo: there has got to be an easier way...? 548 var ms = ('' + this.d.getFullYear() + this.leadZero(this.d.getMonth())).toInt(); 549 return cs && ms < ('' + this.options.minDate.getFullYear() + this.leadZero(this.options.minDate.getMonth())).toInt() 550 || ce && ms > ('' + this.options.maxDate.getFullYear() + this.leadZero(this.options.maxDate.getMonth())).toInt() 551 552 case 'date': 553 return (cs && this.d < this.options.minDate) || (ce && this.d > this.options.maxDate); 554 } 555 }, 556 557 allowZoomOut: function() { 558 if (this.mode == 'time' && this.options.timePickerOnly) return false; 559 if (this.mode == 'decades') return false; 560 if (this.mode == 'year' && !this.options.yearPicker) return false; 561 return true; 562 }, 563 564 zoomOut: function() { 565 if (!this.allowZoomOut()) return; 566 if (this.mode == 'year') { 567 this.mode = 'decades'; 568 } else if (this.mode == 'time') { 569 this.mode = 'month'; 570 } else { 571 this.mode = 'year'; 572 } 573 this.render('fade'); 574 }, 575 576 previous: function() { 577 if (this.mode == 'decades') { 578 this.d.setFullYear(this.d.getFullYear() - this.options.yearsPerPage); 579 } else if (this.mode == 'year') { 580 this.d.setFullYear(this.d.getFullYear() - 1); 581 } else if (this.mode == 'month') { 582 this.d.setDate(1); 583 this.d.setMonth(this.d.getMonth() - 1); 584 } 585 this.render('left'); 586 }, 587 588 next: function() { 589 if (this.mode == 'decades') { 590 this.d.setFullYear(this.d.getFullYear() + this.options.yearsPerPage); 591 } else if (this.mode == 'year') { 592 this.d.setFullYear(this.d.getFullYear() + 1); 593 } else if (this.mode == 'month') { 594 this.d.setDate(1); 595 this.d.setMonth(this.d.getMonth() + 1); 596 } 597 this.render('right'); 598 }, 599 600 close: function(e, force) { 601 if (!$(this.picker)) return; 602 var clickOutside = ($chk(e) && e.target != this.picker && !this.picker.hasChild(e.target) && e.target != this.visual); 603 if (force || clickOutside) { 604 if (this.options.useFadeInOut) { 605 this.picker.set('tween', { duration: this.options.animationDuration / 2, onComplete: this.destroy.bind(this) }).tween('opacity', 1, 0); 606 } else { 607 this.destroy(); 608 } 609 } 610 }, 611 612 destroy: function() { 613 this.picker.destroy(); 614 this.picker = null; 615 this.options.onClose(); 616 }, 617 618 select: function(values) { 619 this.choice = $merge(this.choice, values); 620 var d = this.dateFromObject(this.choice); 621 this.input.set('value', this.format(d, this.options.inputOutputFormat)); 622 this.visual.set('value', this.format(d, this.options.format)); 623 this.options.onSelect(d); 624 this.close(null, true); 625 }, 626 627 leadZero: function(v) { 628 return v < 10 ? '0'+v : v; 629 }, 630 631 format: function(t, format) { 632 var f = ''; 633 var h = t.getHours(); 634 var m = t.getMonth(); 635 636 for (var i = 0; i < format.length; i++) { 637 switch(format.charAt(i)) { 638 case '\\': i++; f+= format.charAt(i); break; 639 case 'y': f += (t.getFullYear() + '').substring(2); break; 640 case 'Y': f += t.getFullYear(); break; 641 case 'm': f += this.leadZero(m + 1); break; 642 case 'n': f += (m + 1); break; 643 case 'M': f += this.options.months[m].substring(0,this.options.monthShort); break; 644 case 'F': f += this.options.months[m]; break; 645 case 'd': f += this.leadZero(t.getDate()); break; 646 case 'j': f += t.getDate(); break; 647 case 'D': f += this.options.days[t.getDay()].substring(0,this.options.dayShort); break; 648 case 'l': f += this.options.days[t.getDay()]; break; 649 case 'G': f += h; break; 650 case 'H': f += this.leadZero(h); break; 651 case 'g': f += (h % 12 ? h % 12 : 12); break; 652 case 'h': f += this.leadZero(h % 12 ? h % 12 : 12); break; 653 case 'a': f += (h > 11 ? 'pm' : 'am'); break; 654 case 'A': f += (h > 11 ? 'PM' : 'AM'); break; 655 case 'i': f += this.leadZero(t.getMinutes()); break; 656 case 's': f += this.leadZero(t.getSeconds()); break; 657 case 'U': f += Math.floor(t.valueOf() / 1000); break; 658 default: f += format.charAt(i); 659 } 660 } 661 return f; 662 }, 663 664 unformat: function(t, format) { 665 var d = new Date(); 666 d.setMonth(0); 667 d.setDate(1); 668 var a = {}; 669 var c, m; 670 t = t.toString(); 671 672 for (var i = 0; i < format.length; i++) { 673 c = format.charAt(i); 674 switch(c) { 675 case '\\': r = null; i++; break; 676 case 'y': r = '[0-9]{2}'; break; 677 case 'Y': r = '[0-9]{4}'; break; 678 case 'm': r = '0[1-9]|1[012]'; break; 679 case 'n': r = '[1-9]|1[012]'; break; 680 case 'M': r = '[A-Za-z]{'+this.options.monthShort+'}'; break; 681 case 'F': r = '[A-Za-z]+'; break; 682 case 'd': r = '0[1-9]|[12][0-9]|3[01]'; break; 683 case 'j': r = '[12][0-9]|3[01]|[1-9]'; break; 684 case 'D': r = '[A-Za-z]{'+this.options.dayShort+'}'; break; 685 case 'l': r = '[A-Za-z]+'; break; 686 case 'G': 687 case 'H': 688 case 'g': 689 case 'h': r = '[0-9]{1,2}'; break; 690 case 'a': r = '(am|pm)'; break; 691 case 'A': r = '(AM|PM)'; break; 692 case 'i': 693 case 's': r = '[012345][0-9]'; break; 694 case 'U': r = '-?[0-9]+$'; break; 695 default: r = null; 696 } 697 698 if ($chk(r)) { 699 m = t.match('^'+r); 700 if ($chk(m)) { 701 a[c] = m[0]; 702 t = t.substring(a[c].length); 703 } else { 704 if (this.options.debug) alert("Fatal Error in DatePicker\n\nUnexpected format at: '"+t+"' expected format character '"+c+"' (pattern '"+r+"')"); 705 return d; 706 } 707 } else { 708 t = t.substring(1); 709 } 710 } 711 712 for (c in a) { 713 var v = a[c]; 714 switch(c) { 715 case 'y': d.setFullYear(v < 30 ? 2000 + v.toInt() : 1900 + v.toInt()); break; // assume between 1930 - 2029
716 case 'Y': d.setFullYear(v); break; 717 case 'm': 718 case 'n': d.setMonth(v - 1); break; 719 // FALL THROUGH NOTICE! "M" has no break, because "v" now is the full month (eg. 'February'), which will work with the next format "F": 720 case 'M': v = this.options.months.filter(function(item, index) { return item.substring(0,this.options.monthShort) == v }.bind(this))[0]; 721 case 'F': d.setMonth(this.options.months.indexOf(v)); break; 722 case 'd': 723 case 'j': d.setDate(v); break; 724 case 'G': 725 case 'H': d.setHours(v); break; 726 case 'g': 727 case 'h': if (a['a'] == 'pm' || a['A'] == 'PM') { d.setHours(v == 12 ? 0 : v.toInt() + 12); } else { d.setHours(v); } break; 728 case 'i': d.setMinutes(v); break; 729 case 's': d.setSeconds(v); break; 730 case 'U': d = new Date(v.toInt() * 1000); 731 } 732 }; 733 734 return d; 735 } 736});
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.