1/******************************************************************** 2 * Kalendae, a framework agnostic javascript date picker * 3 * Copyright(c) 2013 Jarvis Badgley ([email protected]) * 4 * http://github.com/ChiperSoft/Kalendae * 5 * Version 0.5.0 * 6 ********************************************************************/ 7 8(function (undefined) { 9 10var today, moment; 11 12var Kalendae = function (targetElement, options) { 13 if (typeof document.addEventListener !== 'function' && !util.isIE8()) return; 14 15 //if the first argument isn't an element and isn't a string, assume that it is the options object 16 var is_element = false; 17 try { 18 is_element = targetElement instanceof Element; 19 } 20 catch (err) { 21 is_element = !!targetElement && is_element.nodeType === 1; 22 } 23 if (!(is_element || typeof(targetElement) === 'string')) options = targetElement; 24 25 var self = this, 26 classes = self.classes, 27 opts = self.settings = util.merge(self.defaults, {attachTo:targetElement}, options || {}), 28 $container = self.container = util.make('div', {'class':classes.container}), 29 calendars = self.calendars = [], 30 startDay = moment().day(opts.weekStart), 31 vsd, 32 columnHeaders = [], 33 $cal, 34 $title, 35 $caption, 36 $header, 37 $days, $week, dayNodes = [], 38 $span, 39 i = 0, 40 j = opts.months; 41 42 if (util.isIE8()) util.addClassName($container, 'ie8'); 43 44 //generate the column headers (Su, Mo, Tu, etc) 45 i = 7; 46 while (i--) { 47 columnHeaders.push( startDay.format(opts.columnHeaderFormat) ); 48 startDay.add('days',1); 49 } 50
51 //setup publish/subscribe and apply any subscriptions passed in settings 52 MinPubSub(self); 53 if (typeof opts.subscribe === 'object') { 54 for (i in opts.subscribe) if (opts.subscribe.hasOwnProperty(i)) { 55 self.subscribe(i, opts.subscribe[i]); 56 } 57 } 58 59 //process default selected dates 60 self._sel = []; 61 if (!!opts.selected) self.setSelected(opts.selected, false); 62 63 //set the view month 64 if (!!opts.viewStartDate) { 65 vsd = moment(opts.viewStartDate, opts.format); 66 } else if (self._sel.length > 0) { 67 vsd = moment(self._sel[0]); 68 } else { 69 vsd = moment(); 70 } 71 self.viewStartDate = vsd.date(1); 72 73 var viewDelta = ({ 74 'past' : opts.months-1, 75 'today-past' : opts.months-1, 76 'any' : opts.months>2?Math.floor(opts.months/2):0, 77 'today-future' : 0, 78 'future' : 0 79 })[this.settings.direction]; 80 81 82 if (viewDelta && moment().month()==moment(self.viewStartDate).month()){ 83 self.viewStartDate = moment(self.viewStartDate).subtract({M:viewDelta}).date(1); 84 } 85 86 87 if (typeof opts.blackout === 'function') { 88 self.blackout = opts.blackout; 89 } else if (!!opts.blackout) { 90 var bdates = parseDates(opts.blackout, opts.parseSplitDelimiter, opts.format); 91 self.blackout = function (input) { 92 input = moment(input).startOf('day').yearDay(); 93 if (input < 1 || !self._sel) return false; 94 var i = bdates.length; 95 while (i--) if (bdates[i].startOf('day').yearDay() === input) return true; 96 return false; 97 }; 98 } else { 99 self.blackout = function () {return false;}; 100 } 101 102 103 self.direction = self.directions[opts.direction] ? self.directions[opts.direction] : self.directions['any']; 104 105 106 //for the total months setting, generate N calendar views and add them to the container 107 j = Math.max(opts.months,1); 108 while (j--) { 109 $cal = util.make('div', {'class':classes.calendar}, $container); 110 111 $cal.setAttribute('data-cal-index', j); 112 if (opts.months > 1) { 113 if (j == Math.max(opts.months-1,1)) util.addClassName($cal, classes.monthFirst); 114 else if (j === 0) util.addClassName($cal, classes.monthLast); 115 else util.addClassName($cal, classes.monthMiddle); 116 } 117 118 //title bar 119 $title = util.make('div', {'class':classes.title}, $cal); 120 if(!opts.useYearNav){ 121 util.addClassName($title, classes.disableYearNav); 122 } 123 util.make('a', {'class':classes.previousYear}, $title); //previous button 124 util.make('a', {'class':classes.previousMonth}, $title); //previous button 125 util.make('a', {'class':classes.nextYear}, $title); //next button 126 util.make('a', {'class':classes.nextMonth}, $title); //next button 127 $caption = util.make('span', {'class':classes.caption}, $title); //title caption 128 129 //column headers 130 $header = util.make('div', {'class':classes.header}, $cal); 131 i = 0; 132 do { 133 $span = util.make('span', {}, $header); 134 $span.innerHTML = columnHeaders[i]; 135 } while (++i < 7); 136 137 //individual day cells 138 $days = util.make('div', {'class':classes.days}, $cal); 139 i = 0; 140 dayNodes = []; 141 do { 142 if (opts.mode == 'week') { 143 if ((i % 7) === 0) { 144 $week = util.make('div', {'class': classes.week + ' clearfix'}, $days); 145 dayNodes.push($week); 146 } 147 util.make('span', {}, $week); 148 } else { 149 dayNodes.push(util.make('span', {}, $days)); 150 } 151 } while (++i < 42); 152 153 //store each calendar view for easy redrawing 154 calendars.push({ 155 caption:$caption, 156 days:dayNodes 157 }); 158 159 if (j) util.make('div', {'class':classes.monthSeparator}, $container); 160 } 161 162 self.draw(); 163 164 util.addEvent($container, 'mousedown', function (event, target) { 165 var clickedDate; 166 if (util.hasClassName(target, classes.nextMonth)) { 167 //NEXT MONTH BUTTON 168 if (!self.disableNext && self.publish('view-changed', self, ['next-month']) !== false) { 169 self.viewStartDate.add('months',1); 170 self.draw(); 171 } 172 return false; 173 174 } else if (util.hasClassName(target, classes.previousMonth)) { 175 //PREVIOUS MONTH BUTTON 176 if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-month']) !== false) { 177 self.viewStartDate.subtract('months',1); 178 self.draw(); 179 } 180 return false; 181 182 } else if (util.hasClassName(target, classes.nextYear)) { 183 //NEXT MONTH BUTTON 184 if (!self.disableNext && self.publish('view-changed', self, ['next-year']) !== false) { 185 self.viewStartDate.add('years',1); 186 self.draw(); 187 } 188 return false;
189 190 } else if (util.hasClassName(target, classes.previousYear)) { 191 //PREVIOUS MONTH BUTTON 192 if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-year']) !== false) { 193 self.viewStartDate.subtract('years',1); 194 self.draw(); 195 } 196 return false; 197 198 } else if ( (util.hasClassName(target.parentNode, classes.days) || util.hasClassName(target.parentNode, classes.week)) && util.hasClassName(target, classes.dayActive) && (clickedDate = target.getAttribute('data-date'))) { 199 //DAY CLICK 200 clickedDate = moment(clickedDate, opts.dayAttributeFormat).hours(12); 201 if (self.publish('date-clicked', self, [clickedDate]) !== false) { 202 203 switch (opts.mode) { 204 case 'multiple': 205 if (!self.addSelected(clickedDate)) self.removeSelected(clickedDate); 206 break; 207 case 'range': 208 self.addSelected(clickedDate); 209 break; 210 case 'week': 211 self.weekSelected(clickedDate); 212 break; 213 case 'single': 214 /* falls through */ 215 default: 216 self.addSelected(clickedDate); 217 break; 218 } 219 220 } 221 return false; 222 223 } else if ( util.hasClassName(target.parentNode, classes.week) && (clickedDate = target.getAttribute('data-date') ) ) { 224 //INACTIVE WEEK CLICK 225 clickedDate = moment(clickedDate, opts.dayAttributeFormat).hours(12); 226 if (self.publish('date-clicked', self, [clickedDate]) !== false) { 227 if (opts.mode == 'week') { 228 self.weekSelected(clickedDate); 229 } 230 } 231 return false; 232 } 233 234 return false; 235 }); 236 237 238 if (!!(opts.attachTo = util.$(opts.attachTo))) { 239 opts.attachTo.appendChild($container); 240 } 241 242}; 243 244Kalendae.prototype = { 245 defaults : { 246 attachTo :null, /* the element to attach the root container to. can be string or DOMElement */ 247 months :1, /* total number of months to display side by side */ 248 weekStart :0, /* day to use for the start of the week. 0 is Sunday */ 249 direction :'any', /* past, today-past, any, today-future, future */ 250 directionScrolling :true, /* if a direction other than any is defined, prevent scrolling out of range */ 251 viewStartDate :null, /* date in the month to display. When multiple months, this is the left most */ 252 blackout :null, /* array of dates, or function to be passed a date */ 253 selected :null, /* dates already selected. can be string, date, or array of strings or dates. */ 254 mode :'single', /* single, multiple, range */ 255 dayOutOfMonthClickable:false, 256 format :null, /* string used for parsing dates. */ 257 subscribe :null, /* object containing events to subscribe to */ 258 259 columnHeaderFormat :'dd', /* number of characters to show in the column headers */ 260 titleFormat :'MMMM, YYYY', /* format mask for month titles. See momentjs.com for rules */ 261 dayNumberFormat :'D', /* format mask for individual days */ 262 dayAttributeFormat :'YYYY-MM-DD', /* format mask for the data-date attribute set on every span */ 263 parseSplitDelimiter : /,\s*|\s+-\s+/, /* regex to use for splitting multiple dates from a passed string */ 264 rangeDelimiter :' - ', /* string to use between dates when outputting in range mode */ 265 multipleDelimiter :', ', /* string to use between dates when outputting in multiple mode */ 266 useYearNav :true, 267 268 dateClassMap :{} 269 }, 270 classes : { 271 container :'kalendae', 272 calendar :'k-calendar', 273 monthFirst :'k-first-month', 274 monthMiddle :'k-middle-month', 275 monthLast :'k-last-month', 276 title :'k-title', 277 previousMonth :'k-btn-previous-month', 278 nextMonth :'k-btn-next-month', 279 previousYear :'k-btn-previous-year', 280 nextYear :'k-btn-next-year', 281 caption :'k-caption', 282 header :'k-header', 283 days :'k-days', 284 week :'k-week', 285 dayOutOfMonth :'k-out-of-month', 286 dayInMonth :'k-in-month', 287 dayActive :'k-active', 288 daySelected :'k-selected', 289 dayInRange :'k-range', 290 dayToday :'k-today', 291 monthSeparator :'k-separator', 292 disablePreviousMonth :'k-disable-previous-month-btn', 293 disableNextMonth :'k-disable-next-month-btn', 294 disablePreviousYear :'k-disable-previous-year-btn', 295 disableNextYear :'k-disable-next-year-btn', 296 disableYearNav :'k-disable-year-nav' 297 }, 298
299 disablePreviousMonth: false, 300 disableNextMonth: false, 301 disablePreviousYear: false, 302 disableNextYear: false, 303 304 directions: { 305 'past' :function (date) {return moment(date).startOf('day').yearDay() >= today.yearDay();}, 306 'today-past' :function (date) {return moment(date).startOf('day').yearDay() > today.yearDay();}, 307 'any' :function (date) {return false;}, 308 'today-future' :function (date) {return moment(date).startOf('day').yearDay() < today.yearDay();}, 309 'future' :function (date) {return moment(date).startOf('day').yearDay() <= today.yearDay();} 310 }, 311 312 getSelectedAsDates : function () { 313 var out = []; 314 var i=0, c = this._sel.length; 315 for (;i<c;i++) { 316 out.push(this._sel[i].toDate()); 317 } 318 return out; 319 }, 320 321 getSelectedAsText : function (format) { 322 var out = []; 323 var i=0, c = this._sel.length; 324 for (;i<c;i++) { 325 out.push(this._sel[i].format(format || this.settings.format || 'YYYY-MM-DD')); 326 } 327 return out; 328 }, 329 330 getSelectedRaw : function () { 331 var out = []; 332 var i=0, c = this._sel.length; 333 for (;i<c;i++) { 334 out.push(moment(this._sel[i])); 335 } 336 return out; 337 }, 338 339 getSelected : function (format) { 340 var sel = this.getSelectedAsText(format); 341 switch (this.settings.mode) { 342 case 'week': 343 /* falls through range */ 344 345 case 'range': 346 sel.splice(2); //shouldn't be more than two, but lets just make sure. 347 return sel.join(this.settings.rangeDelimiter); 348 349 case 'multiple': 350 return sel.join(this.settings.multipleDelimiter); 351 352 case 'single': 353 /* falls through */ 354 default: 355 return (sel[0] || null); 356 } 357 }, 358 359 isSelected : function (input) { 360 input = moment(input).startOf('day').yearDay(); 361 if (input < 1 || !this._sel || this._sel.length < 1) return false; 362 363 switch (this.settings.mode) { 364 case 'week': 365 /* falls through range */ 366 case 'range': 367 var a = this._sel[0] ? this._sel[0].startOf('day').yearDay() : 0, 368 b = this._sel[1] ? this._sel[1].startOf('day').yearDay() : 0; 369 370 if (a === input || b === input) return 1; 371 if (!a || !b) return 0; 372 373 if ((input > a && input < b) || (a<b && input < a && input > b)) return -1; 374 return false; 375 376 case 'multiple': 377 var i = this._sel.length; 378 while (i--) { 379 if (this._sel[i].startOf('day').yearDay() === input) { 380 return true; 381 } 382 } 383 return false; 384 385 386 case 'single': 387 /* falls through */ 388 default: 389 return (this._sel[0] && (this._sel[0].startOf('day').yearDay() === input)); 390 } 391 392 return false; 393 }, 394 395 setSelected : function (input, draw) { 396 var i, 397 new_dates = parseDates(input, this.settings.parseSplitDelimiter, this.settings.format), 398 old_dates = parseDates(this.getSelected(), this.settings.parseSplitDelimiter, this.settings.format); 399 400 i = old_dates.length; 401 while(i--) { this.removeSelected(old_dates[i], false); } 402 403 i = new_dates.length; 404 while(i--) { this.addSelected(new_dates[i], false); } 405 406 if (draw !== false) { 407 if (new_dates[0]) { 408 this.viewStartDate = moment(new_dates[0], this.settings.format); 409 } 410 this.draw(); 411 } 412 }, 413 414 addSelected : function (date, draw) { 415 date = moment(date, this.settings.format).hours(12); 416 417 if(this.settings.dayOutOfMonthClickable && this.settings.mode !== 'range'){ this.makeSelectedDateVisible(date); } 418 419 switch (this.settings.mode) { 420 case 'multiple': 421 if (!this.isSelected(date)) this._sel.push(date); 422 else return false; 423 break; 424 case 'range': 425 426 if (this._sel.length !== 1) this._sel = [date]; 427 else { 428 if (date.startOf('day').yearDay() > this._sel[0].startOf('day').yearDay()) this._sel[1] = date; 429 else this._sel = [date, this._sel[0]]; 430 } 431 break; 432 case 'single': 433 /* falls through */ 434 default: 435 this._sel = [date]; 436 break; 437 } 438 this._sel.sort(function (a,b) {return a.startOf('day').yearDay() - b.startOf('day').yearDay();}); 439 this.publish('change', this, [date]); 440 if (draw !== false) this.draw(); 441 return true; 442 }, 443 444 weekSelected: function (mom) { 445 var x = mom.toDate(); 446 var start = moment(x).startOf('week'); 447 var end = moment(x).endOf('week').subtract('day',1); 448 this._sel = [start, end]; 449 this.publish('change', this, [mom.day()]); 450 this.draw(); 451 }, 452 453 makeSelectedDateVisible: function (date) { 454 outOfViewMonth = moment(date).date('1').diff(this.viewStartDate,'months'); 455 456 if(outOfViewMonth < 0){ 457 this.viewStartDate.subtract('months',1); 458 } 459 else if(outOfViewMonth > 0 && outOfViewMonth >= this.settings.months){ 460 this.viewStartDate.add('months',1); 461 } 462 }, 463 464 removeSelected : function (date, draw) { 465 date = moment(date, this.settings.format).hours(12); 466 var i = this._sel.length; 467 while (i--) { 468 if (this._sel[i].startOf('day').yearDay() === date.startOf('day').yearDay()) { 469 this._sel.splice(i,1); 470 this.publish('change', this, [date]); 471 if (draw !== false) this.draw(); 472 return true; 473 } 474 } 475 return false;
476 }, 477 478 draw : function draw() { 479 // return; 480 var month = moment(this.viewStartDate).startOf('day').hours(12), //force middle of the day to avoid any weird date shifts 481 day, 482 classes = this.classes, 483 cal, 484 $span, 485 klass, 486 i=0, c, 487 j=0, k, 488 w, 489 s, 490 dateString, 491 opts = this.settings, 492 diff; 493 494 c = this.calendars.length; 495 496 do { 497 day = moment(month).date(1); 498 day.day( day.day() < this.settings.weekStart ? this.settings.weekStart-7 : this.settings.weekStart); 499 //if the first day of the month is less than our week start, back up a week 500 501 cal = this.calendars[i]; 502 cal.caption.innerHTML = month.format(this.settings.titleFormat); 503 j = 0; 504 w = 0; 505 do { 506 if (opts.mode == 'week') { 507 if (((j % 7) === 0) && (j !== 0)) { 508 w++; 509 } 510 $span = cal.days[w].childNodes[j%7]; 511 } else { 512 $span = cal.days[j]; 513 } 514 515 klass = []; 516 517 s = this.isSelected(day); 518 519 if (s) klass.push(({'-1':classes.dayInRange,'1':classes.daySelected, 'true':classes.daySelected})[s]); 520 521 if (day.month() != month.month()) klass.push(classes.dayOutOfMonth); 522 else klass.push(classes.dayInMonth); 523 524 if (!(this.blackout(day) || this.direction(day) || (day.month() != month.month() && opts.dayOutOfMonthClickable === false)) || s>0) klass.push(classes.dayActive); 525 526 if (day.startOf('day').yearDay() === today.yearDay()) klass.push(classes.dayToday); 527 528 dateString = day.format(this.settings.dayAttributeFormat); 529 if (opts.dateClassMap[dateString]) klass.push(opts.dateClassMap[dateString]); 530 531 $span.innerHTML = day.format(opts.dayNumberFormat); 532 $span.className = klass.join(' '); 533 $span.setAttribute('data-date', dateString); 534 535 536 day.add('days',1); 537 } while (++j < 42); 538 month.add('months',1); 539 } while (++i < c); 540 541 if (opts.directionScrolling) { 542 var diffComparison = moment().startOf('day').hours(12); 543 diff = month.diff(diffComparison, 'months', true); 544 545 if (opts.direction === 'today-past' || opts.direction === 'past') { 546 if (diff <= 0) { 547 this.disableNextMonth = false; 548 util.removeClassName(this.container, classes.disableNextMonth); 549 } else { 550 this.disableNextMonth = true; 551 util.addClassName(this.container, classes.disableNextMonth); 552 } 553 } else if (opts.direction === 'today-future' || opts.direction === 'future') { 554 if (diff > opts.months) { 555 this.disablePreviousMonth = false; 556 util.removeClassName(this.container, classes.disablePreviousMonth); 557 } else { 558 this.disablePreviousMonth = true; 559 util.addClassName(this.container, classes.disablePreviousMonth); 560 } 561 } 562 563 if (opts.direction === 'today-past' || opts.direction === 'past') { 564 if (diff <= -11) { 565 this.disableNextYear = false; 566 util.removeClassName(this.container, classes.disableNextYear); 567 } else { 568 this.disableNextYear = true; 569 util.addClassName(this.container, classes.disableNextYear); 570 } 571 } else if (opts.direction==='today-future' || opts.direction==='future') { 572 if (diff > (11 + opts.months)) { 573 this.disablePreviousYear = false; 574 util.removeClassName(this.container, classes.disablePreviousYear); 575 } else { 576 this.disablePreviousYear = true; 577 util.addClassName(this.container, classes.disablePreviousYear); 578 } 579 } 580 } 581 } 582}; 583 584var parseDates = function (input, delimiter, format) { 585 var output = []; 586 587 if (typeof input === 'string') { 588 input = input.split(delimiter); 589 } else if (!util.isArray(input)) { 590 input = [input]; 591 } 592 593 var c = input.length, 594 i = 0, 595 m; 596 597 do { 598 if (input[i]) { 599 m = moment(input[i], format).hours(12); 600 if (m.isValid()) output.push(m); 601 } 602 } while (++i < c); 603 604 return output; 605}; 606 607 608 609window.Kalendae = Kalendae; 610 611var util = Kalendae.util = { 612 613 isIE8: function() { 614 return !!( (/msie 8./i).test(navigator.appVersion) && !(/opera/i).test(navigator.userAgent) && window.ActiveXObject && XDomainRequest && !window.msPerformance ); 615 }, 616 617// ELEMENT FUNCTIONS 618 619 $: function (elem) { 620 return (typeof elem == 'string') ? document.getElementById(elem) : elem; 621 }, 622 623 $$: function (selector) { 624 return document.querySelectorAll(selector); 625 }, 626 627 make: function (tagName, attributes, attach) { 628 var k, e = document.createElement(tagName); 629 if (!!attributes) for (k in attributes) if (attributes.hasOwnProperty(k)) e.setAttribute(k, attributes[k]); 630 if (!!attach) attach.appendChild(e); 631 return e; 632 }, 633 634 // Returns true if the DOM element is visible, false if it's hidden. 635 // Checks if display is anything other than none. 636 isVisible: function (elem) { 637 // shamelessly copied from jQuery 638 return elem.offsetWidth > 0 || elem.offsetHeight > 0; 639 }, 640 641 getStyle: function (elem, styleProp) { 642 var y, s; 643 if (elem.currentStyle) { 644 y = elem.currentStyle[styleProp]; 645 } else if (window.getComputedStyle) { 646 s = window.getComputedStyle(elem, null); 647 y = s ? s[styleProp] : ''; 648 } 649 return y; 650 }, 651 652 domReady: function (f) { 653 var state = document.readyState; 654 if (state === 'complete' || state === 'interactive') { 655 f(); 656 } else { 657 setTimeout(function() { util.domReady(f); }, 9); 658 } 659 }, 660 661 // Adds a listener callback to a DOM element which is fired on a specified 662 // event. Callback is sent the event object and the element that triggered the event 663 addEvent: function (elem, eventName, callback) { 664 var listener = function (event) { 665 event = event || window.event; 666 var target = event.target || event.srcElement; 667 var block = callback.apply(elem, [event, target]); 668 if (block === false) { 669 if (!!event.preventDefault) event.preventDefault(); 670 else { 671 event.returnValue = false;
672 event.cancelBubble = true; 673 } 674 } 675 return block; 676 }; 677 if (elem.attachEvent) { // IE only. The "on" is mandatory. 678 elem.attachEvent("on" + eventName, listener); 679 } else { // Other browsers. 680 elem.addEventListener(eventName, listener, false); 681 } 682 return listener; 683 }, 684 685 // Removes a listener callback from a DOM element which is fired on a specified 686 // event. 687 removeEvent: function (elem, event, listener) { 688 if (elem.detachEvent) { // IE only. The "on" is mandatory. 689 elem.detachEvent("on" + event, listener); 690 } else { // Other browsers. 691 elem.removeEventListener(event, listener, false); 692 } 693 }, 694 695 fireEvent: function (elem, event) { 696 if (document.createEvent) { 697 var e = document.createEvent('HTMLEvents'); 698 e.initEvent(event, false, true); 699 elem.dispatchEvent(e); 700 } else if (document.createEventObject) { 701 elem.fireEvent('on' + event) ; 702 } else if (typeof elem['on' + event] == 'function' ) { 703 elem['on' + event](); 704 } 705 }, 706 707 hasClassName: function(elem, className) { //copied and modified from Prototype.js 708 if (!(elem = util.$(elem))) return false; 709 var eClassName = elem.className; 710 return (eClassName.length > 0 && (eClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(eClassName))); 711 }, 712 713 addClassName: function(elem, className) { //copied and modified from Prototype.js 714 if (!(elem = util.$(elem))) return; 715 if (!util.hasClassName(elem, className)) elem.className += (elem.className ? ' ' : '') + className; 716 }, 717 718 removeClassName: function(elem, className) { //copied and modified from Prototype.js 719 if (!(elem = util.$(elem))) return; 720 elem.className = util.trimString(elem.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ')); 721 }, 722 723 isFixed: function (elem) { 724 do { 725 if (util.getStyle(elem, 'position') === 'fixed') return true; 726 } while ((elem = elem.offsetParent)); 727 return false; 728 }, 729 730 scrollContainer: function (elem) { 731 do { 732 var overflow = util.getStyle(elem, 'overflow'); 733 if (overflow === 'auto' || overflow === 'scroll') return elem; 734 } while ((elem = elem.parentNode) && elem != window.document.body); 735 return null; 736 }, 737 738 getPosition: function (elem, isInner) { 739 var x = elem.offsetLeft, 740 y = elem.offsetTop, 741 r = {}; 742 743 if (!isInner) { 744 while ((elem = elem.offsetParent)) { 745 x += elem.offsetLeft; 746 y += elem.offsetTop; 747 } 748 } 749 750 r[0] = r.left = x; 751 r[1] = r.top = y; 752 return r; 753 }, 754 755 getHeight: function (elem) { 756 return elem.offsetHeight || elem.scrollHeight; 757 }, 758 759 getWidth: function (elem) { 760 return elem.offsetWidth || elem.scrollWidth; 761 }, 762 763 764// TEXT FUNCTIONS 765 766 trimString: function (input) { 767 return input.replace(/^\s+/, '').replace(/\s+$/, ''); 768 }, 769 770 771// OBJECT FUNCTIONS 772 773 merge: function () { 774 /* Combines multiple objects into one. 775 * Syntax: util.extend([true], object1, object2, ... objectN) 776 * If first argument is true, function will merge recursively. 777 */ 778 779 var deep = (arguments[0]===true), 780 d = {}, 781 i = deep?1:0; 782 783 var _c = function (a, b) { 784 if (typeof b !== 'object') return; 785 for (var k in b) if (b.hasOwnProperty(k)) { 786 //if property is an object or array, merge the contents instead of overwriting, if extend() was called as such 787 if (deep && typeof a[k] === 'object' && typeof b[k] === 'object') _update(a[k], b[k]); 788 else a[k] = b[k]; 789 } 790 return a; 791 }; 792 793 for (; i < arguments.length; i++) { 794 _c(d, arguments[i]); 795 } 796 return d; 797 }, 798 799 isArray: function (array) { 800 return Object.prototype.toString.call(array) == "[object Array]"; 801 } 802}; 803 804 805//auto-initializaiton code 806if (typeof document.addEventListener === 'function') Kalendae.util.domReady(function () { 807 var els = util.$$('.auto-kal'), 808 i = els.length, 809 e, 810 options, 811 optionsRaw; 812 813 while (i--) { 814 e = els[i]; 815 optionsRaw = e.getAttribute('data-kal'); 816 options = (optionsRaw == null || optionsRaw == "") ? {} : (new Function('return {' + optionsRaw + '};'))(); 817 818 if (e.tagName === 'INPUT') { 819 //if element is an input, bind a popup calendar to the input. 820 new Kalendae.Input(e, options); 821 } else { 822 //otherwise, insert a flat calendar into the element. 823 new Kalendae(util.merge(options, {attachTo:e})); 824 } 825 826 } 827}); 828Kalendae.Input = function (targetElement, options) { 829 if (typeof document.addEventListener !== 'function' && !util.isIE8()) return; 830 831 var $input = this.input = util.$(targetElement), 832 overwriteInput, 833 $closeButton, 834 changing = false;
835 836 if (!$input || $input.tagName !== 'INPUT') throw "First argument for Kalendae.Input must be an <input> element or a valid element id."; 837 838 var self = this, 839 classes = self.classes, 840 opts = self.settings = util.merge(self.defaults, options); 841 842 this._events = {}; 843 844 //force attachment to the body 845 opts.attachTo = window.document.body; 846 847 //if no override provided, use the input's contents 848 if (!opts.selected) opts.selected = $input.value; 849 else overwriteInput = true; 850 851 //call our parent constructor 852 Kalendae.call(self, opts); 853 854 //create the close button 855 if (opts.closeButton) { 856 $closeButton = util.make('a', {'class':classes.closeButton}, self.container); 857 util.addEvent($closeButton, 'click', function () { 858 $input.blur(); 859 }); 860 } 861 862 if (overwriteInput) $input.value = self.getSelected(); 863 864 var $container = self.container, 865 noclose = false; 866 867 $container.style.display = 'none'; 868 util.addClassName($container, classes.positioned); 869 870 this._events.containerMouseDown = util.addEvent($container, 'mousedown', function (event, target) { 871 noclose = true; //IE8 doesn't obey event blocking when it comes to focusing, so we have to do this shit. 872 }); 873 874 this._events.documentMousedown = util.addEvent(window.document, 'mousedown', function (event, target) { 875 noclose = false; 876 }); 877 878 this._events.inputFocus = util.addEvent($input, 'focus', function () { 879 changing = true; // prevent setSelected from altering the input contents. 880 self.setSelected(this.value); 881 changing = false; 882 self.show(); 883 }); 884 885 this._events.inputBlur = util.addEvent($input, 'blur', function () { 886 if (noclose && util.isIE8()) { 887 noclose = false; 888 $input.focus(); 889 } 890 else self.hide(); 891 }); 892 893 this._events.inputKeyup = util.addEvent($input, 'keyup', function (event) { 894 changing = true; // prevent setSelected from altering the input contents. 895 self.setSelected(this.value); 896 changing = false; 897 }); 898 899 var $scrollContainer = util.scrollContainer($input); 900 901 if( $scrollContainer ) { 902 903 // Hide calendar when $scrollContainer is scrolled 904 util.addEvent($scrollContainer, 'scroll', function (event) { 905 $input.blur(); 906 }); 907 } 908
909 self.subscribe('change', function () { 910 if (changing) { 911 // the change event came from an internal modification, don't update the field contents 912 return; 913 } 914 $input.value = self.getSelected(); 915 util.fireEvent($input, 'change'); 916 }); 917 918}; 919 920Kalendae.Input.prototype = util.merge(Kalendae.prototype, { 921 defaults : util.merge(Kalendae.prototype.defaults, { 922 format: 'MM/DD/YYYY', 923 side: 'bottom', 924 closeButton: true, 925 offsetLeft: 0, 926 offsetTop: 0 927 }), 928 classes : util.merge(Kalendae.prototype.classes, { 929 positioned : 'k-floating', 930 closeButton: 'k-btn-close' 931 }), 932 933 show : function () { 934 var $container = this.container, 935 style = $container.style, 936 $input = this.input, 937 pos = util.getPosition($input), 938 $scrollContainer = util.scrollContainer($input), 939 scrollTop = $scrollContainer ? $scrollContainer.scrollTop : 0, 940 opts = this.settings; 941 942 style.display = ''; 943 switch (opts.side) { 944 case 'left': 945 style.left = (pos.left - util.getWidth($container) + opts.offsetLeft) + 'px'; 946 style.top = (pos.top + opts.offsetTop - scrollTop) + 'px'; 947 break; 948 case 'right': 949 style.left = (pos.left + util.getWidth($input)) + 'px'; 950 style.top = (pos.top + opts.offsetTop - scrollTop) + 'px'; 951 break; 952 case 'top': 953 style.left = (pos.left + opts.offsetLeft) + 'px'; 954 style.top = (pos.top - util.getHeight($container) + opts.offsetTop - scrollTop) + 'px'; 955 break; 956 case 'bottom': 957 /* falls through */ 958 default: 959 style.left = (pos.left + opts.offsetLeft) + 'px'; 960 style.top = (pos.top + util.getHeight($input) + opts.offsetTop - scrollTop) + 'px'; 961 break; 962 } 963 964 style.position = util.isFixed($input) ? 'fixed' : 'absolute'; 965 966 this.publish('show', this); 967 }, 968 969 hide : function () { 970 this.container.style.display = 'none'; 971 this.publish('hide', this); 972 }, 973 974 destroy : function() { 975 var $container = this.container; 976 var $input = this.input; 977 978 util.removeEvent($container, 'mousedown', this._events.containerMousedown); 979 980 util.removeEvent(window.document, 'mousedown', this._events.documentMousedown); 981 982 util.removeEvent($input, 'focus', this._events.inputFocus); 983 984 util.removeEvent($input, 'blur', this._events.inputBlur); 985 986 util.removeEvent($input, 'keyup', this._events.inputKeyup); 987 988 $container.remove(); 989 } 990}); 991 992 993/*! 994* MinPubSub, modified for use on Kalendae 995* Copyright(c) 2011 Daniel Lamb <daniellmb.com> 996* https://github.com/daniellmb/MinPubSub 997* MIT Licensed 998*/ 999 1000var MinPubSub = function(d){ 1001 1002 if (!d) d = this; 1003 1004 // the topic/subscription hash 1005 var cache = d.c_ || {}; //check for "c_" cache for unit testing 1006 1007 d.publish = function(/* String */ topic, /* Object */ target, /* Array? */ args){ 1008 // summary: 1009 // Publish some data on a named topic. 1010 // topic: String 1011 // The channel to publish on 1012 // args: Array? 1013 // The data to publish. Each array item is converted into an ordered 1014 // arguments on the subscribed functions. 1015 // 1016 // example: 1017 // Publish stuff on '/some/topic'. Anything subscribed will be called 1018 // with a function signature like: function(a,b,c){ ... } 1019 // 1020 // publish("/some/topic", ["a","b","c"]); 1021 1022 var subs = cache[topic], 1023 len = subs ? subs.length : 0, 1024 r; 1025 1026 //can change loop or reverse array if the order matters 1027 while(len--){ 1028 r = subs[len].apply(target, args || []); 1029 if (typeof r === 'boolean') return r; 1030 } 1031 }; 1032 1033 d.subscribe = function(/* String */ topic, /* Function */ callback, /* Boolean */ topPriority){ 1034 // summary: 1035 // Register a callback on a named topic. 1036 // topic: String 1037 // The channel to subscribe to 1038 // callback: Function 1039 // The handler event. Anytime something is publish'ed on a 1040 // subscribed channel, the callback will be called with the 1041 // published array as ordered arguments. 1042 // 1043 // returns: Array 1044 // A handle which can be used to unsubscribe this particular subscription. 1045 // 1046 // example: 1047 // subscribe("/some/topic", function(a, b, c){ /* handle data */ }); 1048 1049 if(!cache[topic]){ 1050 cache[topic] = []; 1051 } 1052 if (topPriority) 1053 cache[topic].push(callback); 1054 else 1055 cache[topic].unshift(callback); 1056 return [topic, callback]; // Array 1057 }; 1058 1059 d.unsubscribe = function(/* Array */ handle){ 1060 // summary: 1061 // Disconnect a subscribed function for a topic. 1062 // handle: Array 1063 // The return value from a subscribe call. 1064 // example: 1065 // var handle = subscribe("/some/topic", function(){}); 1066 // unsubscribe(handle); 1067 1068 var subs = cache[handle[0]], 1069 callback = handle[1], 1070 len = subs ? subs.length : 0; 1071 1072 while(len--){
1073 if(subs[len] === callback){ 1074 subs.splice(len, 1); 1075 } 1076 } 1077 }; 1078 1079};// moment.js 1080// version : 2.1.0 1081// author : Tim Wood 1082// license : MIT 1083// momentjs.com 1084 1085(function (undefined) { 1086 1087 /************************************ 1088 Constants 1089 ************************************/ 1090 1091 var moment, 1092 VERSION = "2.1.0", 1093 round = Math.round, i, 1094 // internal storage for language config files 1095 languages = {}, 1096 1097 // check for nodeJS 1098 hasModule = (typeof module !== 'undefined' && module.exports), 1099 1100 // ASP.NET json date format regex 1101 aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, 1102 aspNetTimeSpanJsonRegex = /(\-)?(\d*)?\.?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/, 1103 1104 // format tokens 1105 formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g, 1106 localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g, 1107 1108 // parsing token regexes 1109 parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 1110 parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 1111 parseTokenThreeDigits = /\d{3}/, // 000 - 999 1112 parseTokenFourDigits = /\d{1,4}/, // 0 - 9999 1113 parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 1114 parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. 1115 parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z 1116 parseTokenT = /T/i, // T (ISO seperator) 1117 parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 1118 1119 // preliminary iso regex 1120 // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 1121 isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/, 1122 isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', 1123 1124 // iso time formats and regexes 1125 isoTimes = [ 1126 ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/], 1127 ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], 1128 ['HH:mm', /(T| )\d\d:\d\d/], 1129 ['HH', /(T| )\d\d/] 1130 ], 1131 1132 // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] 1133 parseTimezoneChunker = /([\+\-]|\d\d)/gi, 1134 1135 // getter and setter names 1136 proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), 1137 unitMillisecondFactors = { 1138 'Milliseconds' : 1, 1139 'Seconds' : 1e3, 1140 'Minutes' : 6e4, 1141 'Hours' : 36e5, 1142 'Days' : 864e5, 1143 'Months' : 2592e6, 1144 'Years' : 31536e6 1145 }, 1146 1147 unitAliases = { 1148 ms : 'millisecond', 1149 s : 'second', 1150 m : 'minute', 1151 h : 'hour', 1152 d : 'day', 1153 w : 'week', 1154 M : 'month', 1155 y : 'year' 1156 }, 1157 1158 // format function strings 1159 formatFunctions = {}, 1160 1161 // tokens to ordinalize and pad 1162 ordinalizeTokens = 'DDD w W M D d'.split(' '), 1163 paddedTokens = 'M D H h m s w W'.split(' '), 1164 1165 formatTokenFunctions = { 1166 M : function () { 1167 return this.month() + 1; 1168 }, 1169 MMM : function (format) { 1170 return this.lang().monthsShort(this, format); 1171 }, 1172 MMMM : function (format) { 1173 return this.lang().months(this, format); 1174 }, 1175 D : function () { 1176 return this.date(); 1177 }, 1178 DDD : function () { 1179 return this.dayOfYear(); 1180 }, 1181 d : function () { 1182 return this.day(); 1183 }, 1184 dd : function (format) { 1185 return this.lang().weekdaysMin(this, format); 1186 }, 1187 ddd : function (format) { 1188 return this.lang().weekdaysShort(this, format); 1189 }, 1190 dddd : function (format) { 1191 return this.lang().weekdays(this, format); 1192 }, 1193 w : function () { 1194 return this.week(); 1195 }, 1196 W : function () { 1197 return this.isoWeek(); 1198 }, 1199 YY : function () { 1200 return leftZeroFill(this.year() % 100, 2); 1201 }, 1202 YYYY : function () { 1203 return leftZeroFill(this.year(), 4); 1204 }, 1205 YYYYY : function () { 1206 return leftZeroFill(this.year(), 5); 1207 }, 1208 gg : function () { 1209 return leftZeroFill(this.weekYear() % 100, 2); 1210 }, 1211 gggg : function () { 1212 return this.weekYear(); 1213 }, 1214 ggggg : function () { 1215 return leftZeroFill(this.weekYear(), 5); 1216 }, 1217 GG : function () { 1218 return leftZeroFill(this.isoWeekYear() % 100, 2); 1219 }, 1220 GGGG : function () { 1221 return this.isoWeekYear(); 1222 }, 1223 GGGGG : function () { 1224 return leftZeroFill(this.isoWeekYear(), 5); 1225 }, 1226 e : function () { 1227 return this.weekday(); 1228 }, 1229 E : function () { 1230 return this.isoWeekday(); 1231 }, 1232 a : function () { 1233 return this.lang().meridiem(this.hours(), this.minutes(), true); 1234 }, 1235 A : function () { 1236 return this.lang().meridiem(this.hours(), this.minutes(), false); 1237 }, 1238 H : function () { 1239 return this.hours(); 1240 }, 1241 h : function () { 1242 return this.hours() % 12 || 12; 1243 }, 1244 m : function () { 1245 return this.minutes(); 1246 }, 1247 s : function () { 1248 return this.seconds(); 1249 }, 1250 S : function () { 1251 return ~~(this.milliseconds() / 100); 1252 }, 1253 SS : function () { 1254 return leftZeroFill(~~(this.milliseconds() / 10), 2); 1255 }, 1256 SSS : function () { 1257 return leftZeroFill(this.milliseconds(), 3); 1258 }, 1259 Z : function () { 1260 var a = -this.zone(), 1261 b = "+"; 1262 if (a < 0) { 1263 a = -a; 1264 b = "-"; 1265 } 1266 return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2); 1267 }, 1268 ZZ : function () { 1269 var a = -this.zone(), 1270 b = "+"; 1271 if (a < 0) { 1272 a = -a; 1273 b = "-"; 1274 } 1275 return b + leftZeroFill(~~(10 * a / 6), 4); 1276 }, 1277 z : function () { 1278 return this.zoneAbbr(); 1279 }, 1280 zz : function () { 1281 return this.zoneName(); 1282 }, 1283 X : function () { 1284 return this.unix(); 1285 } 1286 }; 1287 1288 function padToken(func, count) { 1289 return function (a) { 1290 return leftZeroFill(func.call(this, a), count); 1291 }; 1292 } 1293 function ordinalizeToken(func, period) { 1294 return function (a) { 1295 return this.lang().ordinal(func.call(this, a), period); 1296 }; 1297 } 1298 1299 while (ordinalizeTokens.length) { 1300 i = ordinalizeTokens.pop(); 1301 formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); 1302 } 1303 while (paddedTokens.length) { 1304 i = paddedTokens.pop(); 1305 formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); 1306 } 1307 formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); 1308 1309 1310 /************************************ 1311 Constructors 1312 ************************************/ 1313 1314 function Language() { 1315 1316 } 1317 1318 // Moment prototype object 1319 function Moment(config) { 1320 extend(this, config); 1321 } 1322 1323 // Duration Constructor 1324 function Duration(duration) { 1325 var years = duration.years || duration.year || duration.y || 0, 1326 months = duration.months || duration.month || duration.M || 0, 1327 weeks = duration.weeks || duration.week || duration.w || 0, 1328 days = duration.days || duration.day || duration.d || 0, 1329 hours = duration.hours || duration.hour || duration.h || 0, 1330 minutes = duration.minutes || duration.minute || duration.m || 0, 1331 seconds = duration.seconds || duration.second || duration.s || 0, 1332 milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0; 1333 1334 // store reference to input for deterministic cloning 1335 this._input = duration; 1336 1337 // representation for dateAddRemove 1338 this._milliseconds = milliseconds + 1339 seconds * 1e3 + // 1000 1340 minutes * 6e4 + // 1000 * 60 1341 hours * 36e5; // 1000 * 60 * 60 1342 // Because of dateAddRemove treats 24 hours as different from a 1343 // day when working around DST, we need to store them separately 1344 this._days = days + 1345 weeks * 7; 1346 // It is impossible translate months into days without knowing 1347 // which months you are are talking about, so we have to store 1348 // it separately. 1349 this._months = months + 1350 years * 12; 1351 1352 this._data = {}; 1353 1354 this._bubble(); 1355 } 1356 1357 1358 /************************************ 1359 Helpers 1360 ************************************/ 1361 1362 1363 function extend(a, b) { 1364 for (var i in b) { 1365 if (b.hasOwnProperty(i)) { 1366 a[i] = b[i]; 1367 } 1368 } 1369 return a; 1370 } 1371 1372 function absRound(number) { 1373 if (number < 0) { 1374 return Math.ceil(number); 1375 } else { 1376 return Math.floor(number); 1377 } 1378 } 1379 1380 // left zero fill a number 1381 // see http://jsperf.com/left-zero-filling for performance comparison 1382 function leftZeroFill(number, targetLength) { 1383 var output = number + ''; 1384 while (output.length < targetLength) { 1385 output = '0' + output; 1386 } 1387 return output; 1388 } 1389 1390 // helper function for _.addTime and _.subtractTime 1391 function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) { 1392 var milliseconds = duration._milliseconds, 1393 days = duration._days, 1394 months = duration._months, 1395 minutes, 1396 hours, 1397 currentDate; 1398 1399 if (milliseconds) { 1400 mom._d.setTime(+mom._d + milliseconds * isAdding); 1401 } 1402 // store the minutes and hours so we can restore them 1403 if (days || months) { 1404 minutes = mom.minute(); 1405 hours = mom.hour(); 1406 } 1407 if (days) { 1408 mom.date(mom.date() + days * isAdding); 1409 } 1410 if (months) { 1411 mom.month(mom.month() + months * isAdding); 1412 } 1413 if (milliseconds && !ignoreUpdateOffset) { 1414 moment.updateOffset(mom); 1415 } 1416 // restore the minutes and hours after possibly changing dst 1417 if (days || months) { 1418 mom.minute(minutes); 1419 mom.hour(hours); 1420 } 1421 } 1422 1423 // check if is an array 1424 function isArray(input) { 1425 return Object.prototype.toString.call(input) === '[object Array]'; 1426 } 1427 1428 // compare two arrays, return the number of differences 1429 function compareArrays(array1, array2) { 1430 var len = Math.min(array1.length, array2.length), 1431 lengthDiff = Math.abs(array1.length - array2.length), 1432 diffs = 0, 1433 i; 1434 for (i = 0; i < len; i++) { 1435 if (~~array1[i] !== ~~array2[i]) { 1436 diffs++; 1437 } 1438 } 1439 return diffs + lengthDiff; 1440 } 1441 1442 function normalizeUnits(units) { 1443 return units ? unitAliases[units] || units.toLowerCase().replace(/(.)s$/, '$1') : units; 1444 } 1445 1446 1447 /************************************ 1448 Languages 1449 ************************************/ 1450 1451 1452 Language.prototype = { 1453 set : function (config) { 1454 var prop, i; 1455 for (i in config) { 1456 prop = config[i]; 1457 if (typeof prop === 'function') { 1458 this[i] = prop; 1459 } else { 1460 this['_' + i] = prop; 1461 } 1462 } 1463 }, 1464 1465 _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), 1466 months : function (m) { 1467 return this._months[m.month()]; 1468 }, 1469 1470 _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), 1471 monthsShort : function (m) { 1472 return this._monthsShort[m.month()]; 1473 }, 1474 1475 monthsParse : function (monthName) { 1476 var i, mom, regex; 1477 1478 if (!this._monthsParse) { 1479 this._monthsParse = []; 1480 } 1481 1482 for (i = 0; i < 12; i++) { 1483 // make the regex if we don't have it already 1484 if (!this._monthsParse[i]) { 1485 mom = moment([2000, i]); 1486 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 1487 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 1488 } 1489 // test the regex 1490 if (this._monthsParse[i].test(monthName)) { 1491 return i; 1492 } 1493 } 1494 }, 1495 1496 _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), 1497 weekdays : function (m) { 1498 return this._weekdays[m.day()]; 1499 }, 1500 1501 _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), 1502 weekdaysShort : function (m) { 1503 return this._weekdaysShort[m.day()]; 1504 }, 1505 1506 _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), 1507 weekdaysMin : function (m) { 1508 return this._weekdaysMin[m.day()]; 1509 }, 1510 1511 weekdaysParse : function (weekdayName) { 1512 var i, mom, regex; 1513 1514 if (!this._weekdaysParse) { 1515 this._weekdaysParse = []; 1516 } 1517 1518 for (i = 0; i < 7; i++) { 1519 // make the regex if we don't have it already 1520 if (!this._weekdaysParse[i]) { 1521 mom = moment([2000, 1]).day(i); 1522 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); 1523 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); 1524 } 1525 // test the regex 1526 if (this._weekdaysParse[i].test(weekdayName)) { 1527 return i; 1528 } 1529 } 1530 }, 1531 1532 _longDateFormat : { 1533 LT : "h:mm A", 1534 L : "MM/DD/YYYY", 1535 LL : "MMMM D YYYY", 1536 LLL : "MMMM D YYYY LT", 1537 LLLL : "dddd, MMMM D YYYY LT" 1538 }, 1539 longDateFormat : function (key) { 1540 var output = this._longDateFormat[key]; 1541 if (!output && this._longDateFormat[key.toUpperCase()]) { 1542 output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { 1543 return val.slice(1); 1544 }); 1545 this._longDateFormat[key] = output; 1546 } 1547 return output; 1548 }, 1549 1550 isPM : function (input) { 1551 return ((input + '').toLowerCase()[0] === 'p'); 1552 }, 1553 1554 _meridiemParse : /[ap]\.?m?\.?/i, 1555 meridiem : function (hours, minutes, isLower) { 1556 if (hours > 11) { 1557 return isLower ? 'pm' : 'PM'; 1558 } else { 1559 return isLower ? 'am' : 'AM'; 1560 } 1561 }, 1562 1563 _calendar : { 1564 sameDay : '[Today at] LT', 1565 nextDay : '[Tomorrow at] LT', 1566 nextWeek : 'dddd [at] LT', 1567 lastDay : '[Yesterday at] LT', 1568 lastWeek : '[Last] dddd [at] LT', 1569 sameElse : 'L' 1570 }, 1571 calendar : function (key, mom) { 1572 var ou
1572tput = this._calendar[key]; 1573 return typeof output === 'function' ? output.apply(mom) : output; 1574 }, 1575 1576 _relativeTime : { 1577 future : "in %s", 1578 past : "%s ago", 1579 s : "a few seconds", 1580 m : "a minute", 1581 mm : "%d minutes", 1582 h : "an hour", 1583 hh : "%d hours", 1584 d : "a day", 1585 dd : "%d days", 1586 M : "a month", 1587 MM : "%d months", 1588 y : "a year", 1589 yy : "%d years" 1590 }, 1591 relativeTime : function (number, withoutSuffix, string, isFuture) { 1592 var output = this._relativeTime[string]; 1593 return (typeof output === 'function') ? 1594 output(number, withoutSuffix, string, isFuture) : 1595 output.replace(/%d/i, number); 1596 }, 1597 pastFuture : function (diff, output) { 1598 var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 1599 return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); 1600 }, 1601 1602 ordinal : function (number) { 1603 return this._ordinal.replace("%d", number); 1604 }, 1605 _ordinal : "%d", 1606 1607 preparse : function (string) { 1608 return string; 1609 }, 1610 1611 postformat : function (string) { 1612 return string; 1613 }, 1614 1615 week : function (mom) { 1616 return weekOfYear(mom, this._week.dow, this._week.doy).week; 1617 }, 1618 _week : { 1619 dow : 0, // Sunday is the first day of the week. 1620 doy : 6 // The week that contains Jan 1st is the first week of the year. 1621 } 1622 }; 1623 1624 // Loads a language definition into the `languages` cache. The function 1625 // takes a key and optionally values. If not in the browser and no values 1626 // are provided, it will load the language file module. As a convenience, 1627 // this function also returns the language values. 1628 function loadLang(key, values) { 1629 values.abbr = key; 1630 if (!languages[key]) { 1631 languages[key] = new Language(); 1632 } 1633 languages[key].set(values); 1634 return languages[key]; 1635 } 1636 1637 // Determines which language definition to use and returns it. 1638 // 1639 // With no parameters, it will return the global language. If you 1640 // pass in a language key, such as 'en', it will return the 1641 // definition for 'en', so long as 'en' has already been loaded using 1642 // moment.lang. 1643 function getLangDefinition(key) { 1644 if (!key) { 1645 return moment.fn._lang; 1646 } 1647 if (!languages[key] && hasModule) { 1648 try { 1649 require('./lang/' + key); 1650 } catch (e) { 1651 // call with no params to set to default 1652 return moment.fn._lang; 1653 } 1654 } 1655 return languages[key]; 1656 } 1657 1658 1659 /************************************ 1660 Formatting 1661 ************************************/ 1662 1663 1664 function removeFormattingTokens(input) { 1665 if (input.match(/\[.*\]/)) { 1666 return input.replace(/^\[|\]$/g, ""); 1667 } 1668 return input.replace(/\\/g, ""); 1669 } 1670 1671 function makeFormatFunction(format) { 1672 var array = format.match(formattingTokens), i, length; 1673 1674 for (i = 0, length = array.length; i < length; i++) { 1675 if (formatTokenFunctions[array[i]]) { 1676 array[i] = formatTokenFunctions[array[i]]; 1677 } else { 1678 array[i] = removeFormattingTokens(array[i]); 1679 } 1680 } 1681 1682 return function (mom) { 1683 var output = ""; 1684 for (i = 0; i < length; i++) { 1685 output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; 1686 } 1687 return output; 1688 }; 1689 } 1690 1691 // format date using native date object 1692 function formatMoment(m, format) { 1693 var i = 5; 1694 1695 function replaceLongDateFormatTokens(input) { 1696 return m.lang().longDateFormat(input) || input; 1697 } 1698 1699 while (i-- && localFormattingTokens.test(format)) { 1700 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 1701 } 1702 1703 if (!formatFunctions[format]) { 1704 formatFunctions[format] = makeFormatFunction(format); 1705 } 1706 1707 return formatFunctions[format](m); 1708 } 1709 1710 1711 /************************************ 1712 Parsing 1713 ************************************/ 1714 1715 1716 // get the regex to find the next token 1717 function getParseRegexForToken(token, config) { 1718 switch (token) { 1719 case 'DDDD': 1720 return parseTokenThreeDigits; 1721 case 'YYYY': 1722 return parseTokenFourDigits; 1723 case 'YYYYY': 1724 return parseTokenSixDigits; 1725 case 'S': 1726 case 'SS': 1727 case 'SSS': 1728 case 'DDD': 1729 return parseTokenOneToThreeDigits; 1730 case 'MMM': 1731 case 'MMMM': 1732 case 'dd': 1733 case 'ddd': 1734 case 'dddd': 1735 return parseTokenWord; 1736 case 'a': 1737 case 'A': 1738 return getLangDefinition(config._l)._meridiemParse; 1739 case 'X': 1740 return parseTokenTimestampMs; 1741 case 'Z': 1742 case 'ZZ': 1743 return parseTokenTimezone; 1744 case 'T': 1745 return parseTokenT; 1746 case 'MM': 1747 case 'DD': 1748 case 'YY': 1749 case 'HH': 1750 case 'hh': 1751 case 'mm': 1752 case 'ss': 1753 case 'M': 1754 case 'D': 1755 case 'd': 1756 case 'H': 1757 case 'h': 1758 case 'm': 1759 case 's': 1760 return parseTokenOneOrTwoDigits; 1761 default : 1762 return new RegExp(token.replace('\\', '')); 1763 } 1764 } 1765 1766 function timezoneMinutesFromString(string) { 1767 var tzchunk = (parseTokenTimezone.exec(string) || [])[0], 1768 parts = (tzchunk + '').match(parseTimezoneChunker) || ['-', 0, 0], 1769 minutes = +(parts[1] * 60) + ~~parts[2]; 1770 1771 return parts[0] === '+' ? -minutes : minutes; 1772 } 1773 1774 // function to convert string input to date 1775 function addTimeToArrayFromToken(token, input, config) { 1776 var a, datePartArray = config._a; 1777 1778 switch (token) { 1779 // MONTH 1780 case 'M' : // fall through to MM 1781 case 'MM' : 1782 datePartArray[1] = (input == null) ? 0 : ~~input - 1; 1783 break; 1784 case 'MMM' : // fall through to MMMM 1785 case 'MMMM' : 1786 a = getLangDefinition(config._l).monthsParse(input); 1787 // if we didn't find a month name, mark the date as invali
1787d. 1788 if (a != null) { 1789 datePartArray[1] = a; 1790 } else { 1791 config._isValid = false; 1792 } 1793 break; 1794 // DAY OF MONTH 1795 case 'D' : // fall through to DDDD 1796 case 'DD' : // fall through to DDDD 1797 case 'DDD' : // fall through to DDDD 1798 case 'DDDD' : 1799 if (input != null) { 1800 datePartArray[2] = ~~input; 1801 } 1802 break; 1803 // YEAR 1804 case 'YY' : 1805 datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000); 1806 break; 1807 case 'YYYY' : 1808 case 'YYYYY' : 1809 datePartArray[0] = ~~input; 1810 break; 1811 // AM / PM 1812 case 'a' : // fall through to A 1813 case 'A' : 1814 config._isPm = getLangDefinition(config._l).isPM(input); 1815 break; 1816 // 24 HOUR 1817 case 'H' : // fall through to hh 1818 case 'HH' : // fall through to hh 1819 case 'h' : // fall through to hh 1820 case 'hh' : 1821 datePartArray[3] = ~~input; 1822 break; 1823 // MINUTE 1824 case 'm' : // fall through to mm 1825 case 'mm' : 1826 datePartArray[4] = ~~input; 1827 break; 1828 // SECOND 1829 case 's' : // fall through to ss 1830 case 'ss' : 1831 datePartArray[5] = ~~input; 1832 break; 1833 // MILLISECOND 1834 case 'S' : 1835 case 'SS' : 1836 case 'SSS' : 1837 datePartArray[6] = ~~ (('0.' + input) * 1000); 1838 break; 1839 // UNIX TIMESTAMP WITH MS 1840 case 'X': 1841 config._d = new Date(parseFloat(input) * 1000); 1842 break; 1843 // TIMEZONE 1844 case 'Z' : // fall through to ZZ 1845 case 'ZZ' : 1846 config._useUTC = true; 1847 config._tzm = timezoneMinutesFromString(input); 1848 break; 1849 } 1850 1851 // if the input is null, the date is not valid 1852 if (input == null) { 1853 config._isValid = false; 1854 } 1855 } 1856 1857 // convert an array to a date. 1858 // the array should mirror the parameters below 1859 // note: all values past the year are optional and will default to the lowest possible value. 1860 // [year, month, day , hour, minute, second, millisecond] 1861 function dateFromArray(config) { 1862 var i, date, input = []; 1863 1864 if (config._d) { 1865 return; 1866 } 1867 1868 for (i = 0; i < 7; i++) { 1869 config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 1870 } 1871 1872 // add the offsets to the time to be parsed so that we can have a clean array for checking isValid 1873 input[3] += ~~((config._tzm || 0) / 60); 1874 input[4] += ~~((config._tzm || 0) % 60); 1875 1876 date = new Date(0); 1877 1878 if (config._useUTC) { 1879 date.setUTCFullYear(input[0], input[1], input[2]); 1880 date.setUTCHours(input[3], input[4], input[5], input[6]); 1881 } else { 1882 date.setFullYear(input[0], input[1], input[2]); 1883 date.setHours(input[3], input[4], input[5], input[6]); 1884 } 1885 1886 config._d = date; 1887 } 1888 1889 // date from string and format string 1890 function makeDateFromStringAndFormat(config) { 1891 // This array is used to make a Date, either with `new Date` or `Date.UTC` 1892 var tokens = config._f.match(formattingTokens), 1893 string = config._i, 1894 i, parsedInput; 1895 1896 config._a = []; 1897 1898 for (i = 0; i < tokens.length; i++) { 1899 parsedInput = (getParseRegexForToken(tokens[i], config).exec(string) || [])[0]; 1900 if (parsedInput) { 1901 string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 1902 } 1903 // don't parse if its not a known token 1904 if (formatTokenFunctions[tokens[i]]) { 1905 addTimeToArrayFromToken(tokens[i], parsedInput, config); 1906 } 1907 } 1908 1909 // add remaining unparsed input to the string 1910 if (string) { 1911 config._il = string; 1912 } 1913 1914 // handle am pm 1915 if (config._isPm && config._a[3] < 12) { 1916 config._a[3] += 12; 1917 } 1918 // if is 12 am, change hours to 0 1919 if (config._isPm === false && config._a[3] === 12) { 1920 config._a[3] = 0; 1921 } 1922 // return 1923 dateFromArray(config); 1924 } 1925
1926 // date from string and array of format strings 1927 function makeDateFromStringAndArray(config) { 1928 var tempConfig, 1929 tempMoment, 1930 bestMoment, 1931 1932 scoreToBeat = 99, 1933 i, 1934 currentScore; 1935 1936 for (i = 0; i < config._f.length; i++) { 1937 tempConfig = extend({}, config); 1938 tempConfig._f = config._f[i]; 1939 makeDateFromStringAndFormat(tempConfig); 1940 tempMoment = new Moment(tempConfig); 1941 1942 currentScore = compareArrays(tempConfig._a, tempMoment.toArray()); 1943 1944 // if there is any input that was not parsed 1945 // add a penalty for that format 1946 if (tempMoment._il) { 1947 currentScore += tempMoment._il.length; 1948 } 1949 1950 if (currentScore < scoreToBeat) { 1951 scoreToBeat = currentScore; 1952 bestMoment = tempMoment; 1953 } 1954 } 1955 1956 extend(config, bestMoment); 1957 } 1958 1959 // date from iso format 1960 function makeDateFromString(config) { 1961 var i, 1962 string = config._i, 1963 match = isoRegex.exec(string); 1964 1965 if (match) { 1966 // match[2] should be "T" or undefined 1967 config._f = 'YYYY-MM-DD' + (match[2] || " "); 1968 for (i = 0; i < 4; i++) { 1969 if (isoTimes[i][1].exec(string)) { 1970 config._f += isoTimes[i][0]; 1971 break; 1972 } 1973 } 1974 if (parseTokenTimezone.exec(string)) { 1975 config._f += " Z"; 1976 } 1977 makeDateFromStringAndFormat(config); 1978 } else { 1979 config._d = new Date(string); 1980 } 1981 } 1982 1983 function makeDateFromInput(config) { 1984 var input = config._i, 1985 matched = aspNetJsonRegex.exec(input); 1986 1987 if (input === undefined) { 1988 config._d = new Date(); 1989 } else if (matched) { 1990 config._d = new Date(+matched[1]); 1991 } else if (typeof input === 'string') { 1992 makeDateFromString(config); 1993 } else if (isArray(input)) { 1994 config._a = input.slice(0); 1995 dateFromArray(config); 1996 } else { 1997 config._d = input instanceof Date ? new Date(+input) : new Date(input); 1998 } 1999 } 2000 2001 2002 /************************************ 2003 Relative Time 2004 ************************************/ 2005 2006 2007 // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 2008 function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { 2009 return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 2010 } 2011 2012 function relativeTime(milliseconds, withoutSuffix, lang) { 2013 var seconds = round(Math.abs(milliseconds) / 1000), 2014 minutes = round(seconds / 60), 2015 hours = round(minutes / 60), 2016 days = round(hours / 24), 2017 years = round(days / 365), 2018 args = seconds < 45 && ['s', seconds] || 2019 minutes === 1 && ['m'] || 2020 minutes < 45 && ['mm', minutes] || 2021 hours === 1 && ['h'] || 2022 hours < 22 && ['hh', hours] || 2023 days === 1 && ['d'] || 2024 days <= 25 && ['dd', days] || 2025 days <= 45 && ['M'] || 2026 days < 345 && ['MM', round(days / 30)] || 2027 years === 1 && ['y'] || ['yy', years]; 2028 args[2] = withoutSuffix; 2029 args[3] = milliseconds > 0; 2030 args[4] = lang; 2031 return substituteTimeAgo.apply({}, args); 2032 } 2033 2034 2035 /************************************ 2036 Week of Year 2037 ************************************/ 2038 2039 2040 // firstDayOfWeek 0 = sun, 6 = sat 2041 // the day of the week that starts the week 2042 // (usually sunday or monday) 2043 // firstDayOfWeekOfYear 0 = sun, 6 = sat 2044 // the first week is the week that contains the first 2045 // of this day of the week 2046 // (eg. ISO weeks use thursday (4)) 2047 function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { 2048 var end = firstDayOfWeekOfYear - firstDayOfWeek, 2049 daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), 2050 adjustedMoment; 2051 2052 2053 if (daysToDayOfWeek > end) { 2054 daysToDayOfWeek -= 7; 2055 } 2056 2057 if (daysToDayOfWeek < end - 7) { 2058 daysToDayOfWeek += 7; 2059 } 2060 2061 adjustedMoment = moment(mom).add('d', daysToDayOfWeek); 2062 return { 2063 week: Math.ceil(adjustedMoment.dayOfYear() / 7), 2064 year: adjustedMoment.year() 2065 }; 2066 } 2067 2068 2069 /************************************ 2070 Top Level Functions 2071 ************************************/ 2072 2073 function makeMoment(config) { 2074 var input = config._i, 2075 format = config._f; 2076 2077 if (input === null || input === '') { 2078 return null; 2079 } 2080 2081 if (typeof input === 'string') { 2082 config._i = input = getLangDefinition().preparse(input); 2083 } 2084 2085 if (moment.isMoment(input)) { 2086 config = extend({}, input);
2087 config._d = new Date(+input._d); 2088 } else if (format) { 2089 if (isArray(format)) { 2090 makeDateFromStringAndArray(config); 2091 } else { 2092 makeDateFromStringAndFormat(config); 2093 } 2094 } else { 2095 makeDateFromInput(config); 2096 } 2097 2098 return new Moment(config); 2099 } 2100 2101 moment = function (input, format, lang) { 2102 return makeMoment({ 2103 _i : input, 2104 _f : format, 2105 _l : lang, 2106 _isUTC : false 2107 }); 2108 }; 2109 2110 // creating with utc 2111 moment.utc = function (input, format, lang) { 2112 return makeMoment({ 2113 _useUTC : true, 2114 _isUTC : true, 2115 _l : lang, 2116 _i : input, 2117 _f : format 2118 }); 2119 }; 2120 2121 // creating with unix timestamp (in seconds) 2122 moment.unix = function (input) { 2123 return moment(input * 1000); 2124 }; 2125 2126 // duration 2127 moment.duration = function (input, key) { 2128 var isDuration = moment.isDuration(input), 2129 isNumber = (typeof input === 'number'), 2130 duration = (isDuration ? input._input : (isNumber ? {} : input)), 2131 matched = aspNetTimeSpanJsonRegex.exec(input), 2132 sign, 2133 ret; 2134 2135 if (isNumber) { 2136 if (key) { 2137 duration[key] = input; 2138 } else { 2139 duration.milliseconds = input; 2140 } 2141 } else if (matched) { 2142 sign = (matched[1] === "-") ? -1 : 1; 2143 duration = { 2144 y: 0, 2145 d: ~~matched[2] * sign, 2146 h: ~~matched[3] * sign, 2147 m: ~~matched[4] * sign, 2148 s: ~~matched[5] * sign, 2149 ms: ~~matched[6] * sign 2150 }; 2151 } 2152 2153 ret = new Duration(duration); 2154 2155 if (isDuration && input.hasOwnProperty('_lang')) { 2156 ret._lang = input._lang; 2157 } 2158 2159 return ret; 2160 }; 2161 2162 // version number 2163 moment.version = VERSION; 2164 2165 // default format 2166 moment.defaultFormat = isoFormat; 2167 2168 // This function will be called whenever a moment is mutated. 2169 // It is intended to keep the offset in sync with the timezone. 2170 moment.updateOffset = function () {}; 2171 2172 // This function will load languages and then set the global language. If 2173 // no arguments are passed in, it will simply return the current global 2174 // language key. 2175 moment.lang = function (key, values) { 2176 if (!key) { 2177 return moment.fn._lang._abbr; 2178 } 2179 if (values) { 2180 loadLang(key, values); 2181 } else if (!languages[key]) { 2182 getLangDefinition(key); 2183 } 2184 moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key); 2185 }; 2186 2187 // returns language data 2188 moment.langData = function (key) { 2189 if (key && key._lang && key._lang._abbr) { 2190 key = key._lang._abbr; 2191 } 2192 return getLangDefinition(key); 2193 }; 2194 2195 // compare moment object 2196 moment.isMoment = function (obj) { 2197 return obj instanceof Moment; 2198 }; 2199 2200 // for typechecking Duration objects 2201 moment.isDuration = function (obj) { 2202 return obj instanceof Duration; 2203 }; 2204 2205 2206 /************************************ 2207 Moment Prototype 2208 ************************************/ 2209 2210 2211 moment.fn = Moment.prototype = { 2212 2213 clone : function () { 2214 return moment(this); 2215 }, 2216 2217 valueOf : function () { 2218 return +this._d + ((this._offset || 0) * 60000); 2219 }, 2220 2221 unix : function () { 2222 return Math.floor(+this / 1000); 2223 }, 2224 2225 toString : function () { 2226 return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); 2227 }, 2228 2229 toDate : function () { 2230 return this._offset ? new Date(+this) : this._d; 2231 }, 2232 2233 toISOString : function () { 2234 return formatMoment(moment(this).utc(), 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); 2235 }, 2236 2237 toArray : function () { 2238 var m = this; 2239 return [ 2240 m.year(), 2241 m.month(), 2242 m.date(), 2243 m.hours(), 2244 m.minutes(), 2245 m.seconds(), 2246 m.milliseconds() 2247 ]; 2248 }, 2249 2250 isValid : function () { 2251 if (this._isValid == null) { 2252 if (this._a) { 2253 this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()); 2254 } else { 2255 this._isValid = !isNaN(this._d.getTime()); 2256 } 2257 } 2258 return !!this._isValid; 2259 }, 2260 2261 utc : function () { 2262 return this.zone(0); 2263 }, 2264 2265 local : function () { 2266 this.zone(0); 2267 this._isUTC = false;
2268 return this; 2269 }, 2270 2271 format : function (inputString) { 2272 var output = formatMoment(this, inputString || moment.defaultFormat); 2273 return this.lang().postformat(output); 2274 }, 2275 2276 add : function (input, val) { 2277 var dur; 2278 // switch args to support add('s', 1) and add(1, 's') 2279 if (typeof input === 'string') { 2280 dur = moment.duration(+val, input); 2281 } else { 2282 dur = moment.duration(input, val); 2283 } 2284 addOrSubtractDurationFromMoment(this, dur, 1); 2285 return this; 2286 }, 2287 2288 subtract : function (input, val) { 2289 var dur; 2290 // switch args to support subtract('s', 1) and subtract(1, 's') 2291 if (typeof input === 'string') { 2292 dur = moment.duration(+val, input); 2293 } else { 2294 dur = moment.duration(input, val); 2295 } 2296 addOrSubtractDurationFromMoment(this, dur, -1); 2297 return this; 2298 }, 2299 2300 diff : function (input, units, asFloat) { 2301 var that = this._isUTC ? moment(input).zone(this._offset || 0) : moment(input).local(), 2302 zoneDiff = (this.zone() - that.zone()) * 6e4, 2303 diff, output; 2304 2305 units = normalizeUnits(units); 2306 2307 if (units === 'year' || units === 'month') { 2308 // average number of days in the months in the given dates 2309 diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2 2310 // difference in months 2311 output = ((this.year() - that.year()) * 12) + (this.month() - that.month()); 2312 // adjust by taking difference in days, average number of days 2313 // and dst in the given months. 2314 output += ((this - moment(this).startOf('month')) - 2315 (that - moment(that).startOf('month'))) / diff; 2316 // same as above but with zones, to negate all dst 2317 output -= ((this.zone() - moment(this).startOf('month').zone()) - 2318 (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff; 2319 if (units === 'year') { 2320 output = output / 12; 2321 } 2322 } else { 2323 diff = (this - that); 2324 output = units === 'second' ? diff / 1e3 : // 1000 2325 units === 'minute' ? diff / 6e4 : // 1000 * 60 2326 units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 2327 units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst 2328 units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst 2329 diff; 2330 } 2331 return asFloat ? output : absRound(output); 2332 }, 2333 2334 from : function (time, withoutSuffix) { 2335 return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix); 2336 }, 2337 2338 fromNow : function (withoutSuffix) { 2339 return this.from(moment(), withoutSuffix); 2340 }, 2341 2342 calendar : function () { 2343 var diff = this.diff(moment().startOf('day'), 'days', true), 2344 format = diff < -6 ? 'sameElse' : 2345 diff < -1 ? 'lastWeek' : 2346 diff < 0 ? 'lastDay' : 2347 diff < 1 ? 'sameDay' : 2348 diff < 2 ? 'nextDay' : 2349 diff < 7 ? 'nextWeek' : 'sameElse'; 2350 return this.format(this.lang().calendar(format, this)); 2351 }, 2352 2353 isLeapYear : function () { 2354 var year = this.year(); 2355 return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 2356 }, 2357 2358 isDST : function () { 2359 return (this.zone() < this.clone().month(0).zone() || 2360 this.zone() < this.clone().month(5).zone()); 2361 }, 2362 2363 day : function (input) { 2364 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 2365 if (input != null) { 2366 if (typeof input === 'string') { 2367 input = this.lang().weekdaysParse(input); 2368 if (typeof input !== 'number') { 2369 return this; 2370 } 2371 } 2372 return this.add({ d : input - day }); 2373 } else { 2374 return day; 2375 } 2376 }, 2377 2378 month : function (input) { 2379 var utc = this._isUTC ? 'UTC' : '', 2380 dayOfMonth, 2381 daysInMonth; 2382 2383 if (input != null) { 2384 if (typeof input === 'string') { 2385 input = this.lang().monthsParse(input); 2386 if (typeof input !== 'number') { 2387 return this; 2388 } 2389 } 2390 2391 dayOfMonth = this.date(); 2392 this.date(1); 2393 this._d['set' + utc + 'Month'](input); 2394 this.date(Math.min(dayOfMonth, this.daysInMonth())); 2395 2396 moment.updateOffset(this); 2397 return this; 2398 } else { 2399 return this._d['get' + utc + 'Month'](); 2400 } 2401 }, 2402 2403 startOf: function (units) { 2404 units = normalizeUnits(units); 2405 // the following switch intentionally omits break keywords 2406 // to utilize falling through the cases. 2407 switch (units) { 2408 case 'year': 2409 this.month(0); 2410 /* falls through */ 2411 case 'month': 2412 this.date(1); 2413 /* falls through */ 2414 case 'week': 2415 case 'day': 2416 this.hours(0); 2417 /* falls through */ 2418 case 'hour': 2419 this.minutes(0); 2420 /* falls through */ 2421 case 'minute': 2422 this.seconds(0); 2423 /* falls through */ 2424 case 'second': 2425 this.milliseconds(0); 2426 /* falls through */ 2427 } 2428 2429 // weeks are a special case 2430 if (units === 'week') { 2431 this.weekday(0); 2432 } 2433 2434 return this; 2435 }, 2436 2437 endOf: function (units) { 2438 return this.startOf(units).add(units, 1).subtract('ms', 1); 2439 }, 2440 2441 isAfter: function (input, units) { 2442 units = typeof units !== 'undefined' ? units : 'millisecond'; 2443 return +this.clone().startOf(units) > +moment(input).startOf(units); 2444 }, 2445 2446 isBefore: function (input, units) { 2447 units = typeof units !== 'undefined' ? units : 'millisecond'; 2448 return +this.clone().startOf(units) < +moment(input).startOf(units); 2449 }, 2450 2451 isSame: function (input, units) { 2452 units = typeof units !== 'undefined' ? units : 'millisecond'; 2453 return +this.clone().startOf(units) === +moment(input).startOf(units); 2454 }, 2455 2456 min: function (other) { 2457 other = moment.apply(null, arguments);
2458 return other < this ? this : other; 2459 }, 2460 2461 max: function (other) { 2462 other = moment.apply(null, arguments); 2463 return other > this ? this : other; 2464 }, 2465 2466 zone : function (input) { 2467 var offset = this._offset || 0; 2468 if (input != null) { 2469 if (typeof input === "string") { 2470 input = timezoneMinutesFromString(input); 2471 } 2472 if (Math.abs(input) < 16) { 2473 input = input * 60; 2474 } 2475 this._offset = input; 2476 this._isUTC = true; 2477 if (offset !== input) { 2478 addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true); 2479 } 2480 } else { 2481 return this._isUTC ? offset : this._d.getTimezoneOffset(); 2482 } 2483 return this; 2484 }, 2485 2486 zoneAbbr : function () { 2487 return this._isUTC ? "UTC" : ""; 2488 }, 2489 2490 zoneName : function () { 2491 return this._isUTC ? "Coordinated Universal Time" : ""; 2492 }, 2493 2494 daysInMonth : function () { 2495 return moment.utc([this.year(), this.month() + 1, 0]).date(); 2496 }, 2497 2498 dayOfYear : function (input) { 2499 var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; 2500 return input == null ? dayOfYear : this.add("d", (input - dayOfYear)); 2501 }, 2502 2503 weekYear : function (input) { 2504 var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year; 2505 return input == null ? year : this.add("y", (input - year)); 2506 }, 2507 2508 isoWeekYear : function (input) { 2509 var year = weekOfYear(this, 1, 4).year; 2510 return input == null ? year : this.add("y", (input - year)); 2511 }, 2512 2513 week : function (input) { 2514 var week = this.lang().week(this); 2515 return input == null ? week : this.add("d", (input - week) * 7); 2516 }, 2517 2518 isoWeek : function (input) { 2519 var week = weekOfYear(this, 1, 4).week; 2520 return input == null ? week : this.add("d", (input - week) * 7); 2521 }, 2522 2523 weekday : function (input) { 2524 var weekday = (this._d.getDay() + 7 - this.lang()._week.dow) % 7; 2525 return input == null ? weekday : this.add("d", input - weekday); 2526 }, 2527 2528 isoWeekday : function (input) { 2529 // behaves the same as moment#day except 2530 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) 2531 // as a setter, sunday should belong to the previous week. 2532 return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); 2533 }, 2534 2535 // If passed a language key, it will set the language for this 2536 // instance. Otherwise, it will return the language configuration 2537 // variables for this instance. 2538 lang : function (key) { 2539 if (key === undefined) { 2540 return this._lang; 2541 } else { 2542 this._lang = getLangDefinition(key); 2543 return this; 2544 } 2545 } 2546 }; 2547 2548 // helper for adding shortcuts 2549 function makeGetterAndSetter(name, key) { 2550 moment.fn[name] = moment.fn[name + 's'] = function (input) { 2551 var utc = this._isUTC ? 'UTC' : ''; 2552 if (input != null) { 2553 this._d['set' + utc + key](input); 2554 moment.updateOffset(this); 2555 return this; 2556 } else { 2557 return this._d['get' + utc + key](); 2558 } 2559 }; 2560 } 2561 2562 // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds) 2563 for (i = 0; i < proxyGettersAndSetters.length; i ++) { 2564 makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]); 2565 } 2566 2567 // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear') 2568 makeGetterAndSetter('year', 'FullYear'); 2569 2570 // add plural methods 2571 moment.fn.days = moment.fn.day; 2572 moment.fn.months = moment.fn.month; 2573 moment.fn.weeks = moment.fn.week; 2574 moment.fn.isoWeeks = moment.fn.isoWeek; 2575 2576 // add aliased format methods 2577 moment.fn.toJSON = moment.fn.toISOString; 2578 2579 /************************************ 2580 Duration Prototype 2581 ************************************/ 2582 2583 2584 moment.duration.fn = Duration.prototype = { 2585 _bubble : function () { 2586 var milliseconds = this._milliseconds, 2587 days = this._days, 2588 months = this._months, 2589 data = this._data, 2590 seconds, minutes, hours, years; 2591 2592 // The following code bubbles up values, see the tests for 2593 // examples of what that means. 2594 data.milliseconds = milliseconds % 1000; 2595 2596 seconds = absRound(milliseconds / 1000); 2597 data.seconds = seconds % 60; 2598 2599 minutes = absRound(seconds / 60); 2600 data.minutes = minutes % 60; 2601 2602 hours = absRound(minutes / 60); 2603 data.hours = hours % 24; 2604 2605 days += absRound(hours / 24); 2606 data.days = days % 30; 2607 2608 months += absRound(days / 30); 2609 data.months = months % 12; 2610 2611 years = absRound(months / 12); 2612 data.years = years; 2613 }, 2614 2615 weeks : function () { 2616 return absRound(this.days() / 7); 2617 }, 2618 2619 valueOf : function () { 2620 return this._milliseconds + 2621 this._days * 864e5 + 2622 (this._months % 12) * 2592e6 + 2623 ~~(this._months / 12) * 31536e6; 2624 }, 2625 2626 humanize : function (withSuffix) { 2627 var difference = +this, 2628 output = relativeTime(difference, !withSuffix, this.lang()); 2629 2630 if (withSuffix) { 2631 output = this.lang().pastFuture(difference, output); 2632 } 2633
2634 return this.lang().postformat(output); 2635 }, 2636 2637 add : function (input, val) { 2638 // supports only 2.0-style add(1, 's') or add(moment) 2639 var dur = moment.duration(input, val); 2640 2641 this._milliseconds += dur._milliseconds; 2642 this._days += dur._days; 2643 this._months += dur._months; 2644 2645 this._bubble(); 2646 2647 return this; 2648 }, 2649 2650 subtract : function (input, val) { 2651 var dur = moment.duration(input, val); 2652 2653 this._milliseconds -= dur._milliseconds; 2654 this._days -= dur._days; 2655 this._months -= dur._months; 2656 2657 this._bubble(); 2658 2659 return this; 2660 }, 2661 2662 get : function (units) { 2663 units = normalizeUnits(units); 2664 return this[units.toLowerCase() + 's'](); 2665 }, 2666 2667 as : function (units) { 2668 units = normalizeUnits(units); 2669 return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's'](); 2670 }, 2671 2672 lang : moment.fn.lang 2673 }; 2674 2675 function makeDurationGetter(name) { 2676 moment.duration.fn[name] = function () { 2677 return this._data[name]; 2678 }; 2679 } 2680 2681 function makeDurationAsGetter(name, factor) { 2682 moment.duration.fn['as' + name] = function () { 2683 return +this / factor; 2684 }; 2685 } 2686 2687 for (i in unitMillisecondFactors) { 2688 if (unitMillisecondFactors.hasOwnProperty(i)) { 2689 makeDurationAsGetter(i, unitMillisecondFactors[i]); 2690 makeDurationGetter(i.toLowerCase()); 2691 } 2692 } 2693 2694 makeDurationAsGetter('Weeks', 6048e5); 2695 moment.duration.fn.asMonths = function () { 2696 return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12; 2697 }; 2698 2699 2700 /************************************ 2701 Default Lang 2702 ************************************/ 2703 2704 2705 // Set default language, other languages will inherit from English. 2706 moment.lang('en', { 2707 ordinal : function (number) { 2708 var b = number % 10, 2709 output = (~~ (number % 100 / 10) === 1) ? 'th' : 2710 (b === 1) ? 'st' : 2711 (b === 2) ? 'nd' : 2712 (b === 3) ? 'rd' : 'th'; 2713 return number + output; 2714 } 2715 }); 2716 2717 2718 /************************************ 2719 Exposing Moment 2720 ************************************/ 2721 2722 2723 // CommonJS module is defined 2724 if (hasModule) { 2725 module.exports = moment; 2726 } 2727 /*global ender:false */ 2728 if (typeof ender === 'undefined') { 2729 // here, `this` means `window` in the browser, or `global` on the server 2730 // add `moment` as a global object via a string identifier, 2731 // for Closure Compiler "advanced" mode 2732 this['moment'] = moment; 2733 } 2734 /*global define:false */ 2735 if (typeof define === "function" && define.amd) { 2736 define("moment", [], function () { 2737 return moment; 2738 }); 2739 } 2740 /************************************ 2741 Exposing Moment 2742 ************************************/ 2743 2744 this['moment'] = moment; 2745 2746}).call(typeof Kalendae === 'undefined' ? window : Kalendae); 2747 2748if (!Kalendae.moment) { 2749 if (window.moment) { 2750 Kalendae.moment = window.moment; 2751 } else { 2752 throw "Kalendae requires moment.js. You must use kalendae.standalone.js if moment is not available on the page."; 2753 } 2754} 2755 2756moment = Kalendae.moment; 2757 2758//function to get the total number of days since the epoch. 2759moment.fn.yearDay = function (input) { 2760 var yearday = Math.floor(this._d / 86400000); 2761 return (typeof input === 'undefined') ? yearday : 2762 this.add({ d : input - yearday }); 2763}; 2764 2765today = Kalendae.moment().startOf('day'); 2766 2767if (typeof jQuery !== 'undefined' && (typeof document.addEventListener === 'function' || util.isIE8())) { 2768 jQuery.fn.kalendae = function (options) { 2769 this.each(function (i, e) { 2770 if (e.tagName === 'INPUT') { 2771 //if element is an input, bind a popup calendar to the input. 2772 $(e).data('kalendae', new Kalendae.Input(e, options)); 2773 } else { 2774 //otherwise, insert a flat calendar into the element. 2775 $(e).data('kalendae', new Kalendae($.extend({}, {attachTo:e}, options))); 2776 } 2777 }); 2778 return this; 2779 }; 2780} 2781 2782 2783})();
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.