text: // moment.js
  // version : 2.0.0
  // author : Tim Wood
  // license : MIT
  // momentjs.com
  
  (function (undefined) {
  
      /************************************
          Constants
      ************************************/
  
      var moment,
          VERSION = "2.0.0",
          round = Math.round, i,
          // internal storage for language config files
          languages = {},
  
          // check for nodeJS
          hasModule = (typeof module !== 'undefined' && module.exports),
  
          // ASP.NET json date format regex
          aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
          aspNetTimeSpanJsonRegex = /(\-)?(\d*)?\.?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,
  
          // format tokens
          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,
          localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
  
          // parsing tokens
          parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
  
          // parsing token regexes
          parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
          parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
          parseTokenThreeDigits = /\d{3}/, // 000 - 999
          parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
          parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
          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.
          parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
          parseTokenT = /T/i, // T (ISO seperator)
          parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
  
          // preliminary iso regex
          // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
          isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
          isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
  
          // iso time formats and regexes
          isoTimes = [
              ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
              ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
              ['HH:mm', /(T| )\d\d:\d\d/],
              ['HH', /(T| )\d\d/]
          ],
  
          // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
          parseTimezoneChunker = /([\+\-]|\d\d)/gi,
  
          // getter and setter names
          proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
          unitMillisecondFactors = {
              'Milliseconds' : 1,
              'Seconds' : 1e3,
              'Minutes' : 6e4,
              'Hours' : 36e5,
              'Days' : 864e5,
              'Months' : 2592e6,
              'Years' : 31536e6
          },
  
          unitAliases = {
              ms : 'millisecond',
              s : 'second',
              m : 'minute',
              h : 'hour',
              d : 'day',
              w : 'week',
              M : 'month',
              y : 'year'
          },
  
          // format function strings
          formatFunctions = {},
  
          // tokens to ordinalize and pad
          ordinalizeTokens = 'DDD w W M D d'.split(' '),
          paddedTokens = 'M D H h m s w W'.split(' '),
  
          formatTokenFunctions = {
              M    : function () {
                  return this.month() + 1;
              },
              MMM  : function (format) {
                  return this.lang().monthsShort(this, format);
              },
              MMMM : function (format) {
                  return this.lang().months(this, format);
              },
              D    : function () {
                  return this.date();
              },
              DDD  : function () {
                  return this.dayOfYear();
              },
              d    : function () {
                  return this.day();
              },
              dd   : function (format) {
                  return this.lang().weekdaysMin(this, format);
              },
              ddd  : function (format) {
                  return this.lang().weekdaysShort(this, format);
              },
              dddd : function (format) {
                  return this.lang().weekdays(this, format);
              },
              w    : function () {
                  return this.week();
              },
              W    : function () {
                  return this.isoWeek();
              },
              YY   : function () {
                  return leftZeroFill(this.year() % 100, 2);
              },
              YYYY : function () {
                  return leftZeroFill(this.year(), 4);
              },
              YYYYY : function () {
                  return leftZeroFill(this.year(), 5);
              },
              gg   : function () {
                  return leftZeroFill(this.weekYear() % 100, 2);
              },
              gggg : function () {
                  return this.weekYear();
              },
              ggggg : function () {
                  return leftZeroFill(this.weekYear(), 5);
              },
              GG   : function () {
                  return leftZeroFill(this.isoWeekYear() % 100, 2);
              },
              GGGG : function () {
                  return this.isoWeekYear();
              },
              GGGGG : function () {
                  return leftZeroFill(this.isoWeekYear(), 5);
              },
              e : function () {
                  return this.weekday();
              },
              E : function () {
                  return this.isoWeekday();
              },
              a    : function () {
                  return this.lang().meridiem(this.hours(), this.minutes(), true);
              },
              A    : function () {
                  return this.lang().meridiem(this.hours(), this.minutes(), false);
              },
              H    : function () {
                  return this.hours();
              },
              h    : function () {
                  return this.hours() % 12 || 12;
              },
              m    : function () {
                  return this.minutes();
              },
              s    : function () {
                  return this.seconds();
              },
              S    : function () {
                  return ~~(this.milliseconds() / 100);
              },
              SS   : function () {
                  return leftZeroFill(~~(this.milliseconds() / 10), 2);
              },
              SSS  : function () {
                  return leftZeroFill(this.milliseconds(), 3);
              },
              Z    : function () {
                  var a = -this.zone(),
                      b = "+";
                  if (a < 0) {
                      a = -a;
                      b = "-";
                  }
                  return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2);
              },
              ZZ   : function () {
                  var a = -this.zone(),
                      b = "+";
                  if (a < 0) {
                      a = -a;
                      b = "-";
                  }
                  return b + leftZeroFill(~~(10 * a / 6), 4);
              },
              z : function () {
                  return this.zoneAbbr();
              },
              zz : function () {
                  return this.zoneName();
              },
              X    : function () {
                  return this.unix();
              }
          };
  
      function padToken(func, count) {
          return function (a) {
              return leftZeroFill(func.call(this, a), count);
          };
      }
      function ordinalizeToken(func, period) {
          return function (a) {
              return this.lang().ordinal(func.call(this, a), period);
          };
      }
  
      while (ordinalizeTokens.length) {
          i = ordinalizeTokens.pop();
          formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
      }
      while (paddedTokens.length) {
          i = paddedTokens.pop();
          formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
      }
      formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
  
  
      /************************************
          Constructors
      ************************************/
  
      function Language() {
  
      }
  
      // Moment prototype object
      function Moment(config) {
          extend(this, config);
      }
  
      // Duration Constructor
      function Duration(duration) {
          var data = this._data = {},
              years = duration.years || duration.year || duration.y || 0,
              months = duration.months || duration.month || duration.M || 0,
              weeks = duration.weeks || duration.week || duration.w || 0,
              days = duration.days || duration.day || duration.d || 0,
              hours = duration.hours || duration.hour || duration.h || 0,
              minutes = duration.minutes || duration.minute || duration.m || 0,
              seconds = duration.seconds || duration.second || duration.s || 0,
              milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0;
  
          // representation for dateAddRemove
          this._milliseconds = milliseconds +
              seconds * 1e3 + // 1000
              minutes * 6e4 + // 1000 * 60
              hours * 36e5; // 1000 * 60 * 60
          // Because of dateAddRemove treats 24 hours as different from a
          // day when working around DST, we need to store them separately
          this._days = days +
              weeks * 7;
          // It is impossible translate months into days without knowing
          // which months you are are talking about, so we have to store
          // it separately.
          this._months = months +
              years * 12;
  
          // The following code bubbles up values, see the tests for
          // examples of what that means.
          data.milliseconds = milliseconds % 1000;
          seconds += absRound(milliseconds / 1000);
  
          data.seconds = seconds % 60;
          minutes += absRound(seconds / 60);
  
          data.minutes = minutes % 60;
          hours += absRound(minutes / 60);
  
          data.hours = hours % 24;
          days += absRound(hours / 24);
  
          days += weeks * 7;
          data.days = days % 30;
  
          months += absRound(days / 30);
  
          data.months = months % 12;
          years += absRound(months / 12);
  
          data.years = years;
      }
  
  
      /************************************
          Helpers
      ************************************/
  
  
      function extend(a, b) {
          for (var i in b) {
              if (b.hasOwnProperty(i)) {
                  a[i] = b[i];
              }
          }
          return a;
      }
  
      function absRound(number) {
          if (number < 0) {
              return Math.ceil(number);
          } else {
              return Math.floor(number);
          }
      }
  
      // left zero fill a number
      // see http://jsperf.com/left-zero-filling for performance comparison
      function leftZeroFill(number, targetLength) {
          var output = number + '';
          while (output.length < targetLength) {
              output = '0' + output;
          }
          return output;
      }
  
      // helper function for _.addTime and _.subtractTime
      function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) {
          var milliseconds = duration._milliseconds,
              days = duration._days,
              months = duration._months,
              minutes,
              hours,
              currentDate;
  
          if (milliseconds) {
              mom._d.setTime(+mom._d + milliseconds * isAdding);
          }
          // store the minutes and hours so we can restore them
          if (days || months) {
              minutes = mom.minute();
              hours = mom.hour();
          }
          if (days) {
              mom.date(mom.date() + days * isAdding);
          }
          if (months) {
              currentDate = mom.date();
              mom.date(1)
                  .month(mom.month() + months * isAdding)
                  .date(Math.min(currentDate, mom.daysInMonth()));
          }
          if (milliseconds && !ignoreUpdateOffset) {
              moment.updateOffset(mom);
          }
          // restore the minutes and hours after possibly changing dst
          if (days || months) {
              mom.minute(minutes);
              mom.hour(hours);
          }
      }
  
      // check if is an array
      function isArray(input) {
          return Object.prototype.toString.call(input) === '[object Array]';
      }
  
      // compare two arrays, return the number of differences
      function compareArrays(array1, array2) {
          var len = Math.min(array1.length, array2.length),
              lengthDiff = Math.abs(array1.length - array2.length),
              diffs = 0,
              i;
          for (i = 0; i < len; i++) {
              if (~~array1[i] !== ~~array2[i]) {
                  diffs++;
              }
          }
          return diffs + lengthDiff;
      }
  
      function normalizeUnits(units) {
          return units ? unitAliases[units] || units.toLowerCase().replace(/(.)s$/, '$1') : units;
      }
  
  
      /************************************
          Languages
      ************************************/
  
  
      Language.prototype = {
          set : function (config) {
              var prop, i;
              for (i in config) {
                  prop = config[i];
                  if (typeof prop === 'function') {
                      this[i] = prop;
                  } else {
                      this['_' + i] = prop;
                  }
              }
          },
  
          _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
          months : function (m) {
              return this._months[m.month()];
          },
  
          _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
          monthsShort : function (m) {
              return this._monthsShort[m.month()];
          },
  
          monthsParse : function (monthName) {
              var i, mom, regex;
  
              if (!this._monthsParse) {
                  this._monthsParse = [];
              }
  
              for (i = 0; i < 12; i++) {
                  // make the regex if we don't have it already
                  if (!this._monthsParse[i]) {
                      mom = moment([2000, i]);
                      regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
                      this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
                  }
                  // test the regex
                  if (this._monthsParse[i].test(monthName)) {
                      return i;
                  }
              }
          },
  
          _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
          weekdays : function (m) {
              return this._weekdays[m.day()];
          },
  
          _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
          weekdaysShort : function (m) {
              return this._weekdaysShort[m.day()];
          },
  
          _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
          weekdaysMin : function (m) {
              return this._weekdaysMin[m.day()];
          },
  
          weekdaysParse : function (weekdayName) {
              var i, mom, regex;
  
              if (!this._weekdaysParse) {
                  this._weekdaysParse = [];
              }
  
              for (i = 0; i < 7; i++) {
                  // make the regex if we don't have it already
                  if (!this._weekdaysParse[i]) {
                      mom = moment([2000, 1]).day(i);
                      regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
                      this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
                  }
                  // test the regex
                  if (this._weekdaysParse[i].test(weekdayName)) {
                      return i;
                  }
              }
          },
  
          _longDateFormat : {
              LT : "h:mm A",
              L : "MM/DD/YYYY",
              LL : "MMMM D YYYY",
              LLL : "MMMM D YYYY LT",
              LLLL : "dddd, MMMM D YYYY LT"
          },
          longDateFormat : function (key) {
              var output = this._longDateFormat[key];
              if (!output && this._longDateFormat[key.toUpperCase()]) {
                  output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
                      return val.slice(1);
                  });
                  this._longDateFormat[key] = output;
              }
              return output;
          },
  
          isPM : function (input) {
              return ((input + '').toLowerCase()[0] === 'p');
          },
  
          _meridiemParse : /[ap]\.?m?\.?/i,
          meridiem : function (hours, minutes, isLower) {
              if (hours > 11) {
                  return isLower ? 'pm' : 'PM';
              } else {
                  return isLower ? 'am' : 'AM';
              }
          },
  
          _calendar : {
              sameDay : '[Today at] LT',
              nextDay : '[Tomorrow at] LT',
              nextWeek : 'dddd [at] LT',
              lastDay : '[Yesterday at] LT',
              lastWeek : '[Last] dddd [at] LT',
              sameElse : 'L'
          },
          calendar : function (key, mom) {
              var output = this._calendar[key];
              return typeof output === 'function' ? output.apply(mom) : output;
          },
  
          _relativeTime : {
              future : "in %s",
              past : "%s ago",
              s : "a few seconds",
              m : "a minute",
              mm : "%d minutes",
              h : "an hour",
              hh : "%d hours",
              d : "a day",
              dd : "%d days",
              M : "a month",
              MM : "%d months",
              y : "a year",
              yy : "%d years"
          },
          relativeTime : function (number, withoutSuffix, string, isFuture) {
              var output = this._relativeTime[string];
              return (typeof output === 'function') ?
                  output(number, withoutSuffix, string, isFuture) :
                  output.replace(/%d/i, number);
          },
          pastFuture : function (diff, output) {
              var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
              return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
          },
  
          ordinal : function (number) {
              return this._ordinal.replace("%d", number);
          },
          _ordinal : "%d",
  
          preparse : function (string) {
              return string;
          },
  
          postformat : function (string) {
              return string;
          },
  
          week : function (mom) {
              return weekOfYear(mom, this._week.dow, this._week.doy).week;
          },
          _week : {
              dow : 0, // Sunday is the first day of the week.
              doy : 6  // The week that contains Jan 1st is the first week of the year.
          }
      };
  
      // Loads a language definition into the `languages` cache.  The function
      // takes a key and optionally values.  If not in the browser and no values
      // are provided, it will load the language file module.  As a convenience,
      // this function also returns the language values.
      function loadLang(key, values) {
          values.abbr = key;
          if (!languages[key]) {
              languages[key] = new Language();
          }
          languages[key].set(values);
          return languages[key];
      }
  
      // Determines which language definition to use and returns it.
      //
      // With no parameters, it will return the global language.  If you
      // pass in a language key, such as 'en', it will return the
      // definition for 'en', so long as 'en' has already been loaded using
      // moment.lang.
      function getLangDefinition(key) {
          if (!key) {
              return moment.fn._lang;
          }
          if (!languages[key] && hasModule) {
              require('./lang/' + key);
          }
          return languages[key];
      }
  
  
      /************************************
          Formatting
      ************************************/
  
  
      function removeFormattingTokens(input) {
          if (input.match(/\[.*\]/)) {
              return input.replace(/^\[|\]$/g, "");
          }
          return input.replace(/\\/g, "");
      }
  
      function makeFormatFunction(format) {
          var array = format.match(formattingTokens), i, length;
  
          for (i = 0, length = array.length; i < length; i++) {
              if (formatTokenFunctions[array[i]]) {
                  array[i] = formatTokenFunctions[array[i]];
              } else {
                  array[i] = removeFormattingTokens(array[i]);
              }
          }
  
          return function (mom) {
              var output = "";
              for (i = 0; i < length; i++) {
                  output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
              }
              return output;
          };
      }
  
      // format date using native date object
      function formatMoment(m, format) {
          var i = 5;
  
          function replaceLongDateFormatTokens(input) {
              return m.lang().longDateFormat(input) || input;
          }
  
          while (i-- && localFormattingTokens.test(format)) {
              format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
          }
  
          if (!formatFunctions[format]) {
              formatFunctions[format] = makeFormatFunction(format);
          }
  
          return formatFunctions[format](m);
      }
  
  
      /************************************
          Parsing
      ************************************/
  
  
      // get the regex to find the next token
      function getParseRegexForToken(token, config) {
          switch (token) {
          case 'DDDD':
              return parseTokenThreeDigits;
          case 'YYYY':
              return parseTokenFourDigits;
          case 'YYYYY':
              return parseTokenSixDigits;
          case 'S':
          case 'SS':
          case 'SSS':
          case 'DDD':
              return parseTokenOneToThreeDigits;
          case 'MMM':
          case 'MMMM':
          case 'dd':
          case 'ddd':
          case 'dddd':
              return parseTokenWord;
          case 'a':
          case 'A':
              return getLangDefinition(config._l)._meridiemParse;
          case 'X':
              return parseTokenTimestampMs;
          case 'Z':
          case 'ZZ':
              return parseTokenTimezone;
          case 'T':
              return parseTokenT;
          case 'MM':
          case 'DD':
          case 'YY':
          case 'HH':
          case 'hh':
          case 'mm':
          case 'ss':
          case 'M':
          case 'D':
          case 'd':
          case 'H':
          case 'h':
          case 'm':
          case 's':
              return parseTokenOneOrTwoDigits;
          default :
              return new RegExp(token.replace('\\', ''));
          }
      }
  
      function timezoneMinutesFromString(string) {
          var tzchunk = (parseTokenTimezone.exec(string) || [])[0],
              parts = (tzchunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
              minutes = +(parts[1] * 60) + ~~parts[2];
  
          return parts[0] === '+' ? -minutes : minutes;
      }
  
      // function to convert string input to date
      function addTimeToArrayFromToken(token, input, config) {
          var a, b,
              datePartArray = config._a;
  
          switch (token) {
          // MONTH
          case 'M' : // fall through to MM
          case 'MM' :
              datePartArray[1] = (input == null) ? 0 : ~~input - 1;
              break;
          case 'MMM' : // fall through to MMMM
          case 'MMMM' :
              a = getLangDefinition(config._l).monthsParse(input);
              // if we didn't find a month name, mark the date as invalid.
              if (a != null) {
                  datePartArray[1] = a;
              } else {
                  config._isValid = false;
              }
              break;
          // DAY OF MONTH
          case 'D' : // fall through to DDDD
          case 'DD' : // fall through to DDDD
          case 'DDD' : // fall through to DDDD
          case 'DDDD' :
              if (input != null) {
                  datePartArray[2] = ~~input;
              }
              break;
          // YEAR
          case 'YY' :
              datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
              break;
          case 'YYYY' :
          case 'YYYYY' :
              datePartArray[0] = ~~input;
              break;
          // AM / PM
          case 'a' : // fall through to A
          case 'A' :
              config._isPm = getLangDefinition(config._l).isPM(input);
              break;
          // 24 HOUR
          case 'H' : // fall through to hh
          case 'HH' : // fall through to hh
          case 'h' : // fall through to hh
          case 'hh' :
              datePartArray[3] = ~~input;
              break;
          // MINUTE
          case 'm' : // fall through to mm
          case 'mm' :
              datePartArray[4] = ~~input;
              break;
          // SECOND
          case 's' : // fall through to ss
          case 'ss' :
              datePartArray[5] = ~~input;
              break;
          // MILLISECOND
          case 'S' :
          case 'SS' :
          case 'SSS' :
              datePartArray[6] = ~~ (('0.' + input) * 1000);
              break;
          // UNIX TIMESTAMP WITH MS
          case 'X':
              config._d = new Date(parseFloat(input) * 1000);
              break;
          // TIMEZONE
          case 'Z' : // fall through to ZZ
          case 'ZZ' :
              config._useUTC = true;
              config._tzm = timezoneMinutesFromString(input);
              break;
          }
  
          // if the input is null, the date is not valid
          if (input == null) {
              config._isValid = false;
          }
      }
  
      // convert an array to a date.
      // the array should mirror the parameters below
      // note: all values past the year are optional and will default to the lowest possible value.
      // [year, month, day , hour, minute, second, millisecond]
      function dateFromArray(config) {
          var i, date, input = [];
  
          if (config._d) {
              return;
          }
  
          for (i = 0; i < 7; i++) {
              config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
          }
  
          // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
          input[3] += ~~((config._tzm || 0) / 60);
          input[4] += ~~((config._tzm || 0) % 60);
  
          date = new Date(0);
  
          if (config._useUTC) {
              date.setUTCFullYear(input[0], input[1], input[2]);
              date.setUTCHours(input[3], input[4], input[5], input[6]);
          } else {
              date.setFullYear(input[0], input[1], input[2]);
              date.setHours(input[3], input[4], input[5], input[6]);
          }
  
          config._d = date;
      }
  
      // date from string and format string
      function makeDateFromStringAndFormat(config) {
          // This array is used to make a Date, either with `new Date` or `Date.UTC`
          var tokens = config._f.match(formattingTokens),
              string = config._i,
              i, parsedInput;
  
          config._a = [];
  
          for (i = 0; i < tokens.length; i++) {
              parsedInput = (getParseRegexForToken(tokens[i], config).exec(string) || [])[0];
              if (parsedInput) {
                  string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
              }
              // don't parse if its not a known token
              if (formatTokenFunctions[tokens[i]]) {
                  addTimeToArrayFromToken(tokens[i], parsedInput, config);
              }
          }
  
          // add remaining unparsed input to the string
          if (string) {
              config._il = string;
          }
  
          // handle am pm
          if (config._isPm && config._a[3] < 12) {
              config._a[3] += 12;
          }
          // if is 12 am, change hours to 0
          if (config._isPm === false && config._a[3] === 12) {
              config._a[3] = 0;
          }
          // return
          dateFromArray(config);
      }
  
      // date from string and array of format strings
      function makeDateFromStringAndArray(config) {
          var tempConfig,
              tempMoment,
              bestMoment,
  
              scoreToBeat = 99,
              i,
              currentScore;
  
          for (i = 0; i < config._f.length; i++) {
              tempConfig = extend({}, config);
              tempConfig._f = config._f[i];
              makeDateFromStringAndFormat(tempConfig);
              tempMoment = new Moment(tempConfig);
  
              currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
  
              // if there is any input that was not parsed
              // add a penalty for that format
              if (tempMoment._il) {
                  currentScore += tempMoment._il.length;
              }
  
              if (currentScore < scoreToBeat) {
                  scoreToBeat = currentScore;
                  bestMoment = tempMoment;
              }
          }
  
          extend(config, bestMoment);
      }
  
      // date from iso format
      function makeDateFromString(config) {
          var i,
              string = config._i,
              match = isoRegex.exec(string);
  
          if (match) {
              // match[2] should be "T" or undefined
              config._f = 'YYYY-MM-DD' + (match[2] || " ");
              for (i = 0; i < 4; i++) {
                  if (isoTimes[i][1].exec(string)) {
                      config._f += isoTimes[i][0];
                      break;
                  }
              }
              if (parseTokenTimezone.exec(string)) {
                  config._f += " Z";
              }
              makeDateFromStringAndFormat(config);
          } else {
              config._d = new Date(string);
          }
      }
  
      function makeDateFromInput(config) {
          var input = config._i,
              matched = aspNetJsonRegex.exec(input);
  
          if (input === undefined) {
              config._d = new Date();
          } else if (matched) {
              config._d = new Date(+matched[1]);
          } else if (typeof input === 'string') {
              makeDateFromString(config);
          } else if (isArray(input)) {
              config._a = input.slice(0);
              dateFromArray(config);
          } else {
              config._d = input instanceof Date ? new Date(+input) : new Date(input);
          }
      }
  
  
      /************************************
          Relative Time
      ************************************/
  
  
      // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
      function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
          return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
      }
  
      function relativeTime(milliseconds, withoutSuffix, lang) {
          var seconds = round(Math.abs(milliseconds) / 1000),
              minutes = round(seconds / 60),
              hours = round(minutes / 60),
              days = round(hours / 24),
              years = round(days / 365),
              args = seconds < 45 && ['s', seconds] ||
                  minutes === 1 && ['m'] ||
                  minutes < 45 && ['mm', minutes] ||
                  hours === 1 && ['h'] ||
                  hours < 22 && ['hh', hours] ||
                  days === 1 && ['d'] ||
                  days <= 25 && ['dd', days] ||
                  days <= 45 && ['M'] ||
                  days < 345 && ['MM', round(days / 30)] ||
                  years === 1 && ['y'] || ['yy', years];
          args[2] = withoutSuffix;
          args[3] = milliseconds > 0;
          args[4] = lang;
          return substituteTimeAgo.apply({}, args);
      }
  
  
      /************************************
          Week of Year
      ************************************/
  
  
      // firstDayOfWeek       0 = sun, 6 = sat
      //                      the day of the week that starts the week
      //                      (usually sunday or monday)
      // firstDayOfWeekOfYear 0 = sun, 6 = sat
      //                      the first week is the week that contains the first
      //                      of this day of the week
      //                      (eg. ISO weeks use thursday (4))
      function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
          var end = firstDayOfWeekOfYear - firstDayOfWeek,
              daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
              adjustedMoment;
  
  
          if (daysToDayOfWeek > end) {
              daysToDayOfWeek -= 7;
          }
  
          if (daysToDayOfWeek < end - 7) {
              daysToDayOfWeek += 7;
          }
  
          adjustedMoment = moment(mom).add('d', daysToDayOfWeek);
          return {
              week: Math.ceil(adjustedMoment.dayOfYear() / 7),
              year: adjustedMoment.year()
          };
      }
  
  
      /************************************
          Top Level Functions
      ************************************/
  
      function makeMoment(config) {
          var input = config._i,
              format = config._f;
  
          if (input === null || input === '') {
              return null;
          }
  
          if (typeof input === 'string') {
              config._i = input = getLangDefinition().preparse(input);
          }
  
          if (moment.isMoment(input)) {
              config = extend({}, input);
              config._d = new Date(+input._d);
          } else if (format) {
              if (isArray(format)) {
                  makeDateFromStringAndArray(config);
              } else {
                  makeDateFromStringAndFormat(config);
              }
          } else {
              makeDateFromInput(config);
          }
  
          return new Moment(config);
      }
  
      moment = function (input, format, lang) {
          return makeMoment({
              _i : input,
              _f : format,
              _l : lang,
              _isUTC : false
          });
      };
  
      // creating with utc
      moment.utc = function (input, format, lang) {
          return makeMoment({
              _useUTC : true,
              _isUTC : true,
              _l : lang,
              _i : input,
              _f : format
          });
      };
  
      // creating with unix timestamp (in seconds)
      moment.unix = function (input) {
          return moment(input * 1000);
      };
  
      // duration
      moment.duration = function (input, key) {
          var isDuration = moment.isDuration(input),
              isNumber = (typeof input === 'number'),
              duration = (isDuration ? input._data : (isNumber ? {} : input)),
              matched = aspNetTimeSpanJsonRegex.exec(input),
              sign,
              ret;
  
          if (isNumber) {
              if (key) {
                  duration[key] = input;
              } else {
                  duration.milliseconds = input;
              }
          } else if (matched) {
              sign = (matched[1] === "-") ? -1 : 1;
              duration = {
                  y: 0,
                  d: ~~matched[2] * sign,
                  h: ~~matched[3] * sign,
                  m: ~~matched[4] * sign,
                  s: ~~matched[5] * sign,
                  ms: ~~matched[6] * sign
              };
          }
  
          ret = new Duration(duration);
  
          if (isDuration && input.hasOwnProperty('_lang')) {
              ret._lang = input._lang;
          }
  
          return ret;
      };
  
      // version number
      moment.version = VERSION;
  
      // default format
      moment.defaultFormat = isoFormat;
  
      // This function will be called whenever a moment is mutated.
      // It is intended to keep the offset in sync with the timezone.
      moment.updateOffset = function () {};
  
      // This function will load languages and then set the global language.  If
      // no arguments are passed in, it will simply return the current global
      // language key.
      moment.lang = function (key, values) {
          var i;
  
          if (!key) {
              return moment.fn._lang._abbr;
          }
          if (values) {
              loadLang(key, values);
          } else if (!languages[key]) {
              getLangDefinition(key);
          }
          moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
      };
  
      // returns language data
      moment.langData = function (key) {
          if (key && key._lang && key._lang._abbr) {
              key = key._lang._abbr;
          }
          return getLangDefinition(key);
      };
  
      // compare moment object
      moment.isMoment = function (obj) {
          return obj instanceof Moment;
      };
  
      // for typechecking Duration objects
      moment.isDuration = function (obj) {
          return obj instanceof Duration;
      };
  
  
      /************************************
          Moment Prototype
      ************************************/
  
  
      moment.fn = Moment.prototype = {
  
          clone : function () {
              return moment(this);
          },
  
          valueOf : function () {
              return +this._d + ((this._offset || 0) * 60000);
          },
  
          unix : function () {
              return Math.floor(+this / 1000);
          },
  
          toString : function () {
              return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
          },
  
          toDate : function () {
              return this._offset ? new Date(+this) : this._d;
          },
  
          toISOString : function () {
              return formatMoment(moment(this).utc(), 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
          },
  
          toArray : function () {
              var m = this;
              return [
                  m.year(),
                  m.month(),
                  m.date(),
                  m.hours(),
                  m.minutes(),
                  m.seconds(),
                  m.milliseconds()
              ];
          },
  
          isValid : function () {
              if (this._isValid == null) {
                  if (this._a) {
                      this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray());
                  } else {
                      this._isValid = !isNaN(this._d.getTime());
                  }
              }
              return !!this._isValid;
          },
  
          utc : function () {
              return this.zone(0);
          },
  
          local : function () {
              this.zone(0);
              this._isUTC = false;
              return this;
          },
  
          format : function (inputString) {
              var output = formatMoment(this, inputString || moment.defaultFormat);
              return this.lang().postformat(output);
          },
  
          add : function (input, val) {
              var dur;
              // switch args to support add('s', 1) and add(1, 's')
              if (typeof input === 'string') {
                  dur = moment.duration(+val, input);
              } else {
                  dur = moment.duration(input, val);
              }
              addOrSubtractDurationFromMoment(this, dur, 1);
              return this;
          },
  
          subtract : function (input, val) {
              var dur;
              // switch args to support subtract('s', 1) and subtract(1, 's')
              if (typeof input === 'string') {
                  dur = moment.duration(+val, input);
              } else {
                  dur = moment.duration(input, val);
              }
              addOrSubtractDurationFromMoment(this, dur, -1);
              return this;
          },
  
          diff : function (input, units, asFloat) {
              var that = this._isUTC ? moment(input).zone(this._offset || 0) : moment(input).local(),
                  zoneDiff = (this.zone() - that.zone()) * 6e4,
                  diff, output;
  
              units = normalizeUnits(units);
  
              if (units === 'year' || units === 'month') {
                  // average number of days in the months in the given dates
                  diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
                  // difference in months
                  output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
                  // adjust by taking difference in days, average number of days
                  // and dst in the given months.
                  output += ((this - moment(this).startOf('month')) -
                          (that - moment(that).startOf('month'))) / diff;
                  // same as above but with zones, to negate all dst
                  output -= ((this.zone() - moment(this).startOf('month').zone()) -
                          (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff;
                  if (units === 'year') {
                      output = output / 12;
                  }
              } else {
                  diff = (this - that);
                  output = units === 'second' ? diff / 1e3 : // 1000
                      units === 'minute' ? diff / 6e4 : // 1000 * 60
                      units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
                      units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
                      units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
                      diff;
              }
              return asFloat ? output : absRound(output);
          },
  
          from : function (time, withoutSuffix) {
              return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
          },
  
          fromNow : function (withoutSuffix) {
              return this.from(moment(), withoutSuffix);
          },
  
          calendar : function () {
              var diff = this.diff(moment().startOf('day'), 'days', true),
                  format = diff < -6 ? 'sameElse' :
                  diff < -1 ? 'lastWeek' :
                  diff < 0 ? 'lastDay' :
                  diff < 1 ? 'sameDay' :
                  diff < 2 ? 'nextDay' :
                  diff < 7 ? 'nextWeek' : 'sameElse';
              return this.format(this.lang().calendar(format, this));
          },
  
          isLeapYear : function () {
              var year = this.year();
              return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
          },
  
          isDST : function () {
              return (this.zone() < this.clone().month(0).zone() ||
                  this.zone() < this.clone().month(5).zone());
          },
  
          day : function (input) {
              var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
              if (input != null) {
                  if (typeof input === 'string') {
                      input = this.lang().weekdaysParse(input);
                      if (typeof input !== 'number') {
                          return this;
                      }
                  }
                  return this.add({ d : input - day });
              } else {
                  return day;
              }
          },
  
          month : function (input) {
              var utc = this._isUTC ? 'UTC' : '';
              if (input != null) {
                  if (typeof input === 'string') {
                      input = this.lang().monthsParse(input);
                      if (typeof input !== 'number') {
                          return this;
                      }
                  }
                  this._d['set' + utc + 'Month'](input);
                  moment.updateOffset(this);
                  return this;
              } else {
                  return this._d['get' + utc + 'Month']();
              }
          },
  
          startOf: function (units) {
              units = normalizeUnits(units);
              // the following switch intentionally omits break keywords
              // to utilize falling through the cases.
              switch (units) {
              case 'year':
                  this.month(0);
                  /* falls through */
              case 'month':
                  this.date(1);
                  /* falls through */
              case 'week':
              case 'day':
                  this.hours(0);
                  /* falls through */
              case 'hour':
                  this.minutes(0);
                  /* falls through */
              case 'minute':
                  this.seconds(0);
                  /* falls through */
              case 'second':
                  this.milliseconds(0);
                  /* falls through */
              }
  
              // weeks are a special case
              if (units === 'week') {
                  this.weekday(0);
              }
  
              return this;
          },
  
          endOf: function (units) {
              return this.startOf(units).add(units, 1).subtract('ms', 1);
          },
  
          isAfter: function (input, units) {
              units = typeof units !== 'undefined' ? units : 'millisecond';
              return +this.clone().startOf(units) > +moment(input).startOf(units);
          },
  
          isBefore: function (input, units) {
              units = typeof units !== 'undefined' ? units : 'millisecond';
              return +this.clone().startOf(units) < +moment(input).startOf(units);
          },
  
          isSame: function (input, units) {
              units = typeof units !== 'undefined' ? units : 'millisecond';
              return +this.clone().startOf(units) === +moment(input).startOf(units);
          },
  
          min: function (other) {
              other = moment.apply(null, arguments);
              return other < this ? this : other;
          },
  
          max: function (other) {
              other = moment.apply(null, arguments);
              return other > this ? this : other;
          },
  
          zone : function (input) {
              var offset = this._offset || 0;
              if (input != null) {
                  if (typeof input === "string") {
                      input = timezoneMinutesFromString(input);
                  }
                  if (Math.abs(input) < 16) {
                      input = input * 60;
                  }
                  this._offset = input;
                  this._isUTC = true;
                  if (offset !== input) {
                      addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true);
                  }
              } else {
                  return this._isUTC ? offset : this._d.getTimezoneOffset();
              }
              return this;
          },
  
          zoneAbbr : function () {
              return this._isUTC ? "UTC" : "";
          },
  
          zoneName : function () {
              return this._isUTC ? "Coordinated Universal Time" : "";
          },
  
          daysInMonth : function () {
              return moment.utc([this.year(), this.month() + 1, 0]).date();
          },
  
          dayOfYear : function (input) {
              var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
              return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
          },
  
          weekYear : function (input) {
              var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year;
              return input == null ? year : this.add("y", (input - year));
          },
  
          isoWeekYear : function (input) {
              var year = weekOfYear(this, 1, 4).year;
              return input == null ? year : this.add("y", (input - year));
          },
  
          week : function (input) {
              var week = this.lang().week(this);
              return input == null ? week : this.add("d", (input - week) * 7);
          },
  
          isoWeek : function (input) {
              var week = weekOfYear(this, 1, 4).week;
              return input == null ? week : this.add("d", (input - week) * 7);
          },
  
          weekday : function (input) {
              var weekday = (this._d.getDay() + 7 - this.lang()._week.dow) % 7;
              return input == null ? weekday : this.add("d", input - weekday);
          },
  
          isoWeekday : function (input) {
              // behaves the same as moment#day except
              // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
              // as a setter, sunday should belong to the previous week.
              return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
          },
  
          // If passed a language key, it will set the language for this
          // instance.  Otherwise, it will return the language configuration
          // variables for this instance.
          lang : function (key) {
              if (key === undefined) {
                  return this._lang;
              } else {
                  this._lang = getLangDefinition(key);
                  return this;
              }
          }
      };
  
      // helper for adding shortcuts
      function makeGetterAndSetter(name, key) {
          moment.fn[name] = moment.fn[name + 's'] = function (input) {
              var utc = this._isUTC ? 'UTC' : '';
              if (input != null) {
                  this._d['set' + utc + key](input);
                  moment.updateOffset(this);
                  return this;
              } else {
                  return this._d['get' + utc + key]();
              }
          };
      }
  
      // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
      for (i = 0; i < proxyGettersAndSetters.length; i ++) {
          makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
      }
  
      // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
      makeGetterAndSetter('year', 'FullYear');
  
      // add plural methods
      moment.fn.days = moment.fn.day;
      moment.fn.months = moment.fn.month;
      moment.fn.weeks = moment.fn.week;
      moment.fn.isoWeeks = moment.fn.isoWeek;
  
      // add aliased format methods
      moment.fn.toJSON = moment.fn.toISOString;
  
      /************************************
          Duration Prototype
      ************************************/
  
  
      moment.duration.fn = Duration.prototype = {
          weeks : function () {
              return absRound(this.days() / 7);
          },
  
          valueOf : function () {
              return this._milliseconds +
                this._days * 864e5 +
                (this._months % 12) * 2592e6 +
                ~~(this._months / 12) * 31536e6;
          },
  
          humanize : function (withSuffix) {
              var difference = +this,
                  output = relativeTime(difference, !withSuffix, this.lang());
  
              if (withSuffix) {
                  output = this.lang().pastFuture(difference, output);
              }
  
              return this.lang().postformat(output);
          },
  
          add : function (input, val) {
              // supports only 2.0-style add(1, 's') or add(moment)
              var dur = moment.duration(input, val);
  
              this._milliseconds += dur._milliseconds;
              this._days += dur._days;
              this._months += dur._months;
  
              return this;
          },
  
          subtract : function (input, val) {
              var dur = moment.duration(input, val);
  
              this._milliseconds -= dur._milliseconds;
              this._days -= dur._days;
              this._months -= dur._months;
  
              return this;
          },
  
          get : function (units) {
              units = normalizeUnits(units);
              return this[units.toLowerCase() + 's']();
          },
  
          as : function (units) {
              units = normalizeUnits(units);
              return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
          },
  
          lang : moment.fn.lang
      };
  
      function makeDurationGetter(name) {
          moment.duration.fn[name] = function () {
              return this._data[name];
          };
      }
  
      function makeDurationAsGetter(name, factor) {
          moment.duration.fn['as' + name] = function () {
              return +this / factor;
          };
      }
  
      for (i in unitMillisecondFactors) {
          if (unitMillisecondFactors.hasOwnProperty(i)) {
              makeDurationAsGetter(i, unitMillisecondFactors[i]);
              makeDurationGetter(i.toLowerCase());
          }
      }
  
      makeDurationAsGetter('Weeks', 6048e5);
      moment.duration.fn.asMonths = function () {
          return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12;
      };
  
  
      /************************************
          Default Lang
      ************************************/
  
  
      // Set default language, other languages will inherit from English.
      moment.lang('en', {
          ordinal : function (number) {
              var b = number % 10,
                  output = (~~ (number % 100 / 10) === 1) ? 'th' :
                  (b === 1) ? 'st' :
                  (b === 2) ? 'nd' :
                  (b === 3) ? 'rd' : 'th';
              return number + output;
          }
      });
  
  
      /************************************
          Exposing Moment
      ************************************/
  
  
      // CommonJS module is defined
      if (hasModule) {
          module.exports = moment;
      }
      /*global ender:false */
      if (typeof ender === 'undefined') {
          // here, `this` means `window` in the browser, or `global` on the server
          // add `moment` as a global object via a string identifier,
          // for Closure Compiler "advanced" mode
          this['moment'] = moment;
      }
      /*global define:false */
      if (typeof define === "function" && define.amd) {
          define("moment", [], function () {
              return moment;
          });
      }
  }).call(this);
  