bootstrap-daterangepicker-rails 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe234cb59f9a330ab68b7807aeffd66904de7b04
4
- data.tar.gz: b9f117c35cd06d4263a10364b5ca6e7d0e063efb
3
+ metadata.gz: 74d416af849d9db6a14e6c7f8482f1004e50b412
4
+ data.tar.gz: 797929b9a3d13cf612c4e46eace69c57618cb678
5
5
  SHA512:
6
- metadata.gz: 6f7a5ee39f49513c5d802d9dcd78c8e933227077cbebbaca993685d2338b3ae46aed624c59220f6e3e320e0e04939acc2dbeb3d5a455665a8393060dc4139c1e
7
- data.tar.gz: 6720d6b7b099e688bdef175032929694dd4428b78969fa9a73a995e8815d16d84e0da2fe7a4acb56cee76e2636d9c5c8ad8f13c203eee902bb0a13d4c84951c7
6
+ metadata.gz: e439b6cfe45fee1e62511918638de8e8ca626615dae18f4637199937dd6905cb2c58bee20767434fe31f71f118f1dcd96d177952cdbc0d2b41b8e7743f311293
7
+ data.tar.gz: e0492dcca44816bfab49044b360a4b7f8a91ef42f3b78ed1a91d4d797facc2f74097139ab308af86e48939d0423e80b02d672bbe220b62b20716449c03cb7fa7
@@ -1,7 +1,7 @@
1
1
  module Bootstrap
2
2
  module Daterangepicker
3
3
  module Rails
4
- VERSION = '0.0.7'
4
+ VERSION = '0.0.8'
5
5
  end
6
6
  end
7
7
  end
@@ -25,7 +25,7 @@
25
25
  this.timePicker = false;
26
26
  this.timePickerIncrement = 30;
27
27
  this.timePicker12Hour = true;
28
- this.ranges = {};
28
+ this.ranges = {};
29
29
  this.opens = 'right';
30
30
 
31
31
  this.buttonClasses = ['btn', 'btn-small'];
@@ -42,13 +42,16 @@
42
42
  toLabel: 'To',
43
43
  weekLabel: 'W',
44
44
  customRangeLabel: 'Custom Range',
45
- daysOfWeek: moment()._lang._weekdaysMin,
46
- monthNames: moment()._lang._monthsShort,
45
+ daysOfWeek: moment()._lang._weekdaysMin.slice(),
46
+ monthNames: moment()._lang._monthsShort.slice(),
47
47
  firstDay: 0
48
48
  };
49
49
 
50
50
  this.cb = function () { };
51
51
 
52
+ // by default, the daterangepicker element is placed at the bottom of HTML body
53
+ this.parentEl = 'body';
54
+
52
55
  //element that triggered the date range picker
53
56
  this.element = $(element);
54
57
 
@@ -101,7 +104,9 @@
101
104
  '</div>' +
102
105
  '</div>';
103
106
 
104
- this.container = $(DRPTemplate).appendTo('body');
107
+ this.parentEl = (hasOptions && options.parentEl && $(options.parentEl)) || $(this.parentEl);
108
+ //the date range picker
109
+ this.container = $(DRPTemplate).appendTo(this.parentEl);
105
110
 
106
111
  if (hasOptions) {
107
112
 
@@ -264,8 +269,8 @@
264
269
  }
265
270
 
266
271
  //state
267
- this.oldStartDate = this.startDate;
268
- this.oldEndDate = this.endDate;
272
+ this.oldStartDate = this.startDate.clone();
273
+ this.oldEndDate = this.endDate.clone();
269
274
 
270
275
  this.leftCalendar = {
271
276
  month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute()]),
@@ -279,25 +284,24 @@
279
284
 
280
285
  //event listeners
281
286
  this.container.on('mousedown', $.proxy(this.mousedown, this));
282
- this.container.find('.calendar').on('click', '.prev', $.proxy(this.clickPrev, this));
283
- this.container.find('.calendar').on('click', '.next', $.proxy(this.clickNext, this));
284
- this.container.find('.ranges').on('click', 'button.applyBtn', $.proxy(this.clickApply, this));
285
- this.container.find('.ranges').on('click', 'button.cancelBtn', $.proxy(this.clickCancel, this));
286
-
287
- this.container.find('.calendar').on('click', 'td.available', $.proxy(this.clickDate, this));
288
- this.container.find('.calendar').on('mouseenter', 'td.available', $.proxy(this.enterDate, this));
289
- this.container.find('.calendar').on('mouseleave', 'td.available', $.proxy(this.updateView, this));
290
-
291
- this.container.find('.ranges').on('click', 'li', $.proxy(this.clickRange, this));
292
- this.container.find('.ranges').on('mouseenter', 'li', $.proxy(this.enterRange, this));
293
- this.container.find('.ranges').on('mouseleave', 'li', $.proxy(this.updateView, this));
294
287
 
295
- this.container.find('.calendar').on('change', 'select.yearselect', $.proxy(this.updateYear, this));
296
- this.container.find('.calendar').on('change', 'select.monthselect', $.proxy(this.updateMonth, this));
297
-
298
- this.container.find('.calendar').on('change', 'select.hourselect', $.proxy(this.updateTime, this));
299
- this.container.find('.calendar').on('change', 'select.minuteselect', $.proxy(this.updateTime, this));
300
- this.container.find('.calendar').on('change', 'select.ampmselect', $.proxy(this.updateTime, this));
288
+ this.container.find('.calendar')
289
+ .on('click', '.prev', $.proxy(this.clickPrev, this))
290
+ .on('click', '.next', $.proxy(this.clickNext, this))
291
+ .on('click', 'td.available', $.proxy(this.clickDate, this))
292
+ .on('mouseenter', 'td.available', $.proxy(this.enterDate, this))
293
+ .on('mouseleave', 'td.available', $.proxy(this.updateFormInputs, this))
294
+ .on('change', 'select.yearselect', $.proxy(this.updateMonthYear, this))
295
+ .on('change', 'select.monthselect', $.proxy(this.updateMonthYear, this))
296
+ .on('change', 'select.hourselect,select.minuteselect,select.ampmselect', $.proxy(this.updateTime, this));
297
+
298
+ this.container.find('.ranges')
299
+ .on('click', 'button.applyBtn', $.proxy(this.clickApply, this))
300
+ .on('click', 'button.cancelBtn', $.proxy(this.clickCancel, this))
301
+ .on('click', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
302
+ .on('click', 'li', $.proxy(this.clickRange, this))
303
+ .on('mouseenter', 'li', $.proxy(this.enterRange, this))
304
+ .on('mouseleave', 'li', $.proxy(this.updateFormInputs, this));
301
305
 
302
306
  this.element.on('keyup', $.proxy(this.updateFromControl, this));
303
307
 
@@ -317,7 +321,10 @@
317
321
  updateView: function () {
318
322
  this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
319
323
  this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
324
+ this.updateFormInputs();
325
+ },
320
326
 
327
+ updateFormInputs: function () {
321
328
  this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
322
329
  this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
323
330
 
@@ -339,11 +346,15 @@
339
346
  if (start == null || end == null) return;
340
347
  if (end.isBefore(start)) return;
341
348
 
349
+ this.oldStartDate = this.startDate.clone();
350
+ this.oldEndDate = this.endDate.clone();
351
+
342
352
  this.startDate = start;
343
353
  this.endDate = end;
344
354
 
345
- this.updateView();
346
- this.cb(this.startDate, this.endDate);
355
+ if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
356
+ this.notify();
357
+
347
358
  this.updateCalendars();
348
359
  },
349
360
 
@@ -353,17 +364,15 @@
353
364
  },
354
365
 
355
366
  move: function () {
356
- var minWidth = $(this.container).find('.ranges').outerWidth();
357
- if ($(this.container).find('.calendar').is(':visible')) {
358
- var padding = 24; // FIXME: this works for the default styling, but isn't flexible
359
- minWidth += $(this.container).find('.calendar').outerWidth() * 2 + padding;
360
- }
367
+ var parentOffset = {
368
+ top: this.parentEl.offset().top - (this.parentEl.is('body') ? 0 : this.parentEl.scrollTop()),
369
+ left: this.parentEl.offset().left - (this.parentEl.is('body') ? 0 : this.parentEl.scrollLeft())
370
+ };
361
371
  if (this.opens == 'left') {
362
372
  this.container.css({
363
- top: this.element.offset().top + this.element.outerHeight(),
364
- right: $(window).width() - this.element.offset().left - this.element.outerWidth(),
365
- left: 'auto',
366
- 'min-width': minWidth
373
+ top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
374
+ right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
375
+ left: 'auto'
367
376
  });
368
377
  if (this.container.offset().left < 0) {
369
378
  this.container.css({
@@ -373,10 +382,9 @@
373
382
  }
374
383
  } else {
375
384
  this.container.css({
376
- top: this.element.offset().top + this.element.outerHeight(),
377
- left: this.element.offset().left,
378
- right: 'auto',
379
- 'min-width': minWidth
385
+ top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
386
+ left: this.element.offset().left - parentOffset.left,
387
+ right: 'auto'
380
388
  });
381
389
  if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
382
390
  this.container.css({
@@ -396,9 +404,6 @@
396
404
  e.preventDefault();
397
405
  }
398
406
 
399
- this.oldStartDate = this.startDate;
400
- this.oldEndDate = this.endDate;
401
-
402
407
  $(document).on('mousedown', $.proxy(this.hide, this));
403
408
  this.element.trigger('shown', {target: e.target, picker: this});
404
409
  },
@@ -409,6 +414,9 @@
409
414
  if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
410
415
  this.notify();
411
416
 
417
+ this.oldStartDate = this.startDate.clone();
418
+ this.oldEndDate = this.endDate.clone();
419
+
412
420
  $(document).off('mousedown', this.hide);
413
421
  this.element.trigger('hidden', { picker: this });
414
422
  },
@@ -424,11 +432,20 @@
424
432
  }
425
433
  },
426
434
 
435
+ showCalendars: function() {
436
+ this.container.find('.calendar').show();
437
+ this.move();
438
+ },
439
+
440
+ updateInputText: function() {
441
+ if (this.element.is('input'))
442
+ this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
443
+ },
444
+
427
445
  clickRange: function (e) {
428
446
  var label = e.target.innerHTML;
429
447
  if (label == this.locale.customRangeLabel) {
430
- this.container.find('.calendar').show();
431
- this.move();
448
+ this.showCalendars();
432
449
  } else {
433
450
  var dates = this.ranges[label];
434
451
 
@@ -444,8 +461,7 @@
444
461
  this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
445
462
  this.updateCalendars();
446
463
 
447
- if (this.element.is('input'))
448
- this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
464
+ this.updateInputText();
449
465
 
450
466
  this.container.find('.calendar').hide();
451
467
  this.hide();
@@ -531,8 +547,7 @@
531
547
  },
532
548
 
533
549
  clickApply: function (e) {
534
- if (this.element.is('input'))
535
- this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
550
+ this.updateInputText();
536
551
  this.hide();
537
552
  },
538
553
 
@@ -544,29 +559,25 @@
544
559
  this.hide();
545
560
  },
546
561
 
547
- updateYear: function (e) {
548
- var year = parseInt($(e.target).val());
562
+ updateMonthYear: function (e) {
563
+
549
564
  var isLeft = $(e.target).closest('.calendar').hasClass('left');
565
+ var cal = this.container.find('.calendar.left');
566
+ if (!isLeft)
567
+ cal = this.container.find('.calendar.right');
568
+
569
+ // Month must be Number for new moment versions
570
+ var month = parseInt(cal.find('.monthselect').val(), 10);
571
+ var year = cal.find('.yearselect').val();
550
572
 
551
573
  if (isLeft) {
552
- this.leftCalendar.month.month(this.startDate.month()).year(year);
574
+ this.leftCalendar.month.month(month).year(year);
553
575
  } else {
554
- this.rightCalendar.month.month(this.endDate.month()).year(year);
576
+ this.rightCalendar.month.month(month).year(year);
555
577
  }
556
578
 
557
579
  this.updateCalendars();
558
- },
559
-
560
- updateMonth: function (e) {
561
- var month = parseInt($(e.target).val());
562
- var isLeft = $(e.target).closest('.calendar').hasClass('left');
563
580
 
564
- if (isLeft) {
565
- this.leftCalendar.month.month(month).year(this.startDate.year());
566
- } else {
567
- this.rightCalendar.month.month(month).year(this.endDate.year());
568
- }
569
- this.updateCalendars();
570
581
  },
571
582
 
572
583
  updateTime: function(e) {
@@ -583,19 +594,21 @@
583
594
  var ampm = cal.find('.ampmselect').val();
584
595
  if (ampm == 'PM' && hour < 12)
585
596
  hour += 12;
597
+ if (ampm == 'AM' && hour == 12)
598
+ hour = 0;
586
599
  }
587
600
 
588
601
  if (isLeft) {
589
- var start = this.startDate;
602
+ var start = this.startDate.clone();
590
603
  start.hour(hour);
591
604
  start.minute(minute);
592
605
  this.startDate = start;
593
606
  this.leftCalendar.month.hour(hour).minute(minute);
594
607
  } else {
595
- var end = this.endDate;
608
+ var end = this.endDate.clone();
596
609
  end.hour(hour);
597
610
  end.minute(minute);
598
- this.endDate = end;
611
+ this.endDate = end;
599
612
  this.rightCalendar.month.hour(hour).minute(minute);
600
613
  }
601
614
 
@@ -655,13 +668,14 @@
655
668
  if (dayOfWeek == this.locale.firstDay)
656
669
  startDay = daysInLastMonth - 6;
657
670
 
658
- var curDate = moment([lastYear, lastMonth, startDay, hour, minute]);
659
- for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add('day', 1)) {
671
+ var curDate = moment([lastYear, lastMonth, startDay, 12, minute]);
672
+ for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add('hour', 24)) {
660
673
  if (i > 0 && col % 7 == 0) {
661
674
  col = 0;
662
675
  row++;
663
676
  }
664
- calendar[row][col] = curDate;
677
+ calendar[row][col] = curDate.clone().hour(hour);
678
+ curDate.hour(12);
665
679
  }
666
680
 
667
681
  return calendar;
@@ -686,7 +700,7 @@
686
700
  var currentYear = selected.year();
687
701
  var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
688
702
  var minYear = (minDate && minDate.year()) || (currentYear - 50);
689
- var yearHtml = '<select class="yearselect">'
703
+ var yearHtml = '<select class="yearselect">';
690
704
 
691
705
  for (var y = minYear; y <= maxYear; y++) {
692
706
  yearHtml += '<option value="' + y + '"' +
@@ -711,7 +725,7 @@
711
725
  html += '<th></th>';
712
726
 
713
727
  if (!minDate || minDate.isBefore(calendar[1][1])) {
714
- html += '<th class="prev available"><i class="icon-arrow-left"></i></th>';
728
+ html += '<th class="prev available"><i class="icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
715
729
  } else {
716
730
  html += '<th></th>';
717
731
  }
@@ -724,7 +738,7 @@
724
738
 
725
739
  html += '<th colspan="5" style="width: auto">' + dateHtml + '</th>';
726
740
  if (!maxDate || maxDate.isAfter(calendar[1][1])) {
727
- html += '<th class="next available"><i class="icon-arrow-right"></i></th>';
741
+ html += '<th class="next available"><i class="icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
728
742
  } else {
729
743
  html += '<th></th>';
730
744
  }
@@ -851,4 +865,4 @@
851
865
  return this;
852
866
  };
853
867
 
854
- }(window.jQuery);
868
+ }(window.jQuery);
@@ -1,5 +1,5 @@
1
1
  // moment.js
2
- // version : 2.0.0
2
+ // version : 2.1.0
3
3
  // author : Tim Wood
4
4
  // license : MIT
5
5
  // momentjs.com
@@ -7,45 +7,43 @@
7
7
  (function (undefined) {
8
8
 
9
9
  /************************************
10
- Constants
11
- ************************************/
10
+ Constants
11
+ ************************************/
12
12
 
13
13
  var moment,
14
- VERSION = "2.0.0",
14
+ VERSION = "2.1.0",
15
15
  round = Math.round, i,
16
- // internal storage for language config files
16
+ // internal storage for language config files
17
17
  languages = {},
18
18
 
19
- // check for nodeJS
19
+ // check for nodeJS
20
20
  hasModule = (typeof module !== 'undefined' && module.exports),
21
21
 
22
- // ASP.NET json date format regex
22
+ // ASP.NET json date format regex
23
23
  aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
24
+ aspNetTimeSpanJsonRegex = /(\-)?(\d*)?\.?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,
24
25
 
25
- // format tokens
26
- 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,
26
+ // format tokens
27
+ 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,
27
28
  localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
28
29
 
29
- // parsing tokens
30
- parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
31
-
32
- // parsing token regexes
30
+ // parsing token regexes
33
31
  parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
34
32
  parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
35
33
  parseTokenThreeDigits = /\d{3}/, // 000 - 999
36
34
  parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
37
35
  parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
38
- 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.
36
+ 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.
39
37
  parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
40
38
  parseTokenT = /T/i, // T (ISO seperator)
41
39
  parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
42
40
 
43
- // preliminary iso regex
44
- // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
41
+ // preliminary iso regex
42
+ // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
45
43
  isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
46
44
  isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
47
45
 
48
- // iso time formats and regexes
46
+ // iso time formats and regexes
49
47
  isoTimes = [
50
48
  ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
51
49
  ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
@@ -53,11 +51,11 @@
53
51
  ['HH', /(T| )\d\d/]
54
52
  ],
55
53
 
56
- // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
54
+ // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
57
55
  parseTimezoneChunker = /([\+\-]|\d\d)/gi,
58
56
 
59
- // getter and setter names
60
- proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
57
+ // getter and setter names
58
+ proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
61
59
  unitMillisecondFactors = {
62
60
  'Milliseconds' : 1,
63
61
  'Seconds' : 1e3,
@@ -68,10 +66,21 @@
68
66
  'Years' : 31536e6
69
67
  },
70
68
 
71
- // format function strings
69
+ unitAliases = {
70
+ ms : 'millisecond',
71
+ s : 'second',
72
+ m : 'minute',
73
+ h : 'hour',
74
+ d : 'day',
75
+ w : 'week',
76
+ M : 'month',
77
+ y : 'year'
78
+ },
79
+
80
+ // format function strings
72
81
  formatFunctions = {},
73
82
 
74
- // tokens to ordinalize and pad
83
+ // tokens to ordinalize and pad
75
84
  ordinalizeTokens = 'DDD w W M D d'.split(' '),
76
85
  paddedTokens = 'M D H h m s w W'.split(' '),
77
86
 
@@ -118,6 +127,30 @@
118
127
  YYYYY : function () {
119
128
  return leftZeroFill(this.year(), 5);
120
129
  },
130
+ gg : function () {
131
+ return leftZeroFill(this.weekYear() % 100, 2);
132
+ },
133
+ gggg : function () {
134
+ return this.weekYear();
135
+ },
136
+ ggggg : function () {
137
+ return leftZeroFill(this.weekYear(), 5);
138
+ },
139
+ GG : function () {
140
+ return leftZeroFill(this.isoWeekYear() % 100, 2);
141
+ },
142
+ GGGG : function () {
143
+ return this.isoWeekYear();
144
+ },
145
+ GGGGG : function () {
146
+ return leftZeroFill(this.isoWeekYear(), 5);
147
+ },
148
+ e : function () {
149
+ return this.weekday();
150
+ },
151
+ E : function () {
152
+ return this.isoWeekday();
153
+ },
121
154
  a : function () {
122
155
  return this.lang().meridiem(this.hours(), this.minutes(), true);
123
156
  },
@@ -163,6 +196,12 @@
163
196
  }
164
197
  return b + leftZeroFill(~~(10 * a / 6), 4);
165
198
  },
199
+ z : function () {
200
+ return this.zoneAbbr();
201
+ },
202
+ zz : function () {
203
+ return this.zoneName();
204
+ },
166
205
  X : function () {
167
206
  return this.unix();
168
207
  }
@@ -173,15 +212,15 @@
173
212
  return leftZeroFill(func.call(this, a), count);
174
213
  };
175
214
  }
176
- function ordinalizeToken(func) {
215
+ function ordinalizeToken(func, period) {
177
216
  return function (a) {
178
- return this.lang().ordinal(func.call(this, a));
217
+ return this.lang().ordinal(func.call(this, a), period);
179
218
  };
180
219
  }
181
220
 
182
221
  while (ordinalizeTokens.length) {
183
222
  i = ordinalizeTokens.pop();
184
- formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]);
223
+ formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
185
224
  }
186
225
  while (paddedTokens.length) {
187
226
  i = paddedTokens.pop();
@@ -191,8 +230,8 @@
191
230
 
192
231
 
193
232
  /************************************
194
- Constructors
195
- ************************************/
233
+ Constructors
234
+ ************************************/
196
235
 
197
236
  function Language() {
198
237
 
@@ -205,8 +244,7 @@
205
244
 
206
245
  // Duration Constructor
207
246
  function Duration(duration) {
208
- var data = this._data = {},
209
- years = duration.years || duration.year || duration.y || 0,
247
+ var years = duration.years || duration.year || duration.y || 0,
210
248
  months = duration.months || duration.month || duration.M || 0,
211
249
  weeks = duration.weeks || duration.week || duration.w || 0,
212
250
  days = duration.days || duration.day || duration.d || 0,
@@ -215,6 +253,9 @@
215
253
  seconds = duration.seconds || duration.second || duration.s || 0,
216
254
  milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0;
217
255
 
256
+ // store reference to input for deterministic cloning
257
+ this._input = duration;
258
+
218
259
  // representation for dateAddRemove
219
260
  this._milliseconds = milliseconds +
220
261
  seconds * 1e3 + // 1000
@@ -230,35 +271,15 @@
230
271
  this._months = months +
231
272
  years * 12;
232
273
 
233
- // The following code bubbles up values, see the tests for
234
- // examples of what that means.
235
- data.milliseconds = milliseconds % 1000;
236
- seconds += absRound(milliseconds / 1000);
237
-
238
- data.seconds = seconds % 60;
239
- minutes += absRound(seconds / 60);
240
-
241
- data.minutes = minutes % 60;
242
- hours += absRound(minutes / 60);
243
-
244
- data.hours = hours % 24;
245
- days += absRound(hours / 24);
246
-
247
- days += weeks * 7;
248
- data.days = days % 30;
274
+ this._data = {};
249
275
 
250
- months += absRound(days / 30);
251
-
252
- data.months = months % 12;
253
- years += absRound(months / 12);
254
-
255
- data.years = years;
276
+ this._bubble();
256
277
  }
257
278
 
258
279
 
259
280
  /************************************
260
- Helpers
261
- ************************************/
281
+ Helpers
282
+ ************************************/
262
283
 
263
284
 
264
285
  function extend(a, b) {
@@ -289,23 +310,35 @@
289
310
  }
290
311
 
291
312
  // helper function for _.addTime and _.subtractTime
292
- function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
293
- var ms = duration._milliseconds,
294
- d = duration._days,
295
- M = duration._months,
313
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) {
314
+ var milliseconds = duration._milliseconds,
315
+ days = duration._days,
316
+ months = duration._months,
317
+ minutes,
318
+ hours,
296
319
  currentDate;
297
320
 
298
- if (ms) {
299
- mom._d.setTime(+mom + ms * isAdding);
321
+ if (milliseconds) {
322
+ mom._d.setTime(+mom._d + milliseconds * isAdding);
323
+ }
324
+ // store the minutes and hours so we can restore them
325
+ if (days || months) {
326
+ minutes = mom.minute();
327
+ hours = mom.hour();
328
+ }
329
+ if (days) {
330
+ mom.date(mom.date() + days * isAdding);
331
+ }
332
+ if (months) {
333
+ mom.month(mom.month() + months * isAdding);
300
334
  }
301
- if (d) {
302
- mom.date(mom.date() + d * isAdding);
335
+ if (milliseconds && !ignoreUpdateOffset) {
336
+ moment.updateOffset(mom);
303
337
  }
304
- if (M) {
305
- currentDate = mom.date();
306
- mom.date(1)
307
- .month(mom.month() + M * isAdding)
308
- .date(Math.min(currentDate, mom.daysInMonth()));
338
+ // restore the minutes and hours after possibly changing dst
339
+ if (days || months) {
340
+ mom.minute(minutes);
341
+ mom.hour(hours);
309
342
  }
310
343
  }
311
344
 
@@ -328,10 +361,14 @@
328
361
  return diffs + lengthDiff;
329
362
  }
330
363
 
364
+ function normalizeUnits(units) {
365
+ return units ? unitAliases[units] || units.toLowerCase().replace(/(.)s$/, '$1') : units;
366
+ }
367
+
331
368
 
332
369
  /************************************
333
- Languages
334
- ************************************/
370
+ Languages
371
+ ************************************/
335
372
 
336
373
 
337
374
  Language.prototype = {
@@ -358,7 +395,7 @@
358
395
  },
359
396
 
360
397
  monthsParse : function (monthName) {
361
- var i, mom, regex, output;
398
+ var i, mom, regex;
362
399
 
363
400
  if (!this._monthsParse) {
364
401
  this._monthsParse = [];
@@ -393,6 +430,27 @@
393
430
  return this._weekdaysMin[m.day()];
394
431
  },
395
432
 
433
+ weekdaysParse : function (weekdayName) {
434
+ var i, mom, regex;
435
+
436
+ if (!this._weekdaysParse) {
437
+ this._weekdaysParse = [];
438
+ }
439
+
440
+ for (i = 0; i < 7; i++) {
441
+ // make the regex if we don't have it already
442
+ if (!this._weekdaysParse[i]) {
443
+ mom = moment([2000, 1]).day(i);
444
+ regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
445
+ this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
446
+ }
447
+ // test the regex
448
+ if (this._weekdaysParse[i].test(weekdayName)) {
449
+ return i;
450
+ }
451
+ }
452
+ },
453
+
396
454
  _longDateFormat : {
397
455
  LT : "h:mm A",
398
456
  L : "MM/DD/YYYY",
@@ -411,6 +469,11 @@
411
469
  return output;
412
470
  },
413
471
 
472
+ isPM : function (input) {
473
+ return ((input + '').toLowerCase()[0] === 'p');
474
+ },
475
+
476
+ _meridiemParse : /[ap]\.?m?\.?/i,
414
477
  meridiem : function (hours, minutes, isLower) {
415
478
  if (hours > 11) {
416
479
  return isLower ? 'pm' : 'PM';
@@ -424,7 +487,7 @@
424
487
  nextDay : '[Tomorrow at] LT',
425
488
  nextWeek : 'dddd [at] LT',
426
489
  lastDay : '[Yesterday at] LT',
427
- lastWeek : '[last] dddd [at] LT',
490
+ lastWeek : '[Last] dddd [at] LT',
428
491
  sameElse : 'L'
429
492
  },
430
493
  calendar : function (key, mom) {
@@ -472,7 +535,7 @@
472
535
  },
473
536
 
474
537
  week : function (mom) {
475
- return weekOfYear(mom, this._week.dow, this._week.doy);
538
+ return weekOfYear(mom, this._week.dow, this._week.doy).week;
476
539
  },
477
540
  _week : {
478
541
  dow : 0, // Sunday is the first day of the week.
@@ -504,15 +567,20 @@
504
567
  return moment.fn._lang;
505
568
  }
506
569
  if (!languages[key] && hasModule) {
507
- require('./lang/' + key);
570
+ try {
571
+ require('./lang/' + key);
572
+ } catch (e) {
573
+ // call with no params to set to default
574
+ return moment.fn._lang;
575
+ }
508
576
  }
509
577
  return languages[key];
510
578
  }
511
579
 
512
580
 
513
581
  /************************************
514
- Formatting
515
- ************************************/
582
+ Formatting
583
+ ************************************/
516
584
 
517
585
 
518
586
  function removeFormattingTokens(input) {
@@ -536,7 +604,7 @@
536
604
  return function (mom) {
537
605
  var output = "";
538
606
  for (i = 0; i < length; i++) {
539
- output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i];
607
+ output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
540
608
  }
541
609
  return output;
542
610
  };
@@ -563,146 +631,143 @@
563
631
 
564
632
 
565
633
  /************************************
566
- Parsing
567
- ************************************/
634
+ Parsing
635
+ ************************************/
568
636
 
569
637
 
570
- // get the regex to find the next token
571
- function getParseRegexForToken(token) {
638
+ // get the regex to find the next token
639
+ function getParseRegexForToken(token, config) {
572
640
  switch (token) {
573
- case 'DDDD':
574
- return parseTokenThreeDigits;
575
- case 'YYYY':
576
- return parseTokenFourDigits;
577
- case 'YYYYY':
578
- return parseTokenSixDigits;
579
- case 'S':
580
- case 'SS':
581
- case 'SSS':
582
- case 'DDD':
583
- return parseTokenOneToThreeDigits;
584
- case 'MMM':
585
- case 'MMMM':
586
- case 'dd':
587
- case 'ddd':
588
- case 'dddd':
589
- case 'a':
590
- case 'A':
591
- return parseTokenWord;
592
- case 'X':
593
- return parseTokenTimestampMs;
594
- case 'Z':
595
- case 'ZZ':
596
- return parseTokenTimezone;
597
- case 'T':
598
- return parseTokenT;
599
- case 'MM':
600
- case 'DD':
601
- case 'YY':
602
- case 'HH':
603
- case 'hh':
604
- case 'mm':
605
- case 'ss':
606
- case 'M':
607
- case 'D':
608
- case 'd':
609
- case 'H':
610
- case 'h':
611
- case 'm':
612
- case 's':
613
- return parseTokenOneOrTwoDigits;
614
- default :
615
- return new RegExp(token.replace('\\', ''));
641
+ case 'DDDD':
642
+ return parseTokenThreeDigits;
643
+ case 'YYYY':
644
+ return parseTokenFourDigits;
645
+ case 'YYYYY':
646
+ return parseTokenSixDigits;
647
+ case 'S':
648
+ case 'SS':
649
+ case 'SSS':
650
+ case 'DDD':
651
+ return parseTokenOneToThreeDigits;
652
+ case 'MMM':
653
+ case 'MMMM':
654
+ case 'dd':
655
+ case 'ddd':
656
+ case 'dddd':
657
+ return parseTokenWord;
658
+ case 'a':
659
+ case 'A':
660
+ return getLangDefinition(config._l)._meridiemParse;
661
+ case 'X':
662
+ return parseTokenTimestampMs;
663
+ case 'Z':
664
+ case 'ZZ':
665
+ return parseTokenTimezone;
666
+ case 'T':
667
+ return parseTokenT;
668
+ case 'MM':
669
+ case 'DD':
670
+ case 'YY':
671
+ case 'HH':
672
+ case 'hh':
673
+ case 'mm':
674
+ case 'ss':
675
+ case 'M':
676
+ case 'D':
677
+ case 'd':
678
+ case 'H':
679
+ case 'h':
680
+ case 'm':
681
+ case 's':
682
+ return parseTokenOneOrTwoDigits;
683
+ default :
684
+ return new RegExp(token.replace('\\', ''));
616
685
  }
617
686
  }
618
687
 
688
+ function timezoneMinutesFromString(string) {
689
+ var tzchunk = (parseTokenTimezone.exec(string) || [])[0],
690
+ parts = (tzchunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
691
+ minutes = +(parts[1] * 60) + ~~parts[2];
692
+
693
+ return parts[0] === '+' ? -minutes : minutes;
694
+ }
695
+
619
696
  // function to convert string input to date
620
697
  function addTimeToArrayFromToken(token, input, config) {
621
- var a, b,
622
- datePartArray = config._a;
698
+ var a, datePartArray = config._a;
623
699
 
624
700
  switch (token) {
625
- // MONTH
626
- case 'M' : // fall through to MM
627
- case 'MM' :
628
- datePartArray[1] = (input == null) ? 0 : ~~input - 1;
629
- break;
630
- case 'MMM' : // fall through to MMMM
631
- case 'MMMM' :
632
- a = getLangDefinition(config._l).monthsParse(input);
633
- // if we didn't find a month name, mark the date as invalid.
634
- if (a != null) {
635
- datePartArray[1] = a;
636
- } else {
637
- config._isValid = false;
638
- }
639
- break;
640
- // DAY OF MONTH
641
- case 'D' : // fall through to DDDD
642
- case 'DD' : // fall through to DDDD
643
- case 'DDD' : // fall through to DDDD
644
- case 'DDDD' :
645
- if (input != null) {
646
- datePartArray[2] = ~~input;
647
- }
648
- break;
649
- // YEAR
650
- case 'YY' :
651
- datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
652
- break;
653
- case 'YYYY' :
654
- case 'YYYYY' :
655
- datePartArray[0] = ~~input;
656
- break;
657
- // AM / PM
658
- case 'a' : // fall through to A
659
- case 'A' :
660
- config._isPm = ((input + '').toLowerCase() === 'pm');
661
- break;
662
- // 24 HOUR
663
- case 'H' : // fall through to hh
664
- case 'HH' : // fall through to hh
665
- case 'h' : // fall through to hh
666
- case 'hh' :
667
- datePartArray[3] = ~~input;
668
- break;
669
- // MINUTE
670
- case 'm' : // fall through to mm
671
- case 'mm' :
672
- datePartArray[4] = ~~input;
673
- break;
674
- // SECOND
675
- case 's' : // fall through to ss
676
- case 'ss' :
677
- datePartArray[5] = ~~input;
678
- break;
679
- // MILLISECOND
680
- case 'S' :
681
- case 'SS' :
682
- case 'SSS' :
683
- datePartArray[6] = ~~ (('0.' + input) * 1000);
684
- break;
685
- // UNIX TIMESTAMP WITH MS
686
- case 'X':
687
- config._d = new Date(parseFloat(input) * 1000);
688
- break;
689
- // TIMEZONE
690
- case 'Z' : // fall through to ZZ
691
- case 'ZZ' :
692
- config._useUTC = true;
693
- a = (input + '').match(parseTimezoneChunker);
694
- if (a && a[1]) {
695
- config._tzh = ~~a[1];
696
- }
697
- if (a && a[2]) {
698
- config._tzm = ~~a[2];
699
- }
700
- // reverse offsets
701
- if (a && a[0] === '+') {
702
- config._tzh = -config._tzh;
703
- config._tzm = -config._tzm;
704
- }
705
- break;
701
+ // MONTH
702
+ case 'M' : // fall through to MM
703
+ case 'MM' :
704
+ datePartArray[1] = (input == null) ? 0 : ~~input - 1;
705
+ break;
706
+ case 'MMM' : // fall through to MMMM
707
+ case 'MMMM' :
708
+ a = getLangDefinition(config._l).monthsParse(input);
709
+ // if we didn't find a month name, mark the date as invalid.
710
+ if (a != null) {
711
+ datePartArray[1] = a;
712
+ } else {
713
+ config._isValid = false;
714
+ }
715
+ break;
716
+ // DAY OF MONTH
717
+ case 'D' : // fall through to DDDD
718
+ case 'DD' : // fall through to DDDD
719
+ case 'DDD' : // fall through to DDDD
720
+ case 'DDDD' :
721
+ if (input != null) {
722
+ datePartArray[2] = ~~input;
723
+ }
724
+ break;
725
+ // YEAR
726
+ case 'YY' :
727
+ datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
728
+ break;
729
+ case 'YYYY' :
730
+ case 'YYYYY' :
731
+ datePartArray[0] = ~~input;
732
+ break;
733
+ // AM / PM
734
+ case 'a' : // fall through to A
735
+ case 'A' :
736
+ config._isPm = getLangDefinition(config._l).isPM(input);
737
+ break;
738
+ // 24 HOUR
739
+ case 'H' : // fall through to hh
740
+ case 'HH' : // fall through to hh
741
+ case 'h' : // fall through to hh
742
+ case 'hh' :
743
+ datePartArray[3] = ~~input;
744
+ break;
745
+ // MINUTE
746
+ case 'm' : // fall through to mm
747
+ case 'mm' :
748
+ datePartArray[4] = ~~input;
749
+ break;
750
+ // SECOND
751
+ case 's' : // fall through to ss
752
+ case 'ss' :
753
+ datePartArray[5] = ~~input;
754
+ break;
755
+ // MILLISECOND
756
+ case 'S' :
757
+ case 'SS' :
758
+ case 'SSS' :
759
+ datePartArray[6] = ~~ (('0.' + input) * 1000);
760
+ break;
761
+ // UNIX TIMESTAMP WITH MS
762
+ case 'X':
763
+ config._d = new Date(parseFloat(input) * 1000);
764
+ break;
765
+ // TIMEZONE
766
+ case 'Z' : // fall through to ZZ
767
+ case 'ZZ' :
768
+ config._useUTC = true;
769
+ config._tzm = timezoneMinutesFromString(input);
770
+ break;
706
771
  }
707
772
 
708
773
  // if the input is null, the date is not valid
@@ -727,8 +792,8 @@
727
792
  }
728
793
 
729
794
  // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
730
- input[3] += config._tzh || 0;
731
- input[4] += config._tzm || 0;
795
+ input[3] += ~~((config._tzm || 0) / 60);
796
+ input[4] += ~~((config._tzm || 0) % 60);
732
797
 
733
798
  date = new Date(0);
734
799
 
@@ -753,7 +818,7 @@
753
818
  config._a = [];
754
819
 
755
820
  for (i = 0; i < tokens.length; i++) {
756
- parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
821
+ parsedInput = (getParseRegexForToken(tokens[i], config).exec(string) || [])[0];
757
822
  if (parsedInput) {
758
823
  string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
759
824
  }
@@ -762,6 +827,12 @@
762
827
  addTimeToArrayFromToken(tokens[i], parsedInput, config);
763
828
  }
764
829
  }
830
+
831
+ // add remaining unparsed input to the string
832
+ if (string) {
833
+ config._il = string;
834
+ }
835
+
765
836
  // handle am pm
766
837
  if (config._isPm && config._a[3] < 12) {
767
838
  config._a[3] += 12;
@@ -782,22 +853,22 @@
782
853
 
783
854
  scoreToBeat = 99,
784
855
  i,
785
- currentDate,
786
856
  currentScore;
787
857
 
788
- while (config._f.length) {
858
+ for (i = 0; i < config._f.length; i++) {
789
859
  tempConfig = extend({}, config);
790
- tempConfig._f = config._f.pop();
860
+ tempConfig._f = config._f[i];
791
861
  makeDateFromStringAndFormat(tempConfig);
792
862
  tempMoment = new Moment(tempConfig);
793
863
 
794
- if (tempMoment.isValid()) {
795
- bestMoment = tempMoment;
796
- break;
797
- }
798
-
799
864
  currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
800
865
 
866
+ // if there is any input that was not parsed
867
+ // add a penalty for that format
868
+ if (tempMoment._il) {
869
+ currentScore += tempMoment._il.length;
870
+ }
871
+
801
872
  if (currentScore < scoreToBeat) {
802
873
  scoreToBeat = currentScore;
803
874
  bestMoment = tempMoment;
@@ -810,9 +881,12 @@
810
881
  // date from iso format
811
882
  function makeDateFromString(config) {
812
883
  var i,
813
- string = config._i;
814
- if (isoRegex.exec(string)) {
815
- config._f = 'YYYY-MM-DDT';
884
+ string = config._i,
885
+ match = isoRegex.exec(string);
886
+
887
+ if (match) {
888
+ // match[2] should be "T" or undefined
889
+ config._f = 'YYYY-MM-DD' + (match[2] || " ");
816
890
  for (i = 0; i < 4; i++) {
817
891
  if (isoTimes[i][1].exec(string)) {
818
892
  config._f += isoTimes[i][0];
@@ -848,11 +922,11 @@
848
922
 
849
923
 
850
924
  /************************************
851
- Relative Time
852
- ************************************/
925
+ Relative Time
926
+ ************************************/
853
927
 
854
928
 
855
- // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
929
+ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
856
930
  function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
857
931
  return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
858
932
  }
@@ -881,20 +955,21 @@
881
955
 
882
956
 
883
957
  /************************************
884
- Week of Year
885
- ************************************/
958
+ Week of Year
959
+ ************************************/
886
960
 
887
961
 
888
- // firstDayOfWeek 0 = sun, 6 = sat
889
- // the day of the week that starts the week
890
- // (usually sunday or monday)
891
- // firstDayOfWeekOfYear 0 = sun, 6 = sat
892
- // the first week is the week that contains the first
893
- // of this day of the week
894
- // (eg. ISO weeks use thursday (4))
962
+ // firstDayOfWeek 0 = sun, 6 = sat
963
+ // the day of the week that starts the week
964
+ // (usually sunday or monday)
965
+ // firstDayOfWeekOfYear 0 = sun, 6 = sat
966
+ // the first week is the week that contains the first
967
+ // of this day of the week
968
+ // (eg. ISO weeks use thursday (4))
895
969
  function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
896
970
  var end = firstDayOfWeekOfYear - firstDayOfWeek,
897
- daysToDayOfWeek = firstDayOfWeekOfYear - mom.day();
971
+ daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
972
+ adjustedMoment;
898
973
 
899
974
 
900
975
  if (daysToDayOfWeek > end) {
@@ -905,13 +980,17 @@
905
980
  daysToDayOfWeek += 7;
906
981
  }
907
982
 
908
- return Math.ceil(moment(mom).add('d', daysToDayOfWeek).dayOfYear() / 7);
983
+ adjustedMoment = moment(mom).add('d', daysToDayOfWeek);
984
+ return {
985
+ week: Math.ceil(adjustedMoment.dayOfYear() / 7),
986
+ year: adjustedMoment.year()
987
+ };
909
988
  }
910
989
 
911
990
 
912
991
  /************************************
913
- Top Level Functions
914
- ************************************/
992
+ Top Level Functions
993
+ ************************************/
915
994
 
916
995
  function makeMoment(config) {
917
996
  var input = config._i,
@@ -970,7 +1049,9 @@
970
1049
  moment.duration = function (input, key) {
971
1050
  var isDuration = moment.isDuration(input),
972
1051
  isNumber = (typeof input === 'number'),
973
- duration = (isDuration ? input._data : (isNumber ? {} : input)),
1052
+ duration = (isDuration ? input._input : (isNumber ? {} : input)),
1053
+ matched = aspNetTimeSpanJsonRegex.exec(input),
1054
+ sign,
974
1055
  ret;
975
1056
 
976
1057
  if (isNumber) {
@@ -979,6 +1060,16 @@
979
1060
  } else {
980
1061
  duration.milliseconds = input;
981
1062
  }
1063
+ } else if (matched) {
1064
+ sign = (matched[1] === "-") ? -1 : 1;
1065
+ duration = {
1066
+ y: 0,
1067
+ d: ~~matched[2] * sign,
1068
+ h: ~~matched[3] * sign,
1069
+ m: ~~matched[4] * sign,
1070
+ s: ~~matched[5] * sign,
1071
+ ms: ~~matched[6] * sign
1072
+ };
982
1073
  }
983
1074
 
984
1075
  ret = new Duration(duration);
@@ -996,12 +1087,14 @@
996
1087
  // default format
997
1088
  moment.defaultFormat = isoFormat;
998
1089
 
1090
+ // This function will be called whenever a moment is mutated.
1091
+ // It is intended to keep the offset in sync with the timezone.
1092
+ moment.updateOffset = function () {};
1093
+
999
1094
  // This function will load languages and then set the global language. If
1000
1095
  // no arguments are passed in, it will simply return the current global
1001
1096
  // language key.
1002
1097
  moment.lang = function (key, values) {
1003
- var i;
1004
-
1005
1098
  if (!key) {
1006
1099
  return moment.fn._lang._abbr;
1007
1100
  }
@@ -1033,8 +1126,8 @@
1033
1126
 
1034
1127
 
1035
1128
  /************************************
1036
- Moment Prototype
1037
- ************************************/
1129
+ Moment Prototype
1130
+ ************************************/
1038
1131
 
1039
1132
 
1040
1133
  moment.fn = Moment.prototype = {
@@ -1044,11 +1137,11 @@
1044
1137
  },
1045
1138
 
1046
1139
  valueOf : function () {
1047
- return +this._d;
1140
+ return +this._d + ((this._offset || 0) * 60000);
1048
1141
  },
1049
1142
 
1050
1143
  unix : function () {
1051
- return Math.floor(+this._d / 1000);
1144
+ return Math.floor(+this / 1000);
1052
1145
  },
1053
1146
 
1054
1147
  toString : function () {
@@ -1056,11 +1149,11 @@
1056
1149
  },
1057
1150
 
1058
1151
  toDate : function () {
1059
- return this._d;
1152
+ return this._offset ? new Date(+this) : this._d;
1060
1153
  },
1061
1154
 
1062
- toJSON : function () {
1063
- return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
1155
+ toISOString : function () {
1156
+ return formatMoment(moment(this).utc(), 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
1064
1157
  },
1065
1158
 
1066
1159
  toArray : function () {
@@ -1088,11 +1181,11 @@
1088
1181
  },
1089
1182
 
1090
1183
  utc : function () {
1091
- this._isUTC = true;
1092
- return this;
1184
+ return this.zone(0);
1093
1185
  },
1094
1186
 
1095
1187
  local : function () {
1188
+ this.zone(0);
1096
1189
  this._isUTC = false;
1097
1190
  return this;
1098
1191
  },
@@ -1127,30 +1220,35 @@
1127
1220
  },
1128
1221
 
1129
1222
  diff : function (input, units, asFloat) {
1130
- var that = this._isUTC ? moment(input).utc() : moment(input).local(),
1223
+ var that = this._isUTC ? moment(input).zone(this._offset || 0) : moment(input).local(),
1131
1224
  zoneDiff = (this.zone() - that.zone()) * 6e4,
1132
1225
  diff, output;
1133
1226
 
1134
- if (units) {
1135
- // standardize on singular form
1136
- units = units.replace(/s$/, '');
1137
- }
1227
+ units = normalizeUnits(units);
1138
1228
 
1139
1229
  if (units === 'year' || units === 'month') {
1230
+ // average number of days in the months in the given dates
1140
1231
  diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
1232
+ // difference in months
1141
1233
  output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
1142
- output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff;
1234
+ // adjust by taking difference in days, average number of days
1235
+ // and dst in the given months.
1236
+ output += ((this - moment(this).startOf('month')) -
1237
+ (that - moment(that).startOf('month'))) / diff;
1238
+ // same as above but with zones, to negate all dst
1239
+ output -= ((this.zone() - moment(this).startOf('month').zone()) -
1240
+ (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff;
1143
1241
  if (units === 'year') {
1144
1242
  output = output / 12;
1145
1243
  }
1146
1244
  } else {
1147
- diff = (this - that) - zoneDiff;
1245
+ diff = (this - that);
1148
1246
  output = units === 'second' ? diff / 1e3 : // 1000
1149
1247
  units === 'minute' ? diff / 6e4 : // 1000 * 60
1150
- units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
1151
- units === 'day' ? diff / 864e5 : // 1000 * 60 * 60 * 24
1152
- units === 'week' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
1153
- diff;
1248
+ units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
1249
+ units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
1250
+ units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
1251
+ diff;
1154
1252
  }
1155
1253
  return asFloat ? output : absRound(output);
1156
1254
  },
@@ -1166,11 +1264,11 @@
1166
1264
  calendar : function () {
1167
1265
  var diff = this.diff(moment().startOf('day'), 'days', true),
1168
1266
  format = diff < -6 ? 'sameElse' :
1169
- diff < -1 ? 'lastWeek' :
1170
- diff < 0 ? 'lastDay' :
1171
- diff < 1 ? 'sameDay' :
1172
- diff < 2 ? 'nextDay' :
1173
- diff < 7 ? 'nextWeek' : 'sameElse';
1267
+ diff < -1 ? 'lastWeek' :
1268
+ diff < 0 ? 'lastDay' :
1269
+ diff < 1 ? 'sameDay' :
1270
+ diff < 2 ? 'nextDay' :
1271
+ diff < 7 ? 'nextWeek' : 'sameElse';
1174
1272
  return this.format(this.lang().calendar(format, this));
1175
1273
  },
1176
1274
 
@@ -1180,52 +1278,86 @@
1180
1278
  },
1181
1279
 
1182
1280
  isDST : function () {
1183
- return (this.zone() < moment([this.year()]).zone() ||
1184
- this.zone() < moment([this.year(), 5]).zone());
1281
+ return (this.zone() < this.clone().month(0).zone() ||
1282
+ this.zone() < this.clone().month(5).zone());
1185
1283
  },
1186
1284
 
1187
1285
  day : function (input) {
1188
1286
  var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
1189
- return input == null ? day :
1190
- this.add({ d : input - day });
1287
+ if (input != null) {
1288
+ if (typeof input === 'string') {
1289
+ input = this.lang().weekdaysParse(input);
1290
+ if (typeof input !== 'number') {
1291
+ return this;
1292
+ }
1293
+ }
1294
+ return this.add({ d : input - day });
1295
+ } else {
1296
+ return day;
1297
+ }
1298
+ },
1299
+
1300
+ month : function (input) {
1301
+ var utc = this._isUTC ? 'UTC' : '',
1302
+ dayOfMonth,
1303
+ daysInMonth;
1304
+
1305
+ if (input != null) {
1306
+ if (typeof input === 'string') {
1307
+ input = this.lang().monthsParse(input);
1308
+ if (typeof input !== 'number') {
1309
+ return this;
1310
+ }
1311
+ }
1312
+
1313
+ dayOfMonth = this.date();
1314
+ this.date(1);
1315
+ this._d['set' + utc + 'Month'](input);
1316
+ this.date(Math.min(dayOfMonth, this.daysInMonth()));
1317
+
1318
+ moment.updateOffset(this);
1319
+ return this;
1320
+ } else {
1321
+ return this._d['get' + utc + 'Month']();
1322
+ }
1191
1323
  },
1192
1324
 
1193
1325
  startOf: function (units) {
1194
- units = units.replace(/s$/, '');
1326
+ units = normalizeUnits(units);
1195
1327
  // the following switch intentionally omits break keywords
1196
1328
  // to utilize falling through the cases.
1197
1329
  switch (units) {
1198
- case 'year':
1199
- this.month(0);
1330
+ case 'year':
1331
+ this.month(0);
1200
1332
  /* falls through */
1201
- case 'month':
1202
- this.date(1);
1333
+ case 'month':
1334
+ this.date(1);
1203
1335
  /* falls through */
1204
- case 'week':
1205
- case 'day':
1206
- this.hours(0);
1336
+ case 'week':
1337
+ case 'day':
1338
+ this.hours(0);
1207
1339
  /* falls through */
1208
- case 'hour':
1209
- this.minutes(0);
1340
+ case 'hour':
1341
+ this.minutes(0);
1210
1342
  /* falls through */
1211
- case 'minute':
1212
- this.seconds(0);
1343
+ case 'minute':
1344
+ this.seconds(0);
1213
1345
  /* falls through */
1214
- case 'second':
1215
- this.milliseconds(0);
1346
+ case 'second':
1347
+ this.milliseconds(0);
1216
1348
  /* falls through */
1217
1349
  }
1218
1350
 
1219
1351
  // weeks are a special case
1220
1352
  if (units === 'week') {
1221
- this.day(0);
1353
+ this.weekday(0);
1222
1354
  }
1223
1355
 
1224
1356
  return this;
1225
1357
  },
1226
1358
 
1227
1359
  endOf: function (units) {
1228
- return this.startOf(units).add(units.replace(/s?$/, 's'), 1).subtract('ms', 1);
1360
+ return this.startOf(units).add(units, 1).subtract('ms', 1);
1229
1361
  },
1230
1362
 
1231
1363
  isAfter: function (input, units) {
@@ -1243,8 +1375,42 @@
1243
1375
  return +this.clone().startOf(units) === +moment(input).startOf(units);
1244
1376
  },
1245
1377
 
1246
- zone : function () {
1247
- return this._isUTC ? 0 : this._d.getTimezoneOffset();
1378
+ min: function (other) {
1379
+ other = moment.apply(null, arguments);
1380
+ return other < this ? this : other;
1381
+ },
1382
+
1383
+ max: function (other) {
1384
+ other = moment.apply(null, arguments);
1385
+ return other > this ? this : other;
1386
+ },
1387
+
1388
+ zone : function (input) {
1389
+ var offset = this._offset || 0;
1390
+ if (input != null) {
1391
+ if (typeof input === "string") {
1392
+ input = timezoneMinutesFromString(input);
1393
+ }
1394
+ if (Math.abs(input) < 16) {
1395
+ input = input * 60;
1396
+ }
1397
+ this._offset = input;
1398
+ this._isUTC = true;
1399
+ if (offset !== input) {
1400
+ addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true);
1401
+ }
1402
+ } else {
1403
+ return this._isUTC ? offset : this._d.getTimezoneOffset();
1404
+ }
1405
+ return this;
1406
+ },
1407
+
1408
+ zoneAbbr : function () {
1409
+ return this._isUTC ? "UTC" : "";
1410
+ },
1411
+
1412
+ zoneName : function () {
1413
+ return this._isUTC ? "Coordinated Universal Time" : "";
1248
1414
  },
1249
1415
 
1250
1416
  daysInMonth : function () {
@@ -1256,9 +1422,14 @@
1256
1422
  return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
1257
1423
  },
1258
1424
 
1259
- isoWeek : function (input) {
1260
- var week = weekOfYear(this, 1, 4);
1261
- return input == null ? week : this.add("d", (input - week) * 7);
1425
+ weekYear : function (input) {
1426
+ var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year;
1427
+ return input == null ? year : this.add("y", (input - year));
1428
+ },
1429
+
1430
+ isoWeekYear : function (input) {
1431
+ var year = weekOfYear(this, 1, 4).year;
1432
+ return input == null ? year : this.add("y", (input - year));
1262
1433
  },
1263
1434
 
1264
1435
  week : function (input) {
@@ -1266,6 +1437,23 @@
1266
1437
  return input == null ? week : this.add("d", (input - week) * 7);
1267
1438
  },
1268
1439
 
1440
+ isoWeek : function (input) {
1441
+ var week = weekOfYear(this, 1, 4).week;
1442
+ return input == null ? week : this.add("d", (input - week) * 7);
1443
+ },
1444
+
1445
+ weekday : function (input) {
1446
+ var weekday = (this._d.getDay() + 7 - this.lang()._week.dow) % 7;
1447
+ return input == null ? weekday : this.add("d", input - weekday);
1448
+ },
1449
+
1450
+ isoWeekday : function (input) {
1451
+ // behaves the same as moment#day except
1452
+ // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
1453
+ // as a setter, sunday should belong to the previous week.
1454
+ return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
1455
+ },
1456
+
1269
1457
  // If passed a language key, it will set the language for this
1270
1458
  // instance. Otherwise, it will return the language configuration
1271
1459
  // variables for this instance.
@@ -1285,6 +1473,7 @@
1285
1473
  var utc = this._isUTC ? 'UTC' : '';
1286
1474
  if (input != null) {
1287
1475
  this._d['set' + utc + key](input);
1476
+ moment.updateOffset(this);
1288
1477
  return this;
1289
1478
  } else {
1290
1479
  return this._d['get' + utc + key]();
@@ -1302,23 +1491,58 @@
1302
1491
 
1303
1492
  // add plural methods
1304
1493
  moment.fn.days = moment.fn.day;
1494
+ moment.fn.months = moment.fn.month;
1305
1495
  moment.fn.weeks = moment.fn.week;
1306
1496
  moment.fn.isoWeeks = moment.fn.isoWeek;
1307
1497
 
1498
+ // add aliased format methods
1499
+ moment.fn.toJSON = moment.fn.toISOString;
1500
+
1308
1501
  /************************************
1309
- Duration Prototype
1310
- ************************************/
1502
+ Duration Prototype
1503
+ ************************************/
1311
1504
 
1312
1505
 
1313
1506
  moment.duration.fn = Duration.prototype = {
1507
+ _bubble : function () {
1508
+ var milliseconds = this._milliseconds,
1509
+ days = this._days,
1510
+ months = this._months,
1511
+ data = this._data,
1512
+ seconds, minutes, hours, years;
1513
+
1514
+ // The following code bubbles up values, see the tests for
1515
+ // examples of what that means.
1516
+ data.milliseconds = milliseconds % 1000;
1517
+
1518
+ seconds = absRound(milliseconds / 1000);
1519
+ data.seconds = seconds % 60;
1520
+
1521
+ minutes = absRound(seconds / 60);
1522
+ data.minutes = minutes % 60;
1523
+
1524
+ hours = absRound(minutes / 60);
1525
+ data.hours = hours % 24;
1526
+
1527
+ days += absRound(hours / 24);
1528
+ data.days = days % 30;
1529
+
1530
+ months += absRound(days / 30);
1531
+ data.months = months % 12;
1532
+
1533
+ years = absRound(months / 12);
1534
+ data.years = years;
1535
+ },
1536
+
1314
1537
  weeks : function () {
1315
1538
  return absRound(this.days() / 7);
1316
1539
  },
1317
1540
 
1318
1541
  valueOf : function () {
1319
1542
  return this._milliseconds +
1320
- this._days * 864e5 +
1321
- this._months * 2592e6;
1543
+ this._days * 864e5 +
1544
+ (this._months % 12) * 2592e6 +
1545
+ ~~(this._months / 12) * 31536e6;
1322
1546
  },
1323
1547
 
1324
1548
  humanize : function (withSuffix) {
@@ -1332,6 +1556,41 @@
1332
1556
  return this.lang().postformat(output);
1333
1557
  },
1334
1558
 
1559
+ add : function (input, val) {
1560
+ // supports only 2.0-style add(1, 's') or add(moment)
1561
+ var dur = moment.duration(input, val);
1562
+
1563
+ this._milliseconds += dur._milliseconds;
1564
+ this._days += dur._days;
1565
+ this._months += dur._months;
1566
+
1567
+ this._bubble();
1568
+
1569
+ return this;
1570
+ },
1571
+
1572
+ subtract : function (input, val) {
1573
+ var dur = moment.duration(input, val);
1574
+
1575
+ this._milliseconds -= dur._milliseconds;
1576
+ this._days -= dur._days;
1577
+ this._months -= dur._months;
1578
+
1579
+ this._bubble();
1580
+
1581
+ return this;
1582
+ },
1583
+
1584
+ get : function (units) {
1585
+ units = normalizeUnits(units);
1586
+ return this[units.toLowerCase() + 's']();
1587
+ },
1588
+
1589
+ as : function (units) {
1590
+ units = normalizeUnits(units);
1591
+ return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
1592
+ },
1593
+
1335
1594
  lang : moment.fn.lang
1336
1595
  };
1337
1596
 
@@ -1355,29 +1614,32 @@
1355
1614
  }
1356
1615
 
1357
1616
  makeDurationAsGetter('Weeks', 6048e5);
1617
+ moment.duration.fn.asMonths = function () {
1618
+ return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12;
1619
+ };
1358
1620
 
1359
1621
 
1360
1622
  /************************************
1361
- Default Lang
1362
- ************************************/
1623
+ Default Lang
1624
+ ************************************/
1363
1625
 
1364
1626
 
1365
- // Set default language, other languages will inherit from English.
1627
+ // Set default language, other languages will inherit from English.
1366
1628
  moment.lang('en', {
1367
1629
  ordinal : function (number) {
1368
1630
  var b = number % 10,
1369
1631
  output = (~~ (number % 100 / 10) === 1) ? 'th' :
1370
- (b === 1) ? 'st' :
1371
- (b === 2) ? 'nd' :
1372
- (b === 3) ? 'rd' : 'th';
1632
+ (b === 1) ? 'st' :
1633
+ (b === 2) ? 'nd' :
1634
+ (b === 3) ? 'rd' : 'th';
1373
1635
  return number + output;
1374
1636
  }
1375
1637
  });
1376
1638
 
1377
1639
 
1378
1640
  /************************************
1379
- Exposing Moment
1380
- ************************************/
1641
+ Exposing Moment
1642
+ ************************************/
1381
1643
 
1382
1644
 
1383
1645
  // CommonJS module is defined
@@ -1397,4 +1659,4 @@
1397
1659
  return moment;
1398
1660
  });
1399
1661
  }
1400
- }).call(this);
1662
+ }).call(this);