vendor: 43,975 bytes, lines 1-1300
1/*! 2 * Pikaday 3 * 4 * Copyright © 2014 David Bushell | BSD & MIT license | https://github.com/Pikaday/Pikaday 5 */ 6 7(function (root, factory) 8{ 9 'use strict'; 10 11 var moment; 12 if (typeof exports === 'object') { 13 // CommonJS module 14 // Load moment.js as an optional dependency 15 try { moment = require('moment'); } catch (e) {} 16 module.exports = factory(moment); 17 } else if (typeof define === 'function' && define.amd) { 18 // AMD. Register as an anonymous module. 19 define(function (req) 20 { 21 // Load moment.js as an optional dependency 22 var id = 'moment'; 23 try { moment = req(id); } catch (e) {} 24 return factory(moment); 25 }); 26 } else { 27 root.Pikaday = factory(root.moment); 28 } 29}(this, function (moment) 30{ 31 'use strict'; 32 33 /** 34 * feature detection and helper functions 35 */ 36 var hasMoment = typeof moment === 'function', 37 38 hasEventListeners = !!window.addEventListener, 39 40 document = window.document, 41 42 sto = window.setTimeout, 43 44 addEvent = function(el, e, callback, capture) 45 { 46 if (hasEventListeners) { 47 el.addEventListener(e, callback, !!capture); 48 } else { 49 el.attachEvent('on' + e, callback); 50 } 51 }, 52 53 removeEvent = function(el, e, callback, capture) 54 { 55 if (hasEventListeners) { 56 el.removeEventListener(e, callback, !!capture); 57 } else { 58 el.detachEvent('on' + e, callback); 59 } 60 }, 61 62 trim = function(str) 63 { 64 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,''); 65 }, 66 67 hasClass = function(el, cn) 68 { 69 return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1; 70 }, 71 72 addClass = function(el, cn) 73 { 74 if (!hasClass(el, cn)) { 75 el.className = (el.className === '') ? cn : el.className + ' ' + cn; 76 } 77 }, 78 79 removeClass = function(el, cn) 80 { 81 el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' ')); 82 }, 83 84 isArray = function(obj) 85 { 86 return (/Array/).test(Object.prototype.toString.call(obj)); 87 }, 88 89 isDate = function(obj) 90 { 91 return (/Date/).test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime()); 92 }, 93 94 isWeekend = function(date) 95 { 96 var day = date.getDay(); 97 return day === 0 || day === 6; 98 }, 99 100 isLeapYear = function(year) 101 { 102 // solution lifted from date.js (MIT license): https://github.com/datejs/Datejs 103 return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); 104 }, 105 106 getDaysInMonth = function(year, month) 107 { 108 return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 109 }, 110 111 setToStartOfDay = function(date) 112 { 113 if (isDate(date)) date.setHours(0,0,0,0); 114 }, 115 116 compareDates = function(a,b) 117 { 118 // weak date comparison (use setToStartOfDay(date) to ensure correct result) 119 return a.getTime() === b.getTime(); 120 }, 121 122 extend = function(to, from, overwrite) 123 { 124 var prop, hasProp; 125 for (prop in from) { 126 hasProp = to[prop] !== undefined; 127 if (hasProp && typeof from[prop] === 'object' && from[prop] !== null && from[prop].nodeName === undefined) { 128 if (isDate(from[prop])) { 129 if (overwrite) { 130 to[prop] = new Date(from[prop].getTime()); 131 } 132 } 133 else if (isArray(from[prop])) { 134 if (overwrite) { 135 to[prop] = from[prop].slice(0); 136 } 137 } else { 138 to[prop] = extend({}, from[prop], overwrite); 139 } 140 } else if (overwrite || !hasProp) { 141 to[prop] = from[prop]; 142 } 143 } 144 return to; 145 }, 146 147 fireEvent = function(el, eventName, data) 148 { 149 var ev; 150 151 if (document.createEvent) { 152 ev = document.createEvent('HTMLEvents'); 153 ev.initEvent(eventName, true, false); 154 ev = extend(ev, data); 155 el.dispatchEvent(ev); 156 } else if (document.createEventObject) { 157 ev = document.createEventObject(); 158 ev = extend(ev, data); 159 el.fireEvent('on' + eventName, ev); 160 } 161 }, 162 163 adjustCalendar = function(calendar) { 164 if (calendar.month < 0) { 165 calendar.year -= Math.ceil(Math.abs(calendar.month)/12); 166 calendar.month += 12; 167 } 168 if (calendar.month > 11) { 169 calendar.year += Math.floor(Math.abs(calendar.month)/12); 170 calendar.month -= 12; 171 } 172 return calendar; 173 }, 174 175 /** 176 * defaults and localisation 177 */ 178 defaults = { 179 180 // bind the picker to a form field 181 field: null, 182 183 // automatically show/hide the picker on `field` focus (default `true` if `field` is set) 184 bound: undefined, 185 186 // data-attribute on the input field with an aria assistance text (only applied when `bound` is set) 187 ariaLabel: 'Use the arrow keys to pick a date', 188 189 // position of the datepicker, relative to the field (default to bottom & left) 190 // ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position) 191 position: 'bottom left', 192 193 // automatically fit in the viewport even if it means repositioning from the position option 194 reposition: true, 195 196 // the default output format for `.toString()` and `field` value 197 format: 'YYYY-MM-DD', 198 199 // the toString function which gets passed a current date object and format 200 // and returns a string 201 toString: null, 202 203 // used to create date object from current input string 204 parse: null, 205 206 // the initial date to view when first opened 207 defaultDate: null, 208 209 // make the `defaultDate` the initial selected value 210 setDefaultDate: false, 211 212 // first day of week (0: Sunday, 1: Monday etc) 213 firstDay: 0, 214 215 // minimum number of days in the week that gets week number one 216 // default ISO 8601, week 01 is the week with the first Thursday (4) 217 firstWeekOfYearMinDays: 4, 218 219 // the default flag for moment's strict date parsing 220 formatStrict: false, 221 222 // the minimum/earliest date that can be selected 223 minDate: null, 224 // the maximum/latest date that can be selected 225 maxDate: null, 226 227 // number of years either side, or array of upper/lower range 228 yearRange: 10, 229 230 // show week numbers at head of row 231 showWeekNumber: false, 232 233 // Week picker mode 234 pickWholeWeek: false, 235 236 // used internally (don't config outside) 237 minYear: 0, 238 maxYear: 9999, 239 minMonth: undefined, 240 maxMonth: undefined, 241 242 startRange: null, 243 endRange: null, 244 245 isRTL: false, 246 247 // Additional text to append to the year in the calendar title 248 yearSuffix: '', 249 250 // Render the month after year in the calendar title 251 showMonthAfterYear: false, 252 253 // Render days of the calendar grid that fall in the next or previous month 254 showDaysInNextAndPreviousMonths: false, 255 256 // Allows user to select days that fall in the next or previous month 257 enableSelectionDaysInNextAndPreviousMonths: false, 258 259 // how many months are visible 260 numberOfMonths: 1, 261 262 // when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`) 263 // only used for the first display or when a selected date is not visible 264 mainCalendar: 'left', 265 266 // Specify a DOM element to render the calendar in 267 container: undefined, 268 269 // Blur field when date is selected 270 blurFieldOnSelect : true, 271 272 // internationalization 273 i18n: { 274 previousMonth : 'Mois prochain', 275 nextMonth : 'Mois précédent', 276 months : ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'], 277 weekdays : ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], 278 weekdaysShort : ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'] 279 }, 280 281 // Theme Classname 282 theme: null, 283 284 // events array 285 events: [], 286 287 // callback function 288 onSelect: null, 289 onOpen: null, 290 onClose: null, 291 onDraw: null, 292 293 // Enable keyboard input 294 keyboardInput: true 295 }, 296 297 298 /** 299 * templating functions to abstract HTML rendering 300 */ 301 renderDayName = function(opts, day, abbr) 302 { 303 day += opts.firstDay; 304 while (day >= 7) { 305 day -= 7; 306 } 307 return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day]; 308 }, 309 310 renderDay = function(opts) 311 { 312 var arr = []; 313 var ariaSelected = 'false'; 314 if (opts.isEmpty) { 315 if (opts.showDaysInNextAndPreviousMonths) { 316 arr.push('is-outside-current-month'); 317 318 if(!opts.enableSelectionDaysInNextAndPreviousMonths) { 319 arr.push('is-selection-disabled'); 320 } 321 322 } else { 323 return '<td class="is-empty"></td>'; 324 } 325 } 326 if (opts.isDisabled) { 327 arr.push('is-disabled'); 328 } 329 if (opts.isToday) { 330 arr.push('is-today'); 331 } 332 if (opts.isSelected) { 333 arr.push('is-selected'); 334 ariaSelected = 'true'; 335 } 336 if (opts.hasEvent) { 337 arr.push('has-event'); 338 } 339 if (opts.isInRange) { 340 arr.push('is-inrange'); 341 } 342 if (opts.isStartRange) { 343 arr.push('is-startrange'); 344 } 345 if (opts.isEndRange) { 346 arr.push('is-endrange'); 347 } 348 return '<td data-day="' + opts.day + '" class="' + arr.join(' ') + '" aria-selected="' + ariaSelected + '">' + 349 '<button class="pika-button pika-day" type="button" ' + 350 'data-pika-year="' + opts.year + '" data-pika-month="' + opts.month + '" data-pika-day="' + opts.day + '">' + 351 opts.day + 352 '</button>' + 353 '</td>'; 354 }, 355 356 isoWeek = function(date, firstWeekOfYearMinDays) { 357 // Ensure we're at the start of the day. 358 date.setHours(0, 0, 0, 0); 359 360 // Thursday in current week decides the year because January 4th 361 // is always in the first week according to ISO8601. 362 var yearDay = date.getDate(), 363 weekDay = date.getDay(), 364 dayInFirstWeek = firstWeekOfYearMinDays, 365 dayShift = dayInFirstWeek - 1, // counting starts at 0 366 daysPerWeek = 7, 367 prevWeekDay = function(day) { return (day + daysPerWeek - 1) % daysPerWeek; }; 368 369 // Adjust to Thursday in week 1 and count number of weeks from date to week 1. 370 date.setDate(yearDay + dayShift - prevWeekDay(weekDay)); 371 372 var jan4th = new Date(date.getFullYear(), 0, dayInFirstWeek), 373 msPerDay = 24 * 60 * 60 * 1000, 374 daysBetween = (date.getTime() - jan4th.getTime()) / msPerDay, 375 weekNum = 1 + Math.round((daysBetween - dayShift + prevWeekDay(jan4th.getDay())) / daysPerWeek); 376 377 return weekNum; 378 }, 379 380 renderWeek = function (d, m, y, firstWeekOfYearMinDays) { 381 var date = new Date(y, m, d), 382 week = hasMoment ? moment(date).isoWeek() : isoWeek(date, firstWeekOfYearMinDays); 383 384 return '<td class="pika-week">' + week + '</td>'; 385 }, 386 387 renderRow = function(days, isRTL, pickWholeWeek, isRowSelected) 388 { 389 return '<tr class="pika-row' + (pickWholeWeek ? ' pick-whole-week' : '') + (isRowSelected ? ' is-selected' : '') + '">' + (isRTL ? days.reverse() : days).join('') + '</tr>'; 390 }, 391 392 renderBody = function(rows) 393 { 394 return '<tbody>' + rows.join('') + '</tbody>'; 395 }, 396 397 renderHead = function(opts) 398 { 399 var i, arr = []; 400 if (opts.showWeekNumber) { 401 arr.push('<th></th>'); 402 } 403 for (i = 0; i < 7; i++) { 404 arr.push('<th scope="col"><abbr title="' + renderDayName(opts, i) + '">' + renderDayName(opts, i, true) + '</abbr></th>'); 405 } 406 return '<thead><tr>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</tr></thead>'; 407 }, 408 409 renderTitle = function(instance, c, year, month, refYear, randId) 410 { 411 var i, j, arr, 412 opts = instance._o, 413 isMinYear = year === opts.minYear, 414 isMaxYear = year === opts.maxYear, 415 html = '<div id="' + randId + '" class="pika-title" role="heading" aria-live="assertive">', 416 monthHtml, 417 yearHtml, 418 prev = true, 419 next = true; 420 421 for (arr = [], i = 0; i < 12; i++) { 422 arr.push('<option value="' + (year === refYear ? i - c : 12 + i - c) + '"' + 423 (i === month ? ' selected="selected"': '') + 424 ((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth) ? ' disabled="disabled"' : '') + '>' + 425 opts.i18n.months[i] + '</option>'); 426 } 427 428 monthHtml = '<div class="pika-label">' + opts.i18n.months[month] + '<select class="pika-select pika-select-month" tabindex="-1">' + arr.join('') + '</select></div>'; 429 430 if (isArray(opts.yearRange)) { 431 i = opts.yearRange[0]; 432 j = opts.yearRange[1] + 1; 433 } else { 434 i = year - opts.yearRange; 435 j = 1 + year + opts.yearRange; 436 } 437 438 for (arr = []; i < j && i <= opts.maxYear; i++) { 439 if (i >= opts.minYear) { 440 arr.push('<option value="' + i + '"' + (i === year ? ' selected="selected"': '') + '>' + (i) + '</option>'); 441 } 442 } 443 yearHtml = '<div class="pika-label">' + year + opts.yearSuffix + '<select class="pika-select pika-select-year" tabindex="-1">' + arr.join('') + '</select></div>'; 444 445 if (opts.showMonthAfterYear) { 446 html += yearHtml + monthHtml; 447 } else { 448 html += monthHtml + yearHtml; 449 } 450 451 if (isMinYear && (month === 0 || opts.minMonth >= month)) { 452 prev = false; 453 } 454 455 if (isMaxYear && (month === 11 || opts.maxMonth <= month)) { 456 next = false; 457 } 458 459 if (c === 0) { 460 html += '<button class="pika-prev' + (prev ? '' : ' is-disabled') + '" type="button">' + opts.i18n.previousMonth + '</button>'; 461 } 462 if (c === (instance._o.numberOfMonths - 1) ) { 463 html += '<button class="pika-next' + (next ? '' : ' is-disabled') + '" type="button">' + opts.i18n.nextMonth + '</button>'; 464 } 465 466 return html += '</div>'; 467 }, 468 469 renderTable = function(opts, data, randId) 470 { 471 return '<table cellpadding="0" cellspacing="0" class="pika-table" role="grid" aria-labelledby="' + randId + '">' + renderHead(opts) + renderBody(data) + '</table>'; 472 }, 473 474 475 /** 476 * Pikaday constructor 477 */ 478 Pikaday = function(options) 479 { 480 var self = this, 481 opts = self.config(options); 482 483 self._onMouseDown = function(e) 484 { 485 if (!self._v) { 486 return; 487 } 488 e = e || window.event; 489 var target = e.target || e.srcElement; 490 if (!target) { 491 return; 492 } 493 494 if (!hasClass(target, 'is-disabled')) { 495 if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty') && !hasClass(target.parentNode, 'is-disabled')) { 496 self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day'))); 497 if (opts.bound) { 498 sto(function() { 499 self.hide(); 500 if (opts.blurFieldOnSelect && opts.field) { 501 opts.field.blur(); 502 } 503 }, 100); 504 } 505 } 506 else if (hasClass(target, 'pika-prev')) { 507 self.prevMonth(); 508 } 509 else if (hasClass(target, 'pika-next')) { 510 self.nextMonth(); 511 } 512 } 513 if (!hasClass(target, 'pika-select')) { 514 // if this is touch event prevent mouse events emulation 515 if (e.preventDefault) { 516 e.preventDefault(); 517 } else { 518 e.returnValue = false; 519 return false; 520 } 521 } else { 522 self._c = true; 523 } 524 }; 525 526 self._onChange = function(e) 527 { 528 e = e || window.event; 529 var target = e.target || e.srcElement; 530 if (!target) { 531 return; 532 } 533 if (hasClass(target, 'pika-select-month')) { 534 self.gotoMonth(target.value); 535 } 536 else if (hasClass(target, 'pika-select-year')) { 537 self.gotoYear(target.value); 538 } 539 }; 540 541 self._onKeyChange = function(e) 542 { 543 e = e || window.event; 544 545 if (self.isVisible()) { 546 547 switch(e.keyCode){ 548 case 13: 549 case 27: 550 if (opts.field) { 551 opts.field.blur(); 552 } 553 break; 554 case 37: 555 self.adjustDate('subtract', 1); 556 break; 557 case 38: 558 self.adjustDate('subtract', 7); 559 break; 560 case 39: 561 self.adjustDate('add', 1); 562 break; 563 case 40: 564 self.adjustDate('add', 7); 565 break; 566 case 8: 567 case 46: 568 self.setDate(null); 569 break; 570 } 571 } 572 }; 573 574 self._parseFieldValue = function() 575 { 576 if (opts.parse) { 577 return opts.parse(opts.field.value, opts.format); 578 } else if (hasMoment) { 579 var date = moment(opts.field.value, opts.format, opts.formatStrict); 580 return (date && date.isValid()) ? date.toDate() : null; 581 } else { 582 return new Date(Date.parse(opts.field.value)); 583 } 584 }; 585 586 self._onInputChange = function(e) 587 { 588 var date; 589 590 if (e.firedBy === self) { 591 return; 592 } 593 date = self._parseFieldValue(); 594 if (isDate(date)) { 595 self.setDate(date); 596 } 597 if (!self._v) { 598 self.show(); 599 } 600 }; 601 602 self._onInputFocus = function() 603 { 604 self.show(); 605 }; 606 607 self._onInputClick = function() 608 { 609 self.show(); 610 }; 611 612 self._onInputBlur = function() 613 { 614 // IE allows pika div to gain focus; catch blur the input field 615 var pEl = document.activeElement; 616 do { 617 if (hasClass(pEl, 'pika-single')) { 618 return; 619 } 620 } 621 while ((pEl = pEl.parentNode)); 622 623 if (!self._c) { 624 self._b = sto(function() { 625 self.hide(); 626 }, 50); 627 } 628 self._c = false; 629 }; 630 631 self._onClick = function(e) 632 { 633 e = e || window.event; 634 var target = e.target || e.srcElement, 635 pEl = target; 636 if (!target) { 637 return; 638 } 639 if (!hasEventListeners && hasClass(target, 'pika-select')) { 640 if (!target.onchange) { 641 target.setAttribute('onchange', 'return;'); 642 addEvent(target, 'change', self._onChange); 643 } 644 } 645 do { 646 if (hasClass(pEl, 'pika-single') || pEl === opts.trigger) { 647 return; 648 } 649 } 650 while ((pEl = pEl.parentNode)); 651 if (self._v && target !== opts.trigger && pEl !== opts.trigger) { 652 self.hide(); 653 } 654 }; 655 656 self.el = document.createElement('div'); 657 self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '') + (opts.theme ? ' ' + opts.theme : ''); 658 659 addEvent(self.el, 'ontouchend', self._onMouseDown, true); 660 addEvent(self.el, 'onclick', self._onMouseDown, true); 661 addEvent(self.el, 'mousedown', self._onMouseDown, true); 662 addEvent(self.el, 'touchend', self._onMouseDown, true); 663 addEvent(self.el, 'change', self._onChange); 664 665 if (opts.keyboardInput) { 666 addEvent(document, 'keydown', self._onKeyChange); 667 } 668 669 if (opts.field) { 670 if (opts.container) { 671 opts.container.appendChild(self.el); 672 } else if (opts.bound) { 673 document.body.appendChild(self.el); 674 } else { 675 opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling); 676 } 677 addEvent(opts.field, 'change', self._onInputChange); 678 679 if (!opts.defaultDate) { 680 opts.defaultDate = self._parseFieldValue(); 681 opts.setDefaultDate = true; 682 } 683 } 684 685 var defDate = opts.defaultDate; 686 687 if (isDate(defDate)) { 688 if (opts.setDefaultDate) { 689 self.setDate(defDate, true); 690 } else { 691 self.gotoDate(defDate); 692 } 693 } else { 694 self.gotoDate(new Date()); 695 } 696 697 if (opts.bound) { 698 this.hide(); 699 self.el.className += ' is-bound'; 700 addEvent(opts.trigger, 'click', self._onInputClick); 701 addEvent(opts.trigger, 'focus', self._onInputFocus); 702 addEvent(opts.trigger, 'blur', self._onInputBlur); 703 } else { 704 this.show(); 705 } 706 }; 707 708 709 /** 710 * public Pikaday API 711 */ 712 Pikaday.prototype = { 713 714 715 /** 716 * configure functionality 717 */ 718 config: function(options) 719 { 720 if (!this._o) { 721 this._o = extend({}, defaults, true); 722 } 723 724 var opts = extend(this._o, options, true); 725 726 opts.isRTL = !!opts.isRTL; 727 728 opts.field = (opts.field && opts.field.nodeName) ? opts.field : null; 729 730 opts.theme = (typeof opts.theme) === 'string' && opts.theme ? opts.theme : null; 731 732 opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field); 733 734 opts.trigger = (opts.trigger && opts.trigger.nodeName) ? opts.trigger : opts.field; 735 736 opts.disableWeekends = !!opts.disableWeekends; 737 738 opts.disableDayFn = (typeof opts.disableDayFn) === 'function' ? opts.disableDayFn : null; 739 740 var nom = parseInt(opts.numberOfMonths, 10) || 1; 741 opts.numberOfMonths = nom > 4 ? 4 : nom; 742 743 if (!isDate(opts.minDate)) { 744 opts.minDate = false; 745 } 746 if (!isDate(opts.maxDate)) { 747 opts.maxDate = false; 748 } 749 if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) { 750 opts.maxDate = opts.minDate = false; 751 } 752 if (opts.minDate) { 753 this.setMinDate(opts.minDate); 754 } 755 if (opts.maxDate) { 756 this.setMaxDate(opts.maxDate); 757 } 758 759 if (isArray(opts.yearRange)) { 760 var fallback = new Date().getFullYear() - 10; 761 opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback; 762 opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback; 763 } else { 764 opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange; 765 if (opts.yearRange > 100) { 766 opts.yearRange = 100; 767 } 768 } 769 770 return opts; 771 }, 772 773 /** 774 * return a formatted string of the current selection (using Moment.js if available) 775 */ 776 toString: function(format) 777 { 778 format = format || this._o.format; 779 if (!isDate(this._d)) { 780 return ''; 781 } 782 if (this._o.toString) { 783 return this._o.toString(this._d, format); 784 } 785 if (hasMoment) { 786 return moment(this._d).format(format); 787 } 788 return this._d.toDateString(); 789 }, 790 791 /** 792 * return a Moment.js object of the current selection (if available) 793 */ 794 getMoment: function() 795 { 796 return hasMoment ? moment(this._d) : null; 797 }, 798 799 /** 800 * set the current selection from a Moment.js object (if available) 801 */ 802 setMoment: function(date, preventOnSelect) 803 { 804 if (hasMoment && moment.isMoment(date)) { 805 this.setDate(date.toDate(), preventOnSelect); 806 } 807 }, 808 809 /** 810 * return a Date object of the current selection 811 */ 812 getDate: function() 813 { 814 return isDate(this._d) ? new Date(this._d.getTime()) : null; 815 }, 816 817 /** 818 * set the current selection 819 */ 820 setDate: function(date, preventOnSelect) 821 { 822 if (!date) { 823 this._d = null; 824 825 if (this._o.field) { 826 this._o.field.value = ''; 827 fireEvent(this._o.field, 'change', { firedBy: this }); 828 } 829 830 return this.draw(); 831 } 832 if (typeof date === 'string') { 833 date = new Date(Date.parse(date)); 834 } 835 if (!isDate(date)) { 836 return; 837 } 838 839 var min = this._o.minDate, 840 max = this._o.maxDate; 841 842 if (isDate(min) && date < min) { 843 date = min; 844 } else if (isDate(max) && date > max) { 845 date = max; 846 } 847 848 this._d = new Date(date.getTime()); 849 setToStartOfDay(this._d); 850 this.gotoDate(this._d); 851 852 if (this._o.field) { 853 this._o.field.value = this.toString(); 854 fireEvent(this._o.field, 'change', { firedBy: this }); 855 } 856 if (!preventOnSelect && typeof this._o.onSelect === 'function') { 857 this._o.onSelect.call(this, this.getDate()); 858 } 859 }, 860 861 /** 862 * clear and reset the date 863 */ 864 clear: function() 865 { 866 this.setDate(null); 867 }, 868 869 /** 870 * change view to a specific date 871 */ 872 gotoDate: function(date) 873 { 874 var newCalendar = true; 875 876 if (!isDate(date)) { 877 return; 878 } 879 880 if (this.calendars) { 881 var firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1), 882 lastVisibleDate = new Date(this.calendars[this.calendars.length-1].year, this.calendars[this.calendars.length-1].month, 1), 883 visibleDate = date.getTime(); 884 // get the end of the month 885 lastVisibleDate.setMonth(lastVisibleDate.getMonth()+1); 886 lastVisibleDate.setDate(lastVisibleDate.getDate()-1); 887 newCalendar = (visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate); 888 } 889 890 if (newCalendar) { 891 this.calendars = [{ 892 month: date.getMonth(), 893 year: date.getFullYear() 894 }]; 895 if (this._o.mainCalendar === 'right') { 896 this.calendars[0].month += 1 - this._o.numberOfMonths; 897 } 898 } 899 900 this.adjustCalendars(); 901 }, 902 903 adjustDate: function(sign, days) { 904 905 var day = this.getDate() || new Date(); 906 var difference = parseInt(days)*24*60*60*1000; 907 908 var newDay; 909 910 if (sign === 'add') { 911 newDay = new Date(day.valueOf() + difference); 912 } else if (sign === 'subtract') { 913 newDay = new Date(day.valueOf() - difference); 914 } 915 916 this.setDate(newDay); 917 }, 918 919 adjustCalendars: function() { 920 this.calendars[0] = adjustCalendar(this.calendars[0]); 921 for (var c = 1; c < this._o.numberOfMonths; c++) { 922 this.calendars[c] = adjustCalendar({ 923 month: this.calendars[0].month + c, 924 year: this.calendars[0].year 925 }); 926 } 927 this.draw(); 928 }, 929 930 gotoToday: function() 931 { 932 this.gotoDate(new Date()); 933 }, 934 935 /** 936 * change view to a specific month (zero-index, e.g. 0: January) 937 */ 938 gotoMonth: function(month) 939 { 940 if (!isNaN(month)) { 941 this.calendars[0].month = parseInt(month, 10); 942 this.adjustCalendars(); 943 } 944 }, 945 946 nextMonth: function() 947 { 948 this.calendars[0].month++; 949 this.adjustCalendars(); 950 }, 951 952 prevMonth: function() 953 { 954 this.calendars[0].month--; 955 this.adjustCalendars(); 956 }, 957 958 /** 959 * change view to a specific full year (e.g. "2012") 960 */ 961 gotoYear: function(year) 962 { 963 if (!isNaN(year)) { 964 this.calendars[0].year = parseInt(year, 10); 965 this.adjustCalendars(); 966 } 967 }, 968 969 /** 970 * change the minDate 971 */ 972 setMinDate: function(value) 973 { 974 if(value instanceof Date) { 975 setToStartOfDay(value); 976 this._o.minDate = value; 977 this._o.minYear = value.getFullYear(); 978 this._o.minMonth = value.getMonth(); 979 } else { 980 this._o.minDate = defaults.minDate; 981 this._o.minYear = defaults.minYear; 982 this._o.minMonth = defaults.minMonth; 983 this._o.startRange = defaults.startRange; 984 } 985 986 this.draw(); 987 }, 988 989 /** 990 * change the maxDate 991 */ 992 setMaxDate: function(value) 993 { 994 if(value instanceof Date) { 995 setToStartOfDay(value); 996 this._o.maxDate = value; 997 this._o.maxYear = value.getFullYear(); 998 this._o.maxMonth = value.getMonth(); 999 } else { 1000 this._o.maxDate = defaults.maxDate; 1001 this._o.maxYear = defaults.maxYear; 1002 this._o.maxMonth = defaults.maxMonth; 1003 this._o.endRange = defaults.endRange; 1004 } 1005 1006 this.draw(); 1007 }, 1008 1009 setStartRange: function(value) 1010 { 1011 this._o.startRange = value; 1012 }, 1013 1014 setEndRange: function(value) 1015 { 1016 this._o.endRange = value; 1017 }, 1018 1019 /** 1020 * refresh the HTML 1021 */ 1022 draw: function(force) 1023 { 1024 if (!this._v && !force) { 1025 return; 1026 } 1027 var opts = this._o, 1028 minYear = opts.minYear, 1029 maxYear = opts.maxYear, 1030 minMonth = opts.minMonth, 1031 maxMonth = opts.maxMonth, 1032 html = '', 1033 randId; 1034 1035 if (this._y <= minYear) { 1036 this._y = minYear; 1037 if (!isNaN(minMonth) && this._m < minMonth) { 1038 this._m = minMonth; 1039 } 1040 } 1041 if (this._y >= maxYear) { 1042 this._y = maxYear; 1043 if (!isNaN(maxMonth) && this._m > maxMonth) { 1044 this._m = maxMonth; 1045 } 1046 } 1047 1048 for (var c = 0; c < opts.numberOfMonths; c++) { 1049 randId = 'pika-title-' + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 2); 1050 html += '<div class="pika-lendar">' + renderTitle(this, c, this.calendars[c].year, this.calendars[c].month, this.calendars[0].year, randId) + this.render(this.calendars[c].year, this.calendars[c].month, randId) + '</div>'; 1051 } 1052 1053 this.el.innerHTML = html; 1054 1055 if (opts.bound) { 1056 if(opts.field.type !== 'hidden') { 1057 sto(function() { 1058 opts.trigger.focus(); 1059 }, 1); 1060 } 1061 } 1062 1063 if (typeof this._o.onDraw === 'function') { 1064 this._o.onDraw(this); 1065 } 1066 1067 if (opts.bound) { 1068 // let the screen reader user know to use arrow keys 1069 opts.field.setAttribute('aria-label', opts.ariaLabel); 1070 } 1071 }, 1072 1073 adjustPosition: function() 1074 { 1075 var field, pEl, width, height, viewportWidth, viewportHeight, scrollTop, left, top, clientRect, leftAligned, bottomAligned; 1076 1077 if (this._o.container) return; 1078 1079 this.el.style.position = 'absolute'; 1080 1081 field = this._o.trigger; 1082 pEl = field; 1083 width = this.el.offsetWidth; 1084 height = this.el.offsetHeight; 1085 viewportWidth = window.innerWidth || document.documentElement.clientWidth; 1086 viewportHeight = window.innerHeight || document.documentElement.clientHeight; 1087 scrollTop = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; 1088 leftAligned = true; 1089 bottomAligned = true; 1090 1091 if (typeof field.getBoundingClientRect === 'function') { 1092 clientRect = field.getBoundingClientRect(); 1093 left = clientRect.left + window.pageXOffset; 1094 top = clientRect.bottom + window.pageYOffset; 1095 } else { 1096 left = pEl.offsetLeft; 1097 top = pEl.offsetTop + pEl.offsetHeight; 1098 while((pEl = pEl.offsetParent)) { 1099 left += pEl.offsetLeft; 1100 top += pEl.offsetTop; 1101 } 1102 } 1103 1104 // default position is bottom & left 1105 if ((this._o.reposition && left + width > viewportWidth) || 1106 ( 1107 this._o.position.indexOf('right') > -1 && 1108 left - width + field.offsetWidth > 0 1109 ) 1110 ) { 1111 left = left - width + field.offsetWidth; 1112 leftAligned = false; 1113 } 1114 if ((this._o.reposition && top + height > viewportHeight + scrollTop) || 1115 ( 1116 this._o.position.indexOf('top') > -1 && 1117 top - height - field.offsetHeight > 0 1118 ) 1119 ) { 1120 top = top - height - field.offsetHeight; 1121 bottomAligned = false; 1122 } 1123 1124 this.el.style.left = left + 'px'; 1125 this.el.style.top = top + 'px'; 1126 1127 addClass(this.el, leftAligned ? 'left-aligned' : 'right-aligned'); 1128 addClass(this.el, bottomAligned ? 'bottom-aligned' : 'top-aligned'); 1129 removeClass(this.el, !leftAligned ? 'left-aligned' : 'right-aligned'); 1130 removeClass(this.el, !bottomAligned ? 'bottom-aligned' : 'top-aligned'); 1131 }, 1132 1133 /** 1134 * render HTML for a particular month 1135 */ 1136 render: function(year, month, randId) 1137 { 1138 var opts = this._o, 1139 now = new Date(), 1140 days = getDaysInMonth(year, month), 1141 before = new Date(year, month, 1).getDay(), 1142 data = [], 1143 row = []; 1144 setToStartOfDay(now); 1145 if (opts.firstDay > 0) { 1146 before -= opts.firstDay; 1147 if (before < 0) { 1148 before += 7; 1149 } 1150 } 1151 var previousMonth = month === 0 ? 11 : month - 1, 1152 nextMonth = month === 11 ? 0 : month + 1, 1153 yearOfPreviousMonth = month === 0 ? year - 1 : year, 1154 yearOfNextMonth = month === 11 ? year + 1 : year, 1155 daysInPreviousMonth = getDaysInMonth(yearOfPreviousMonth, previousMonth); 1156 var cells = days + before, 1157 after = cells; 1158 while(after > 7) { 1159 after -= 7; 1160 } 1161 cells += 7 - after; 1162 var isWeekSelected = false; 1163 for (var i = 0, r = 0; i < cells; i++) 1164 { 1165 var day = new Date(year, month, 1 + (i - before)), 1166 isSelected = isDate(this._d) ? compareDates(day, this._d) : false, 1167 isToday = compareDates(day, now), 1168 hasEvent = opts.events.indexOf(day.toDateString()) !== -1 ? true : false, 1169 isEmpty = i < before || i >= (days + before), 1170 dayNumber = 1 + (i - before), 1171 monthNumber = month, 1172 yearNumber = year, 1173 isStartRange = opts.startRange && compareDates(opts.startRange, day), 1174 isEndRange = opts.endRange && compareDates(opts.endRange, day), 1175 isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange, 1176 isDisabled = (opts.minDate && day < opts.minDate) || 1177 (opts.maxDate && day > opts.maxDate) || 1178 (opts.disableWeekends && isWeekend(day)) || 1179 (opts.disableDayFn && opts.disableDayFn(day)); 1180 1181 if (isEmpty) { 1182 if (i < before) { 1183 dayNumber = daysInPreviousMonth + dayNumber; 1184 monthNumber = previousMonth; 1185 yearNumber = yearOfPreviousMonth; 1186 } else { 1187 dayNumber = dayNumber - days; 1188 monthNumber = nextMonth; 1189 yearNumber = yearOfNextMonth; 1190 } 1191 } 1192 1193 var dayConfig = { 1194 day: dayNumber, 1195 month: monthNumber, 1196 year: yearNumber, 1197 hasEvent: hasEvent, 1198 isSelected: isSelected, 1199 isToday: isToday, 1200 isDisabled: isDisabled, 1201 isEmpty: isEmpty, 1202 isStartRange: isStartRange, 1203 isEndRange: isEndRange, 1204 isInRange: isInRange, 1205 showDaysInNextAndPreviousMonths: opts.showDaysInNextAndPreviousMonths, 1206 enableSelectionDaysInNextAndPreviousMonths: opts.enableSelectionDaysInNextAndPreviousMonths 1207 }; 1208 1209 if (opts.pickWholeWeek && isSelected) { 1210 isWeekSelected = true; 1211 } 1212 1213 row.push(renderDay(dayConfig)); 1214 1215 if (++r === 7) { 1216 if (opts.showWeekNumber) { 1217 row.unshift(renderWeek(i - before, month, year, opts.firstWeekOfYearMinDays)); 1218 } 1219 data.push(renderRow(row, opts.isRTL, opts.pickWholeWeek, isWeekSelected)); 1220 row = []; 1221 r = 0; 1222 isWeekSelected = false; 1223 } 1224 } 1225 return renderTable(opts, data, randId); 1226 }, 1227 1228 isVisible: function() 1229 { 1230 return this._v; 1231 }, 1232 1233 show: function() 1234 { 1235 if (!this.isVisible()) { 1236 this._v = true; 1237 this.draw(); 1238 removeClass(this.el, 'is-hidden'); 1239 if (this._o.bound) { 1240 addEvent(document, 'click', this._onClick); 1241 this.adjustPosition(); 1242 } 1243 if (typeof this._o.onOpen === 'function') { 1244 this._o.onOpen.call(this); 1245 } 1246 } 1247 }, 1248 1249 hide: function() 1250 { 1251 var v = this._v; 1252 if (v !== false) { 1253 if (this._o.bound) { 1254 removeEvent(document, 'click', this._onClick); 1255 } 1256 1257 if (!this._o.container) { 1258 this.el.style.position = 'static'; // reset 1259 this.el.style.left = 'auto'; 1260 this.el.style.top = 'auto'; 1261 } 1262 addClass(this.el, 'is-hidden'); 1263 this._v = false; 1264 if (v !== undefined && typeof this._o.onClose === 'function') { 1265 this._o.onClose.call(this); 1266 } 1267 } 1268 }, 1269 1270 /** 1271 * GAME OVER 1272 */ 1273 destroy: function() 1274 { 1275 var opts = this._o; 1276 1277 this.hide(); 1278 removeEvent(this.el, 'mousedown', this._onMouseDown, true); 1279 removeEvent(this.el, 'touchend', this._onMouseDown, true); 1280 removeEvent(this.el, 'change', this._onChange); 1281 if (opts.keyboardInput) { 1282 removeEvent(document, 'keydown', this._onKeyChange); 1283 } 1284 if (opts.field) { 1285 removeEvent(opts.field, 'change', this._onInputChange); 1286 if (opts.bound) { 1287 removeEvent(opts.trigger, 'click', this._onInputClick); 1288 removeEvent(opts.trigger, 'focus', this._onInputFocus); 1289 removeEvent(opts.trigger, 'blur', this._onInputBlur); 1290 } 1291 } 1292 if (this.el.parentNode) { 1293 this.el.parentNode.removeChild(this.el); 1294 } 1295 } 1296 1297 }; 1298 1299 return Pikaday; 1300}));
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.