javascript-time-ago 2.5.12 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +410 -272
  3. package/bundle/javascript-time-ago.js +1 -1
  4. package/bundle/javascript-time-ago.js.map +1 -1
  5. package/bundle/javascript-time-ago.min.js +1 -1
  6. package/bundle/javascript-time-ago.min.js.map +1 -1
  7. package/commonjs/FullDateFormatter.js +72 -0
  8. package/commonjs/FullDateFormatter.js.map +1 -0
  9. package/commonjs/FullDateFormatter.test.js +26 -0
  10. package/commonjs/FullDateFormatter.test.js.map +1 -0
  11. package/commonjs/TimeAgo.js +208 -106
  12. package/commonjs/TimeAgo.js.map +1 -1
  13. package/commonjs/TimeAgo.test.js +95 -10
  14. package/commonjs/TimeAgo.test.js.map +1 -1
  15. package/commonjs/steps/getStepMinTime.js +26 -19
  16. package/commonjs/steps/getStepMinTime.js.map +1 -1
  17. package/commonjs/steps/getTimeToNextUpdate.js +10 -2
  18. package/commonjs/steps/getTimeToNextUpdate.js.map +1 -1
  19. package/commonjs/style/twitter.js +2 -3
  20. package/commonjs/style/twitter.js.map +1 -1
  21. package/commonjs/style/twitter.test.js +5 -2
  22. package/commonjs/style/twitter.test.js.map +1 -1
  23. package/full-date-formatter/index.cjs +4 -0
  24. package/full-date-formatter/index.cjs.js +9 -0
  25. package/full-date-formatter/index.d.ts +6 -0
  26. package/full-date-formatter/index.js +1 -0
  27. package/full-date-formatter/package.json +15 -0
  28. package/index.cjs +1 -1
  29. package/index.cjs.js +2 -2
  30. package/index.d.ts +14 -4
  31. package/index.js +3 -1
  32. package/modules/FullDateFormatter.js +67 -0
  33. package/modules/FullDateFormatter.js.map +1 -0
  34. package/modules/FullDateFormatter.test.js +22 -0
  35. package/modules/FullDateFormatter.test.js.map +1 -0
  36. package/modules/TimeAgo.js +208 -107
  37. package/modules/TimeAgo.js.map +1 -1
  38. package/modules/TimeAgo.test.js +95 -8
  39. package/modules/TimeAgo.test.js.map +1 -1
  40. package/modules/steps/getStepMinTime.js +26 -19
  41. package/modules/steps/getStepMinTime.js.map +1 -1
  42. package/modules/steps/getTimeToNextUpdate.js +10 -2
  43. package/modules/steps/getTimeToNextUpdate.js.map +1 -1
  44. package/modules/style/twitter.js +3 -3
  45. package/modules/style/twitter.js.map +1 -1
  46. package/modules/style/twitter.test.js +5 -2
  47. package/modules/style/twitter.test.js.map +1 -1
  48. package/package.json +16 -11
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports["default"] = exports.FallbackDateFormatter = void 0;
7
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
8
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
9
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
10
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
11
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
12
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
13
+ var FullDateFormatter = exports["default"] = /*#__PURE__*/function () {
14
+ function FullDateFormatter(localeOrLocales) {
15
+ _classCallCheck(this, FullDateFormatter);
16
+ // See if `Intl.DateTimeFormat` is supported in the current environment.
17
+ //
18
+ // `Intl` is present in all modern web browsers and is only absent from some of the ancient ones.
19
+ //
20
+ // Babel transforms `typeof` into some "branches"
21
+ // so istanbul will show this as "branch not covered".
22
+ /* istanbul ignore next */
23
+ var isIntlAvailable = (typeof Intl === "undefined" ? "undefined" : _typeof(Intl)) === 'object';
24
+ var isIntlDateTimeFormatAvailable = isIntlAvailable && typeof Intl.DateTimeFormat === 'function';
25
+
26
+ /* istanbul ignore else */
27
+ if (isIntlDateTimeFormatAvailable) {
28
+ this.formatter = new Intl.DateTimeFormat(localeOrLocales, VERBOSE_DATE_FORMAT_OPTIONS);
29
+ } else {
30
+ this.formatter = new FallbackDateFormatter();
31
+ }
32
+ }
33
+ _createClass(FullDateFormatter, [{
34
+ key: "format",
35
+ value: function format(dateOrTimestamp) {
36
+ return this.formatter.format(getDate(dateOrTimestamp));
37
+ }
38
+ }]);
39
+ return FullDateFormatter;
40
+ }();
41
+ var FallbackDateFormatter = exports.FallbackDateFormatter = /*#__PURE__*/function () {
42
+ function FallbackDateFormatter() {
43
+ _classCallCheck(this, FallbackDateFormatter);
44
+ }
45
+ _createClass(FallbackDateFormatter, [{
46
+ key: "format",
47
+ value: function format(date) {
48
+ return String(date);
49
+ }
50
+ }]);
51
+ return FallbackDateFormatter;
52
+ }();
53
+ function getDate(dateOrTimestamp) {
54
+ if (typeof dateOrTimestamp === 'number') {
55
+ return new Date(dateOrTimestamp);
56
+ }
57
+ return dateOrTimestamp;
58
+ }
59
+
60
+ // `Intl.DateTimeFormat` options for outputting a verbose date.
61
+ // Formatted date example: "Thursday, December 20, 2012, 7:00:00 AM GMT+4"
62
+ var VERBOSE_DATE_FORMAT_OPTIONS = {
63
+ weekday: 'long',
64
+ day: 'numeric',
65
+ month: 'long',
66
+ year: 'numeric',
67
+ hour: 'numeric',
68
+ minute: '2-digit',
69
+ second: '2-digit'
70
+ // timeZoneName: 'short'
71
+ };
72
+ //# sourceMappingURL=FullDateFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FullDateFormatter.js","names":["FullDateFormatter","exports","localeOrLocales","_classCallCheck","isIntlAvailable","Intl","_typeof","isIntlDateTimeFormatAvailable","DateTimeFormat","formatter","VERBOSE_DATE_FORMAT_OPTIONS","FallbackDateFormatter","_createClass","key","value","format","dateOrTimestamp","getDate","date","String","Date","weekday","day","month","year","hour","minute","second"],"sources":["../source/FullDateFormatter.js"],"sourcesContent":["export default class FullDateFormatter {\r\n\tconstructor(localeOrLocales) {\r\n\t\t// See if `Intl.DateTimeFormat` is supported in the current environment.\r\n\t\t//\r\n\t\t// `Intl` is present in all modern web browsers and is only absent from some of the ancient ones.\r\n\t\t//\r\n\t\t// Babel transforms `typeof` into some \"branches\"\r\n\t\t// so istanbul will show this as \"branch not covered\".\r\n\t\t/* istanbul ignore next */\r\n\t\tconst isIntlAvailable = typeof Intl === 'object'\r\n\t\tconst isIntlDateTimeFormatAvailable = isIntlAvailable && typeof Intl.DateTimeFormat === 'function'\r\n\r\n\t\t/* istanbul ignore else */\r\n\t\tif (isIntlDateTimeFormatAvailable) {\r\n\t\t\tthis.formatter = new Intl.DateTimeFormat(localeOrLocales, VERBOSE_DATE_FORMAT_OPTIONS)\r\n\t\t} else {\r\n\t\t\tthis.formatter = new FallbackDateFormatter()\r\n\t\t}\r\n\t}\r\n\r\n\tformat(dateOrTimestamp) {\r\n\t\treturn this.formatter.format(getDate(dateOrTimestamp))\r\n\t}\r\n}\r\n\r\nexport class FallbackDateFormatter {\r\n\tformat(date) {\r\n\t\treturn String(date)\r\n\t}\r\n}\r\n\r\nfunction getDate(dateOrTimestamp) {\r\n\tif (typeof dateOrTimestamp === 'number') {\r\n\t\treturn new Date(dateOrTimestamp)\r\n\t}\r\n\treturn dateOrTimestamp\r\n}\r\n\r\n// `Intl.DateTimeFormat` options for outputting a verbose date.\r\n// Formatted date example: \"Thursday, December 20, 2012, 7:00:00 AM GMT+4\"\r\nconst VERBOSE_DATE_FORMAT_OPTIONS = {\r\n\tweekday: 'long',\r\n\tday: 'numeric',\r\n\tmonth: 'long',\r\n\tyear: 'numeric',\r\n\thour: 'numeric',\r\n\tminute: '2-digit',\r\n\tsecond: '2-digit'\r\n\t// timeZoneName: 'short'\r\n}"],"mappings":";;;;;;;;;;;;IAAqBA,iBAAiB,GAAAC,OAAA;EACrC,SAAAD,kBAAYE,eAAe,EAAE;IAAAC,eAAA,OAAAH,iBAAA;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAMI,eAAe,GAAG,QAAOC,IAAI,iCAAAC,OAAA,CAAJD,IAAI,OAAK,QAAQ;IAChD,IAAME,6BAA6B,GAAGH,eAAe,IAAI,OAAOC,IAAI,CAACG,cAAc,KAAK,UAAU;;IAElG;IACA,IAAID,6BAA6B,EAAE;MAClC,IAAI,CAACE,SAAS,GAAG,IAAIJ,IAAI,CAACG,cAAc,CAACN,eAAe,EAAEQ,2BAA2B,CAAC;IACvF,CAAC,MAAM;MACN,IAAI,CAACD,SAAS,GAAG,IAAIE,qBAAqB,CAAC,CAAC;IAC7C;EACD;EAACC,YAAA,CAAAZ,iBAAA;IAAAa,GAAA;IAAAC,KAAA,EAED,SAAAC,OAAOC,eAAe,EAAE;MACvB,OAAO,IAAI,CAACP,SAAS,CAACM,MAAM,CAACE,OAAO,CAACD,eAAe,CAAC,CAAC;IACvD;EAAC;EAAA,OAAAhB,iBAAA;AAAA;AAAA,IAGWW,qBAAqB,GAAAV,OAAA,CAAAU,qBAAA;EAAA,SAAAA,sBAAA;IAAAR,eAAA,OAAAQ,qBAAA;EAAA;EAAAC,YAAA,CAAAD,qBAAA;IAAAE,GAAA;IAAAC,KAAA,EACjC,SAAAC,OAAOG,IAAI,EAAE;MACZ,OAAOC,MAAM,CAACD,IAAI,CAAC;IACpB;EAAC;EAAA,OAAAP,qBAAA;AAAA;AAGF,SAASM,OAAOA,CAACD,eAAe,EAAE;EACjC,IAAI,OAAOA,eAAe,KAAK,QAAQ,EAAE;IACxC,OAAO,IAAII,IAAI,CAACJ,eAAe,CAAC;EACjC;EACA,OAAOA,eAAe;AACvB;;AAEA;AACA;AACA,IAAMN,2BAA2B,GAAG;EACnCW,OAAO,EAAE,MAAM;EACfC,GAAG,EAAE,SAAS;EACdC,KAAK,EAAE,MAAM;EACbC,IAAI,EAAE,SAAS;EACfC,IAAI,EAAE,SAAS;EACfC,MAAM,EAAE,SAAS;EACjBC,MAAM,EAAE;EACR;AACD,CAAC","ignoreList":[]}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
+ var _FullDateFormatter = _interopRequireWildcard(require("./FullDateFormatter.js"));
5
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) { "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); } return f; })(e, t); }
6
+ describe('FullDateFormatter', function () {
7
+ it('should format full date', function () {
8
+ var formatter = new _FullDateFormatter["default"]('en');
9
+ expect(formatter.format(new Date(Date.UTC(2000, 0, 1)))).to.equal('Saturday, January 1, 2000 at 3:00:00 AM');
10
+ });
11
+ it('should format full date (`locales` argument)', function () {
12
+ var formatter = new _FullDateFormatter["default"](['en', 'ru']);
13
+ expect(formatter.format(new Date(Date.UTC(2000, 0, 1)))).to.equal('Saturday, January 1, 2000 at 3:00:00 AM');
14
+ });
15
+ it('should format full date (timestamp)', function () {
16
+ var formatter = new _FullDateFormatter["default"]('en');
17
+ expect(formatter.format(Date.UTC(2000, 0, 1))).to.equal('Saturday, January 1, 2000 at 3:00:00 AM');
18
+ });
19
+ it('should fallback to non-`Intl` formatter', function () {
20
+ var formatter = new _FullDateFormatter.FallbackDateFormatter('en');
21
+ // The output depends on the user's time zone.
22
+ // Example when running in Moscow: "Sat Jan 01 2000 03:00:00 GMT+0300 (Moscow Standard Time)".
23
+ expect(formatter.format(new Date(Date.UTC(2000, 1, 1)))).to.include(':00:00 GMT');
24
+ });
25
+ });
26
+ //# sourceMappingURL=FullDateFormatter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FullDateFormatter.test.js","names":["_FullDateFormatter","_interopRequireWildcard","require","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","describe","it","formatter","FullDateFormatter","expect","format","Date","UTC","to","equal","FallbackDateFormatter","include"],"sources":["../source/FullDateFormatter.test.js"],"sourcesContent":["import FullDateFormatter, { FallbackDateFormatter } from './FullDateFormatter.js'\r\n\r\ndescribe('FullDateFormatter', () => {\r\n\tit('should format full date', () => {\r\n\t\tconst formatter = new FullDateFormatter('en')\r\n\t\texpect(\r\n\t\t\tformatter.format(new Date(Date.UTC(2000, 0, 1)))\r\n\t\t).to.equal('Saturday, January 1, 2000 at 3:00:00 AM')\r\n\t})\r\n\r\n\tit('should format full date (`locales` argument)', () => {\r\n\t\tconst formatter = new FullDateFormatter(['en', 'ru'])\r\n\t\texpect(\r\n\t\t\tformatter.format(new Date(Date.UTC(2000, 0, 1)))\r\n\t\t).to.equal('Saturday, January 1, 2000 at 3:00:00 AM')\r\n\t})\r\n\r\n\tit('should format full date (timestamp)', () => {\r\n\t\tconst formatter = new FullDateFormatter('en')\r\n\t\texpect(\r\n\t\t\tformatter.format(Date.UTC(2000, 0, 1))\r\n\t\t).to.equal('Saturday, January 1, 2000 at 3:00:00 AM')\r\n\t})\r\n\r\n\tit('should fallback to non-`Intl` formatter', () => {\r\n\t\tconst formatter = new FallbackDateFormatter('en')\r\n\t\t// The output depends on the user's time zone.\r\n\t\t// Example when running in Moscow: \"Sat Jan 01 2000 03:00:00 GMT+0300 (Moscow Standard Time)\".\r\n\t\texpect(\r\n\t\t\tformatter.format(new Date(Date.UTC(2000, 1, 1)))\r\n\t\t).to.include(':00:00 GMT')\r\n\t})\r\n})"],"mappings":";;;AAAA,IAAAA,kBAAA,GAAAC,uBAAA,CAAAC,OAAA;AAAiF,SAAAD,wBAAAE,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAJ,uBAAA,YAAAA,wBAAAE,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,mBAAAT,CAAA,iBAAAA,CAAA,gBAAAU,OAAA,CAAAV,CAAA,0BAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,cAAAM,EAAA,IAAAd,CAAA,kBAAAc,EAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAhB,CAAA,EAAAc,EAAA,OAAAP,CAAA,IAAAD,CAAA,GAAAW,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAnB,CAAA,EAAAc,EAAA,OAAAP,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAM,EAAA,EAAAP,CAAA,IAAAC,CAAA,CAAAM,EAAA,IAAAd,CAAA,CAAAc,EAAA,aAAAN,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAEjFmB,QAAQ,CAAC,mBAAmB,EAAE,YAAM;EACnCC,EAAE,CAAC,yBAAyB,EAAE,YAAM;IACnC,IAAMC,SAAS,GAAG,IAAIC,6BAAiB,CAAC,IAAI,CAAC;IAC7CC,MAAM,CACLF,SAAS,CAACG,MAAM,CAAC,IAAIC,IAAI,CAACA,IAAI,CAACC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAChD,CAAC,CAACC,EAAE,CAACC,KAAK,CAAC,yCAAyC,CAAC;EACtD,CAAC,CAAC;EAEFR,EAAE,CAAC,8CAA8C,EAAE,YAAM;IACxD,IAAMC,SAAS,GAAG,IAAIC,6BAAiB,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrDC,MAAM,CACLF,SAAS,CAACG,MAAM,CAAC,IAAIC,IAAI,CAACA,IAAI,CAACC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAChD,CAAC,CAACC,EAAE,CAACC,KAAK,CAAC,yCAAyC,CAAC;EACtD,CAAC,CAAC;EAEFR,EAAE,CAAC,qCAAqC,EAAE,YAAM;IAC/C,IAAMC,SAAS,GAAG,IAAIC,6BAAiB,CAAC,IAAI,CAAC;IAC7CC,MAAM,CACLF,SAAS,CAACG,MAAM,CAACC,IAAI,CAACC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CACtC,CAAC,CAACC,EAAE,CAACC,KAAK,CAAC,yCAAyC,CAAC;EACtD,CAAC,CAAC;EAEFR,EAAE,CAAC,yCAAyC,EAAE,YAAM;IACnD,IAAMC,SAAS,GAAG,IAAIQ,wCAAqB,CAAC,IAAI,CAAC;IACjD;IACA;IACAN,MAAM,CACLF,SAAS,CAACG,MAAM,CAAC,IAAIC,IAAI,CAACA,IAAI,CAACC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAChD,CAAC,CAACC,EAAE,CAACG,OAAO,CAAC,YAAY,CAAC;EAC3B,CAAC,CAAC;AACH,CAAC,CAAC","ignoreList":[]}
@@ -29,10 +29,6 @@ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o =
29
29
  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
30
30
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
31
31
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
32
- // Valid time units.
33
- var UNITS = ['now',
34
- // The rest are the same as in `Intl.RelativeTimeFormat`.
35
- 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'];
36
32
  var TimeAgo = exports["default"] = /*#__PURE__*/function () {
37
33
  /**
38
34
  * @param {(string|string[])} locales=[] - Preferred locales (or locale).
@@ -97,11 +93,16 @@ var TimeAgo = exports["default"] = /*#__PURE__*/function () {
97
93
  *
98
94
  * @param {boolean} [options.getTimeToNextUpdate] — Pass `true` to return `[formattedDate, timeToNextUpdate]` instead of just `formattedDate`.
99
95
  *
96
+ * @param {boolean} [options.getTimeToNextUpdateUncapped] — Pass `true` to not apply the workaround for `setTimeout()` bug. https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values
97
+ *
98
+ * @param {function} [options.refresh] — When `refresh` function is passed, it will be automatically called with a new text when it's time to refresh the label.
99
+ *
100
100
  * @return {string} The formatted relative date/time. If no eligible `step` is found, then an empty string is returned.
101
101
  */
102
102
  _createClass(TimeAgo, [{
103
103
  key: "format",
104
104
  value: function format(input, style, options) {
105
+ var _this = this;
105
106
  if (!options) {
106
107
  if (style && !isStyle(style)) {
107
108
  options = style;
@@ -123,125 +124,203 @@ var TimeAgo = exports["default"] = /*#__PURE__*/function () {
123
124
  var _this$getLabels = this.getLabels(style.flavour || style.labels),
124
125
  labels = _this$getLabels.labels,
125
126
  labelsType = _this$getLabels.labelsType;
127
+
128
+ // `round` setting could be passed as a parameter to `.format()` function
129
+ // or be configured globally for a style.
130
+ var round = options.round || style.round;
131
+
132
+ // A developer can pass a custom `now`, e.g. for testing purposes.
126
133
  var now;
127
- // Can pass a custom `now`, e.g. for testing purposes.
128
- //
134
+ var nowRealWhenCalled = Date.now();
135
+ // (deprecated)
129
136
  // Legacy way was passing `now` in `style`.
130
- // That way is deprecated.
131
137
  if (style.now !== undefined) {
132
138
  now = style.now;
133
139
  }
134
- // The new way is passing `now` option to `.format()`.
140
+ // One could pass `now` option to `.format()`.
135
141
  if (now === undefined && options.now !== undefined) {
136
142
  now = options.now;
137
143
  }
144
+ // The default `now` is `Date.now()`.
138
145
  if (now === undefined) {
139
- now = Date.now();
146
+ now = nowRealWhenCalled;
140
147
  }
141
-
142
- // how much time has passed (in seconds)
143
- var secondsPassed = (now - timestamp) / 1000; // in seconds
144
-
145
- var future = options.future || secondsPassed < 0;
146
- var nowLabel = getNowLabel(labels, (0, _LocaleDataStore.getLocaleData)(this.locale).now, (0, _LocaleDataStore.getLocaleData)(this.locale)["long"], future);
147
-
148
- // `custom` – A function of `{ elapsed, time, date, now, locale }`.
149
- //
150
- // Looks like `custom` function is deprecated and will be removed
151
- // in the next major version.
152
- //
153
- // If this function returns a value, then the `.format()` call will return that value.
154
- // Otherwise the relative date/time is formatted as usual.
155
- // This feature is currently not used anywhere and is here
156
- // just for providing the ultimate customization point
157
- // in case anyone would ever need that. Prefer using
158
- // `steps[step].format(value, locale)` instead.
159
- //
160
- if (style.custom) {
161
- var custom = style.custom({
162
- now: now,
163
- date: new Date(timestamp),
164
- time: timestamp,
165
- elapsed: secondsPassed,
166
- locale: this.locale
167
- });
168
- if (custom !== undefined) {
169
- // Won't return `timeToNextUpdate` here
170
- // because `custom()` seems deprecated.
171
- return custom;
148
+ var getTextAndTextRefreshDelayGetterFunctions = function getTextAndTextRefreshDelayGetterFunctions(_ref2) {
149
+ var now = _ref2.now;
150
+ // how much time has passed (in seconds)
151
+ var secondsPassed = (now - timestamp) / 1000; // in seconds
152
+
153
+ var future = options.future || secondsPassed < 0;
154
+ var nowLabel = getNowLabel(labels, (0, _LocaleDataStore.getLocaleData)(_this.locale).now, (0, _LocaleDataStore.getLocaleData)(_this.locale)["long"], future);
155
+
156
+ // (deprecated)
157
+ //
158
+ // `custom` A function of `{ elapsed, time, date, now, locale }`.
159
+ //
160
+ // If this function returns a value, then the `.format()` call will return that value.
161
+ // Otherwise the relative date/time is formatted as usual.
162
+ // This feature is currently not used anywhere and is here
163
+ // just for providing the ultimate customization point
164
+ // in case anyone would ever need that. Prefer using
165
+ // `steps[step].format(value, locale)` instead.
166
+ //
167
+ if (style.custom) {
168
+ var text = style.custom({
169
+ now: now,
170
+ date: new Date(timestamp),
171
+ time: timestamp,
172
+ elapsed: secondsPassed,
173
+ locale: _this.locale
174
+ });
175
+ if (text !== undefined) {
176
+ // Won't return `timeToNextUpdate` here
177
+ // because `custom()` seems deprecated.
178
+ return {
179
+ getText: function getText() {
180
+ return text;
181
+ },
182
+ getTextRefreshDelay: function getTextRefreshDelay() {
183
+ throw new Error('`getTimeToNextUpdate: true` feature is not supported by legacy "styles" that have a `custom` function');
184
+ }
185
+ };
186
+ }
172
187
  }
173
- }
174
-
175
- // Get the list of available time interval units.
176
- var units = getTimeIntervalMeasurementUnits(
177
- // Controlling `style.steps` through `style.units` seems to be deprecated:
178
- // create a new custom `style` instead.
179
- style.units, labels, nowLabel);
180
188
 
181
- // // If no available time unit is suitable, just output an empty string.
182
- // if (units.length === 0) {
183
- // console.error(`None of the "${units.join(', ')}" time units have been found in "${labelsType}" labels for "${this.locale}" locale.`)
184
- // return ''
185
- // }
186
-
187
- var round = options.round || style.round;
189
+ // Get the list of available time interval units.
190
+ var units = getTimeIntervalMeasurementUnits(
191
+ // Controlling `style.steps` through `style.units` seems to be deprecated:
192
+ // create a new custom `style` instead.
193
+ style.units, labels, nowLabel);
194
+
195
+ // Choose the appropriate time measurement unit
196
+ // and get the corresponding rounded time amount.
197
+ var _getStep = (0, _getStep3["default"])(
198
+ // "gradation" is a legacy name for "steps".
199
+ // For historical reasons, "approximate" steps are used by default.
200
+ // In the next major version, there'll be no default for `steps`.
201
+ style.gradation || style.steps || _roundMinute["default"].steps, secondsPassed, {
202
+ now: now,
203
+ units: units,
204
+ round: round,
205
+ future: future,
206
+ getNextStep: true
207
+ }),
208
+ _getStep2 = _slicedToArray(_getStep, 3),
209
+ prevStep = _getStep2[0],
210
+ step = _getStep2[1],
211
+ nextStep = _getStep2[2];
212
+ var getText = function getText() {
213
+ return _this.formatDateForStep(timestamp, step, secondsPassed, {
214
+ labels: labels,
215
+ labelsType: labelsType,
216
+ nowLabel: nowLabel,
217
+ now: now,
218
+ future: future,
219
+ round: round
220
+ }) || '';
221
+ };
222
+
223
+ // Returns the time (in milliseconds) after which the formatted date label should be refreshed.
224
+ //
225
+ // It will return `undefined` for a custom style
226
+ // that doesn't meet the minimum requirements for this feature.
227
+ // See the README for more details.
228
+ //
229
+ var getTextRefreshDelay = function getTextRefreshDelay() {
230
+ var timeToNextUpdate = (0, _getTimeToNextUpdate["default"])(timestamp, step, {
231
+ nextStep: nextStep,
232
+ prevStep: prevStep,
233
+ now: now,
234
+ future: future,
235
+ round: round
236
+ });
188
237
 
189
- // Choose the appropriate time measurement unit
190
- // and get the corresponding rounded time amount.
191
- var _getStep = (0, _getStep3["default"])(
192
- // "gradation" is a legacy name for "steps".
193
- // For historical reasons, "approximate" steps are used by default.
194
- // In the next major version, there'll be no default for `steps`.
195
- style.gradation || style.steps || _roundMinute["default"].steps, secondsPassed, {
196
- now: now,
197
- units: units,
198
- round: round,
199
- future: future,
200
- getNextStep: true
238
+ // `timeToNextUpdate` could be `undefined` for a custom style
239
+ // that doesn't meet the minimum requirements for this feature.
240
+ // See the README for more details.
241
+ if (typeof timeToNextUpdate === 'number') {
242
+ // `setTimeout()` function has a bug when it fires immediately
243
+ // when the delay is longer than about `24.85` days.
244
+ // https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values
245
+ //
246
+ // To not burden the end user of this library with manually working around that bug,
247
+ // this library automatically caps the returned delay to a maximum value that
248
+ // still works with `setTimeout()` and doesn't break it.
249
+ //
250
+ // The end user of this library could still opt out of this auto-workaround feature
251
+ // by passing a `getTimeToNextUpdateUncapped: true` option.
252
+ //
253
+ if (options.getTimeToNextUpdateUncapped) {
254
+ return timeToNextUpdate;
255
+ }
256
+ return getSafeTimeoutDelay(timeToNextUpdate);
257
+ }
258
+ };
259
+ return {
260
+ getText: getText,
261
+ getTextRefreshDelay: getTextRefreshDelay
262
+ };
263
+ };
264
+ var _getTextAndTextRefres = getTextAndTextRefreshDelayGetterFunctions({
265
+ now: now
201
266
  }),
202
- _getStep2 = _slicedToArray(_getStep, 3),
203
- prevStep = _getStep2[0],
204
- step = _getStep2[1],
205
- nextStep = _getStep2[2];
206
- var formattedDate = this.formatDateForStep(timestamp, step, secondsPassed, {
207
- labels: labels,
208
- labelsType: labelsType,
209
- nowLabel: nowLabel,
210
- now: now,
211
- future: future,
212
- round: round
213
- }) || '';
267
+ getText = _getTextAndTextRefres.getText,
268
+ getTextRefreshDelay = _getTextAndTextRefres.getTextRefreshDelay;
214
269
  if (options.getTimeToNextUpdate) {
215
- var timeToNextUpdate = (0, _getTimeToNextUpdate["default"])(timestamp, step, {
216
- nextStep: nextStep,
217
- prevStep: prevStep,
218
- now: now,
219
- future: future,
220
- round: round
221
- });
222
- return [formattedDate, timeToNextUpdate];
270
+ return [getText(), getTextRefreshDelay()];
223
271
  }
224
- return formattedDate;
272
+ if (options.refresh) {
273
+ // `getTextRefreshDelay()` will return `undefined` for a custom style
274
+ // that doesn't meet the minimum requirements for this feature.
275
+ // See the README for more details.
276
+ //
277
+ // This is a "sensible default" interval for refreshing time labels
278
+ // that use such custom style.
279
+ //
280
+ var defaultRefreshInterval = 60 * 1000;
281
+
282
+ // If `refresh` function was passed, schedule it to be called when the time comes,
283
+ // after which schedule the next refresh.
284
+ var refreshTimer;
285
+ var scheduleRefresh = function scheduleRefresh() {
286
+ var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultRefreshInterval;
287
+ refreshTimer = setTimeoutSafe(function () {
288
+ var _getTextAndTextRefres2 = getTextAndTextRefreshDelayGetterFunctions({
289
+ now: now + (Date.now() - nowRealWhenCalled)
290
+ }),
291
+ getText = _getTextAndTextRefres2.getText,
292
+ getTextRefreshDelay = _getTextAndTextRefres2.getTextRefreshDelay;
293
+ options.refresh(getText());
294
+ scheduleRefresh(getTextRefreshDelay());
295
+ }, delay);
296
+ };
297
+ scheduleRefresh(getTextRefreshDelay());
298
+ var stopRefreshing = function stopRefreshing() {
299
+ clearTimeout(refreshTimer);
300
+ };
301
+ return [getText(), stopRefreshing];
302
+ }
303
+ return getText();
225
304
  }
226
305
  }, {
227
306
  key: "formatDateForStep",
228
- value: function formatDateForStep(timestamp, step, secondsPassed, _ref2) {
229
- var _this = this;
230
- var labels = _ref2.labels,
231
- labelsType = _ref2.labelsType,
232
- nowLabel = _ref2.nowLabel,
233
- now = _ref2.now,
234
- future = _ref2.future,
235
- round = _ref2.round;
307
+ value: function formatDateForStep(timestamp, step, secondsPassed, _ref3) {
308
+ var _this2 = this;
309
+ var labels = _ref3.labels,
310
+ labelsType = _ref3.labelsType,
311
+ nowLabel = _ref3.nowLabel,
312
+ now = _ref3.now,
313
+ future = _ref3.future,
314
+ round = _ref3.round;
236
315
  // If no step matches, then output an empty string.
237
316
  if (!step) {
238
317
  return;
239
318
  }
240
319
  if (step.format) {
241
320
  return step.format(timestamp, this.locale, {
242
- formatAs: function formatAs(unit, value) {
321
+ formatAs: function formatAs(unit, amount) {
243
322
  // Mimicks `Intl.RelativeTimeFormat.format()`.
244
- return _this.formatValue(value, unit, {
323
+ return _this2.formatValue(amount, unit, {
245
324
  labels: labels,
246
325
  future: future
247
326
  });
@@ -322,9 +401,9 @@ var TimeAgo = exports["default"] = /*#__PURE__*/function () {
322
401
  */
323
402
  }, {
324
403
  key: "formatValue",
325
- value: function formatValue(value, unit, _ref3) {
326
- var labels = _ref3.labels,
327
- future = _ref3.future;
404
+ value: function formatValue(value, unit, _ref4) {
405
+ var labels = _ref4.labels,
406
+ future = _ref4.future;
328
407
  return this.getFormattingRule(labels, unit, value, {
329
408
  future: future
330
409
  }).replace('{0}', this.formatNumber(Math.abs(value)));
@@ -343,8 +422,8 @@ var TimeAgo = exports["default"] = /*#__PURE__*/function () {
343
422
  */
344
423
  }, {
345
424
  key: "getFormattingRule",
346
- value: function getFormattingRule(formattingRules, unit, value, _ref4) {
347
- var future = _ref4.future;
425
+ value: function getFormattingRule(formattingRules, unit, value, _ref5) {
426
+ var future = _ref5.future;
348
427
  // Passing the language is required in order to
349
428
  // be able to correctly classify the `value` as a number.
350
429
  var locale = this.locale;
@@ -477,7 +556,7 @@ TimeAgo.getDefaultLocale = function () {
477
556
  * @param {string} locale
478
557
  */
479
558
  TimeAgo.setDefaultLocale = function (locale) {
480
- return defaultLocale = locale;
559
+ defaultLocale = locale;
481
560
  };
482
561
 
483
562
  /**
@@ -485,14 +564,19 @@ TimeAgo.setDefaultLocale = function (locale) {
485
564
  * @param {Object} localeData
486
565
  */
487
566
  TimeAgo.addDefaultLocale = function (localeData) {
567
+ // Warn the user if they've previously already added a default locale (a different one).
488
568
  if (defaultLocaleHasBeenSpecified) {
489
- return console.error('[javascript-time-ago] `TimeAgo.addDefaultLocale()` can only be called once. To add other locales, use `TimeAgo.addLocale()`.');
569
+ if (TimeAgo.getDefaultLocale() !== localeData.locale) {
570
+ console.warn("[javascript-time-ago] You're adding \"".concat(localeData.locale, "\" as the default locale but you have already added \"").concat(TimeAgo.getDefaultLocale(), "\" as the default locale. \"").concat(localeData.locale, "\" is the default locale now."));
571
+ }
490
572
  }
491
573
  defaultLocaleHasBeenSpecified = true;
492
- TimeAgo.setDefaultLocale(localeData.locale);
574
+
575
+ // `addDefaultLocale()` is just a shortcut to `addLocale()` + `setDefaultLocale()`.
493
576
  TimeAgo.addLocale(localeData);
577
+ TimeAgo.setDefaultLocale(localeData.locale);
494
578
  };
495
- var defaultLocaleHasBeenSpecified;
579
+ var defaultLocaleHasBeenSpecified = false;
496
580
 
497
581
  /**
498
582
  * Adds locale data for a specific locale.
@@ -599,4 +683,22 @@ function getNowLabel(labels, nowLabels, longLabels, future) {
599
683
  function isStyle(variable) {
600
684
  return typeof variable === 'string' || (0, _isStyleObject["default"])(variable);
601
685
  }
686
+
687
+ // `setTimeout()` function has a bug when it fires immediately
688
+ // when the delay is longer than about `24.85` days.
689
+ // https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values
690
+ //
691
+ // Since `renderLabel()` function uses `setTimeout()` for recursion,
692
+ // that would mean infinite recursion.
693
+ //
694
+ // `setTimeoutSafe()` function works around that bug
695
+ // by capping the delay at the maximum allowed value.
696
+ //
697
+ function setTimeoutSafe(func, delay) {
698
+ return setTimeout(func, getSafeTimeoutDelay(delay));
699
+ }
700
+ function getSafeTimeoutDelay(delay) {
701
+ return Math.min(delay, SET_TIMEOUT_MAX_SAFE_DELAY);
702
+ }
703
+ var SET_TIMEOUT_MAX_SAFE_DELAY = 2147483647;
602
704
  //# sourceMappingURL=TimeAgo.js.map