UNPKG

javascript-time-ago

Version:

Localized relative date/time formatting

701 lines (658 loc) 30.2 kB
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); } function _createForOfIteratorHelperLoose(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (t) return (t = t.call(r)).next.bind(t); if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var o = 0; return function () { return o >= r.length ? { done: !0 } : { done: !1, value: r[o++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) { n[e] = r[e]; } return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) { ; } } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 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); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 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); } import RelativeTimeFormatPolyfill from 'relative-time-format'; import Cache from './cache.js'; import chooseLocale from './locale.js'; import isStyleObject from './isStyleObject.js'; import getStep from './steps/getStep.js'; import getStepDenominator from './steps/getStepDenominator.js'; import getTimeToNextUpdate from './steps/getTimeToNextUpdate.js'; import { addLocaleData, getLocaleData } from './LocaleDataStore.js'; import defaultStyle from './style/roundMinute.js'; import getStyleByName from './style/getStyleByName.js'; import { getRoundFunction } from './round.js'; var TimeAgo = /*#__PURE__*/function () { /** * @param {(string|string[])} locales=[] - Preferred locales (or locale). * @param {boolean} [polyfill] — Pass `false` to use native `Intl.RelativeTimeFormat` and `Intl.PluralRules` instead of the polyfills. */ function TimeAgo() { var locales = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, polyfill = _ref.polyfill; _classCallCheck(this, TimeAgo); // Convert `locales` to an array. if (typeof locales === 'string') { locales = [locales]; } // Choose the most appropriate locale // from the list of `locales` added by the user. // For example, new TimeAgo("en-US") -> "en". this.locale = chooseLocale(locales.concat(TimeAgo.getDefaultLocale()), getLocaleData); if (typeof Intl !== 'undefined') { // Use `Intl.NumberFormat` for formatting numbers (when available). if (Intl.NumberFormat) { this.numberFormat = new Intl.NumberFormat(this.locale); } } // Some people have requested the ability to use native // `Intl.RelativeTimeFormat` and `Intl.PluralRules` // instead of the polyfills. // https://github.com/catamphetamine/javascript-time-ago/issues/21 if (polyfill === false) { this.IntlRelativeTimeFormat = Intl.RelativeTimeFormat; this.IntlPluralRules = Intl.PluralRules; } else { this.IntlRelativeTimeFormat = RelativeTimeFormatPolyfill; this.IntlPluralRules = RelativeTimeFormatPolyfill.PluralRules; } // Cache `Intl.RelativeTimeFormat` instance. this.relativeTimeFormatCache = new Cache(); // Cache `Intl.PluralRules` instance. this.pluralRulesCache = new Cache(); } /** * Formats relative date/time. * * @param {(number|Date)} input — A `Date` or a javascript timestamp. * * @param {(string|object)} style — Date/time formatting style. Either one of the built-in style names or a "custom" style definition object having `steps: object[]` and `labels: string[]`. * * @param {number} [options.now] - Sets the current date timestamp. * * @param {boolean} [options.future] — Tells how to format value `0`: * as "future" (`true`) or "past" (`false`). * Is `false` by default, but should have been `true` actually, * in order to correspond to `Intl.RelativeTimeFormat` * that uses `future` formatting for `0` unless `-0` is passed. * * @param {string} [options.round] — Rounding method. Overrides the style's one. * * @param {boolean} [options.getTimeToNextUpdate] — Pass `true` to return `[formattedDate, timeToNextUpdate]` instead of just `formattedDate`. * * @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 * * @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. * * @return {string} The formatted relative date/time. If no eligible `step` is found, then an empty string is returned. */ _createClass(TimeAgo, [{ key: "format", value: function format(input, style, options) { var _this = this; if (!options) { if (style && !isStyle(style)) { options = style; style = undefined; } else { options = {}; } } if (!style) { style = defaultStyle; } if (typeof style === 'string') { style = getStyleByName(style); } var timestamp = getTimestamp(input); // Get locale messages for this type of labels. // "flavour" is a legacy name for "labels". var _this$getLabels = this.getLabels(style.flavour || style.labels), labels = _this$getLabels.labels, labelsType = _this$getLabels.labelsType; // `round` setting could be passed as a parameter to `.format()` function // or be configured globally for a style. var round = options.round || style.round; // A developer can pass a custom `now`, e.g. for testing purposes. var now; var nowRealWhenCalled = Date.now(); // (deprecated) // Legacy way was passing `now` in `style`. if (style.now !== undefined) { now = style.now; } // One could pass `now` option to `.format()`. if (now === undefined && options.now !== undefined) { now = options.now; } // The default `now` is `Date.now()`. if (now === undefined) { now = nowRealWhenCalled; } var getTextAndTextRefreshDelayGetterFunctions = function getTextAndTextRefreshDelayGetterFunctions(_ref2) { var now = _ref2.now; // how much time has passed (in seconds) var secondsPassed = (now - timestamp) / 1000; // in seconds var future = options.future || secondsPassed < 0; var nowLabel = getNowLabel(labels, getLocaleData(_this.locale).now, getLocaleData(_this.locale)["long"], future); // (deprecated) // // `custom` – A function of `{ elapsed, time, date, now, locale }`. // // If this function returns a value, then the `.format()` call will return that value. // Otherwise the relative date/time is formatted as usual. // This feature is currently not used anywhere and is here // just for providing the ultimate customization point // in case anyone would ever need that. Prefer using // `steps[step].format(value, locale)` instead. // if (style.custom) { var text = style.custom({ now: now, date: new Date(timestamp), time: timestamp, elapsed: secondsPassed, locale: _this.locale }); if (text !== undefined) { // Won't return `timeToNextUpdate` here // because `custom()` seems deprecated. return { getText: function getText() { return text; }, getTextRefreshDelay: function getTextRefreshDelay() { throw new Error('`getTimeToNextUpdate: true` feature is not supported by legacy "styles" that have a `custom` function'); } }; } } // Get the list of available time interval units. var units = getTimeIntervalMeasurementUnits( // Controlling `style.steps` through `style.units` seems to be deprecated: // create a new custom `style` instead. style.units, labels, nowLabel); // Choose the appropriate time measurement unit // and get the corresponding rounded time amount. var _getStep = getStep( // "gradation" is a legacy name for "steps". // For historical reasons, "approximate" steps are used by default. // In the next major version, there'll be no default for `steps`. style.gradation || style.steps || defaultStyle.steps, secondsPassed, { now: now, units: units, round: round, future: future, getNextStep: true }), _getStep2 = _slicedToArray(_getStep, 3), prevStep = _getStep2[0], step = _getStep2[1], nextStep = _getStep2[2]; var getText = function getText() { return _this.formatDateForStep(timestamp, step, secondsPassed, { labels: labels, labelsType: labelsType, nowLabel: nowLabel, now: now, future: future, round: round }) || ''; }; // Returns the time (in milliseconds) after which the formatted date label should be refreshed. // // It will return `undefined` for a custom style // that doesn't meet the minimum requirements for this feature. // See the README for more details. // var getTextRefreshDelay = function getTextRefreshDelay() { var timeToNextUpdate = getTimeToNextUpdate(timestamp, step, { nextStep: nextStep, prevStep: prevStep, now: now, future: future, round: round }); // `timeToNextUpdate` could be `undefined` for a custom style // that doesn't meet the minimum requirements for this feature. // See the README for more details. if (typeof timeToNextUpdate === 'number') { // `setTimeout()` function has a bug when it fires immediately // when the delay is longer than about `24.85` days. // https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values // // In order to not burden the end users of this library with manually working around that bug, // this library automatically caps the returned delay to a maximum value of about `24.85` days // which still works correctly with `setTimeout()` function and doesn't break it. // // The end user of this library could still disable this automatic workaround // by passing a `getTimeToNextUpdateUncapped: true` parameter. // In that case, it will return the original non-modified uncapped delay // which can be longer than `24.85` days and should be manually capped // by the developer if it's going to be used in a `setTimeout()` call. // if (options.getTimeToNextUpdateUncapped) { return timeToNextUpdate; } return getSafeTimeoutDelay(timeToNextUpdate); } }; return { getText: getText, getTextRefreshDelay: getTextRefreshDelay }; }; var _getTextAndTextRefres = getTextAndTextRefreshDelayGetterFunctions({ now: now }), getText = _getTextAndTextRefres.getText, getTextRefreshDelay = _getTextAndTextRefres.getTextRefreshDelay; if (options.getTimeToNextUpdate) { return [getText(), getTextRefreshDelay()]; } if (options.refresh) { // `getTextRefreshDelay()` will return `undefined` for a custom style // that doesn't meet the minimum requirements for this feature. // See the README for more details. // // This is a "sensible default" interval for refreshing time labels // that use such custom style. // var defaultRefreshInterval = 60 * 1000; // If `refresh` function was passed, schedule it to be called when the time comes, // after which schedule the next refresh. var refreshTimer; var scheduleRefresh = function scheduleRefresh() { var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultRefreshInterval; refreshTimer = setTimeoutSafe(function () { var _getTextAndTextRefres2 = getTextAndTextRefreshDelayGetterFunctions({ now: now + (Date.now() - nowRealWhenCalled) }), getText = _getTextAndTextRefres2.getText, getTextRefreshDelay = _getTextAndTextRefres2.getTextRefreshDelay; options.refresh(getText()); scheduleRefresh(getTextRefreshDelay()); }, delay); }; scheduleRefresh(getTextRefreshDelay()); var stopRefreshing = function stopRefreshing() { clearTimeout(refreshTimer); }; return [getText(), stopRefreshing]; } return getText(); } }, { key: "formatDateForStep", value: function formatDateForStep(timestamp, step, secondsPassed, _ref3) { var _this2 = this; var labels = _ref3.labels, labelsType = _ref3.labelsType, nowLabel = _ref3.nowLabel, now = _ref3.now, future = _ref3.future, round = _ref3.round; // If no step matches, then output an empty string. if (!step) { return; } if (step.format) { return step.format(timestamp, this.locale, { formatAs: function formatAs(unit, amount) { // Mimicks `Intl.RelativeTimeFormat.format()`. return _this2.formatValue(amount, unit, { labels: labels, future: future }); }, now: now, future: future }); } // "unit" is now called "formatAs". var unit = step.unit || step.formatAs; if (!unit) { throw new Error("[javascript-time-ago] Each step must define either `formatAs` or `format()`. Step: ".concat(JSON.stringify(step))); } // `Intl.RelativeTimeFormat` doesn't operate in "now" units. // Therefore, threat "now" as a special case. if (unit === 'now') { return nowLabel; } // Amount in units. var amount = Math.abs(secondsPassed) / getStepDenominator(step); // Apply granularity to the time amount // (and fallback to the previous step // if the first level of granularity // isn't met by this amount) // // `granularity` — (advanced) Time interval value "granularity". // For example, it could be set to `5` for minutes to allow only 5-minute increments // when formatting time intervals: `0 minutes`, `5 minutes`, `10 minutes`, etc. // Perhaps this feature will be removed because there seem to be no use cases // of it in the real world. // if (step.granularity) { // Recalculate the amount of seconds passed based on granularity amount = getRoundFunction(round)(amount / step.granularity) * step.granularity; } var valueForFormatting = -1 * Math.sign(secondsPassed) * getRoundFunction(round)(amount); // By default, this library formats a `0` in "past" mode, // unless `future: true` option is passed. // This is different to `relative-time-format`'s behavior // which formats a `0` in "future" mode by default, unless it's a `-0`. // So, convert `0` to `-0` if `future: true` option wasn't passed. // `=== 0` matches both `0` and `-0`. if (valueForFormatting === 0) { if (future) { valueForFormatting = 0; } else { valueForFormatting = -0; } } switch (labelsType) { case 'long': case 'short': case 'narrow': // Format the amount using `Intl.RelativeTimeFormat`. return this.getFormatter(labelsType).format(valueForFormatting, unit); default: // Format the amount. // (mimicks `Intl.RelativeTimeFormat` behavior for other time label styles) return this.formatValue(valueForFormatting, unit, { labels: labels, future: future }); } } /** * Mimicks what `Intl.RelativeTimeFormat` does for additional locale styles. * @param {number} value * @param {string} unit * @param {object} options.labels — Relative time labels. * @param {boolean} [options.future] — Tells how to format value `0`: as "future" (`true`) or "past" (`false`). Is `false` by default, but should have been `true` actually. * @return {string} */ }, { key: "formatValue", value: function formatValue(value, unit, _ref4) { var labels = _ref4.labels, future = _ref4.future; return this.getFormattingRule(labels, unit, value, { future: future }).replace('{0}', this.formatNumber(Math.abs(value))); } /** * Returns formatting rule for `value` in `units` (either in past or in future). * @param {object} formattingRules — Relative time labels for different units. * @param {string} unit - Time interval measurement unit. * @param {number} value - Time interval value. * @param {boolean} [options.future] — Tells how to format value `0`: as "future" (`true`) or "past" (`false`). Is `false` by default. * @return {string} * @example * // Returns "{0} days ago" * getFormattingRule(en.long, "day", -2, 'en') */ }, { key: "getFormattingRule", value: function getFormattingRule(formattingRules, unit, value, _ref5) { var future = _ref5.future; // Passing the language is required in order to // be able to correctly classify the `value` as a number. var locale = this.locale; formattingRules = formattingRules[unit]; // Check for a special "compacted" rules case: // if formatting rules are the same for "past" and "future", // and also for all possible `value`s, then those rules are // stored as a single string. if (typeof formattingRules === 'string') { return formattingRules; } // Choose either "past" or "future" based on time `value` sign. // If "past" is same as "future" then they're stored as "other". // If there's only "other" then it's being collapsed. var pastOrFuture = value === 0 ? future ? 'future' : 'past' : value < 0 ? 'past' : 'future'; var quantifierRules = formattingRules[pastOrFuture] || formattingRules; // Bundle size optimization technique. if (typeof quantifierRules === 'string') { return quantifierRules; } // Quantify `value`. var quantifier = this.getPluralRules().select(Math.abs(value)); // "other" rule is supposed to always be present. // If only "other" rule is present then "rules" is not an object and is a string. return quantifierRules[quantifier] || quantifierRules.other; } /** * Formats a number into a string. * Uses `Intl.NumberFormat` when available. * @param {number} number * @return {string} */ }, { key: "formatNumber", value: function formatNumber(number) { return this.numberFormat ? this.numberFormat.format(number) : String(number); } /** * Returns an `Intl.RelativeTimeFormat` for a given `labelsType`. * @param {string} labelsType * @return {object} `Intl.RelativeTimeFormat` instance */ }, { key: "getFormatter", value: function getFormatter(labelsType) { // `Intl.RelativeTimeFormat` instance creation is (hypothetically) assumed // a lengthy operation so the instances are cached and reused. return this.relativeTimeFormatCache.get(this.locale, labelsType) || this.relativeTimeFormatCache.put(this.locale, labelsType, new this.IntlRelativeTimeFormat(this.locale, { style: labelsType })); } /** * Returns an `Intl.PluralRules` instance. * @return {object} `Intl.PluralRules` instance */ }, { key: "getPluralRules", value: function getPluralRules() { // `Intl.PluralRules` instance creation is (hypothetically) assumed // a lengthy operation so the instances are cached and reused. return this.pluralRulesCache.get(this.locale) || this.pluralRulesCache.put(this.locale, new this.IntlPluralRules(this.locale)); } /** * Gets localized labels for this type of labels. * * @param {(string|string[])} labelsType - Relative date/time labels type. * If it's an array then all label types are tried * until a suitable one is found. * * @returns {Object} Returns an object of shape { labelsType, labels } */ }, { key: "getLabels", value: function getLabels() { var labelsType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; // Convert `labels` to an array. if (typeof labelsType === 'string') { labelsType = [labelsType]; } // Supports legacy "tiny" and "mini-time" label styles. labelsType = labelsType.map(function (labelsType) { switch (labelsType) { case 'tiny': case 'mini-time': return 'mini'; default: return labelsType; } }); // "long" labels type is the default one. // (it's always present for all languages) labelsType = labelsType.concat('long'); // Find a suitable labels type. var localeData = getLocaleData(this.locale); for (var _iterator = _createForOfIteratorHelperLoose(labelsType), _step; !(_step = _iterator()).done;) { var _labelsType = _step.value; if (localeData[_labelsType]) { return { labelsType: _labelsType, labels: localeData[_labelsType] }; } } } }]); return TimeAgo; }(); /** * Default locale global variable. */ export { TimeAgo as default }; var defaultLocale = 'en'; /** * Gets default locale. * @return {string} locale */ TimeAgo.getDefaultLocale = function () { return defaultLocale; }; /** * Sets default locale. * @param {string} locale */ TimeAgo.setDefaultLocale = function (locale) { defaultLocale = locale; }; /** * Adds locale data for a specific locale and marks the locale as default. * @param {Object} localeData */ TimeAgo.addDefaultLocale = function (localeData) { // Warn the user if they've previously already added a default locale (a different one). if (defaultLocaleHasBeenSpecified) { if (TimeAgo.getDefaultLocale() !== localeData.locale) { 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.")); } } defaultLocaleHasBeenSpecified = true; // `addDefaultLocale()` is just a shortcut to `addLocale()` + `setDefaultLocale()`. TimeAgo.addLocale(localeData); TimeAgo.setDefaultLocale(localeData.locale); }; var defaultLocaleHasBeenSpecified = false; /** * Adds locale data for a specific locale. * @param {Object} localeData */ TimeAgo.addLocale = function (localeData) { addLocaleData(localeData); RelativeTimeFormatPolyfill.addLocale(localeData); }; /** * (legacy alias) * Adds locale data for a specific locale. * @param {Object} localeData * @deprecated */ TimeAgo.locale = TimeAgo.addLocale; /** * Adds custom labels to locale data. * @param {string} locale * @param {string} name * @param {object} labels */ TimeAgo.addLabels = function (locale, name, labels) { var localeData = getLocaleData(locale); if (!localeData) { addLocaleData({ locale: locale }); localeData = getLocaleData(locale); // throw new Error(`[javascript-time-ago] No data for locale "${locale}"`) } localeData[name] = labels; }; // Normalizes `.format()` `time` argument. function getTimestamp(input) { if (input.constructor === Date || isMockedDate(input)) { return input.getTime(); } if (typeof input === 'number') { return input; } // For some weird reason istanbul doesn't see this `throw` covered. /* istanbul ignore next */ throw new Error("Unsupported relative time formatter input: ".concat(_typeof(input), ", ").concat(input)); } // During testing via some testing libraries `Date`s aren't actually `Date`s. // https://github.com/catamphetamine/javascript-time-ago/issues/22 function isMockedDate(object) { return _typeof(object) === 'object' && typeof object.getTime === 'function'; } // Get available time interval measurement units. function getTimeIntervalMeasurementUnits(allowedUnits, labels, nowLabel) { // Get all time interval measurement units that're available // in locale data for a given time labels style. var units = Object.keys(labels); // `now` unit is handled separately and is shipped in its own `now.json` file. // `now.json` isn't present for all locales, so it could be substituted with // ".second.current". // Add `now` unit if it's available in locale data. if (nowLabel) { units.push('now'); } // If only a specific set of available time measurement units can be used // then only those units are allowed (if they're present in locale data). if (allowedUnits) { units = allowedUnits.filter(function (unit) { return unit === 'now' || units.indexOf(unit) >= 0; }); } return units; } function getNowLabel(labels, nowLabels, longLabels, future) { var nowLabel = labels.now || nowLabels && nowLabels.now; // Specific "now" message form extended locale data (if present). if (nowLabel) { // Bundle size optimization technique. if (typeof nowLabel === 'string') { return nowLabel; } // Not handling `value === 0` as `localeData.now.current` here // because it wouldn't make sense: "now" is a moment, // so one can't possibly differentiate between a // "previous" moment, a "current" moment and a "next moment". // It can only be differentiated between "past" and "future". if (future) { return nowLabel.future; } else { return nowLabel.past; } } // Use ".second.current" as "now" message. if (longLabels && longLabels.second && longLabels.second.current) { return longLabels.second.current; } } function isStyle(variable) { return typeof variable === 'string' || isStyleObject(variable); } // `setTimeout()` function has a bug when it fires immediately // when the delay is longer than about `24.85` days. // https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values // // Since `renderLabel()` function uses `setTimeout()` for recursion, // that would mean infinite recursion. // // `setTimeoutSafe()` function works around that bug // by capping the delay at the maximum allowed value. // function setTimeoutSafe(func, delay) { return setTimeout(func, getSafeTimeoutDelay(delay)); } function getSafeTimeoutDelay(delay) { return Math.min(delay, SET_TIMEOUT_MAX_SAFE_DELAY); } var SET_TIMEOUT_MAX_SAFE_DELAY = 2147483647; //# sourceMappingURL=TimeAgo.js.map