kalendae_assets 0.2.1 → 0.4.1

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