timeago.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /**
  2. * 作者: wujiawei0926@yeah.net
  3. * 原作者: https://github.com/hustcc/timeago.js
  4. * Copyright (c) 2016 hustcc
  5. * License: MIT
  6. * Version: v3.0.0
  7. **/
  8. layui.define([], function (exports) {
  9. var $ = layui.jquery;
  10. var indexMapEn = 'second_minute_hour_day_week_month_year'.split('_'),
  11. indexMapZh = '秒_分钟_小时_天_周_月_年'.split('_'),
  12. // build-in locales: en & zh_CN
  13. locales = {
  14. 'en': function (number, index) {
  15. if (index === 0) return ['just now', 'right now'];
  16. var unit = indexMapEn[parseInt(index / 2)];
  17. if (number > 1) unit += 's';
  18. return [number + ' ' + unit + ' ago', 'in ' + number + ' ' + unit];
  19. },
  20. 'zh_CN': function (number, index) {
  21. if (index === 0) return ['刚刚', '片刻后'];
  22. var unit = indexMapZh[parseInt(index / 2)];
  23. return [number + unit + '前', number + unit + '后'];
  24. }
  25. },
  26. // second, minute, hour, day, week, month, year(365 days)
  27. SEC_ARRAY = [60, 60, 24, 7, 365 / 7 / 12, 12],
  28. SEC_ARRAY_LEN = 6,
  29. ATTR_DATETIME = 'datetime',
  30. ATTR_DATA_TID = 'data-tid',
  31. timers = {}; // real-time render timers
  32. // format Date / string / timestamp to Date instance.
  33. function toDate(input) {
  34. if (input instanceof Date) return input;
  35. if (!isNaN(input)) return new Date(toInt(input));
  36. if (/^\d+$/.test(input)) return new Date(toInt(input));
  37. input = (input || '').trim().replace(/\.\d+/, '') // remove milliseconds
  38. .replace(/-/, '/').replace(/-/, '/')
  39. .replace(/(\d)T(\d)/, '$1 $2').replace(/Z/, ' UTC') // 2017-2-5T3:57:52Z -> 2017-2-5 3:57:52UTC
  40. .replace(/([\+\-]\d\d)\:?(\d\d)/, ' $1$2'); // -04:00 -> -0400
  41. return new Date(input);
  42. }
  43. // change f into int, remove decimal. Just for code compression
  44. function toInt(f) {
  45. return parseInt(f);
  46. }
  47. // format the diff second to *** time ago, with setting locale
  48. function formatDiff(diff, locale, defaultLocale) {
  49. // if locale is not exist, use defaultLocale.
  50. // if defaultLocale is not exist, use build-in `en`.
  51. // be sure of no error when locale is not exist.
  52. locale = locales[locale] ? locale : (locales[defaultLocale] ? defaultLocale : 'zh_CN');
  53. // if (! locales[locale]) locale = defaultLocale;
  54. var i = 0,
  55. agoin = diff < 0 ? 1 : 0, // timein or timeago
  56. total_sec = diff = Math.abs(diff);
  57. for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) {
  58. diff /= SEC_ARRAY[i];
  59. }
  60. diff = toInt(diff);
  61. i *= 2;
  62. if (diff > (i === 0 ? 9 : 1)) i += 1;
  63. return locales[locale](diff, i, total_sec)[agoin].replace('%s', diff);
  64. }
  65. // calculate the diff second between date to be formated an now date.
  66. function diffSec(date, nowDate) {
  67. nowDate = nowDate ? toDate(nowDate) : new Date();
  68. return (nowDate - toDate(date)) / 1000;
  69. }
  70. /**
  71. * nextInterval: calculate the next interval time.
  72. * - diff: the diff sec between now and date to be formated.
  73. *
  74. * What's the meaning?
  75. * diff = 61 then return 59
  76. * diff = 3601 (an hour + 1 second), then return 3599
  77. * make the interval with high performace.
  78. **/
  79. function nextInterval(diff) {
  80. var rst = 1, i = 0, d = Math.abs(diff);
  81. for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) {
  82. diff /= SEC_ARRAY[i];
  83. rst *= SEC_ARRAY[i];
  84. }
  85. // return leftSec(d, rst);
  86. d = d % rst;
  87. d = d ? rst - d : rst;
  88. return Math.ceil(d);
  89. }
  90. // get the datetime attribute, jQuery and DOM
  91. function getDateAttr(node) {
  92. if (node.dataset.timeago) return node.dataset.timeago; // data-timeago supported
  93. return getAttr(node, ATTR_DATETIME);
  94. }
  95. function getAttr(node, name) {
  96. if (node.getAttribute) return node.getAttribute(name); // native
  97. if (node.attr) return node.attr(name); // jquery
  98. }
  99. function setTidAttr(node, val) {
  100. if (node.setAttribute) return node.setAttribute(ATTR_DATA_TID, val); // native
  101. if (node.attr) return node.attr(ATTR_DATA_TID, val); // jquery
  102. }
  103. function getTidFromNode(node) {
  104. return getAttr(node, ATTR_DATA_TID);
  105. }
  106. /**
  107. * timeago: the function to get `timeago` instance.
  108. * - nowDate: the relative date, default is new Date().
  109. * - defaultLocale: the default locale, default is en. if your set it, then the `locale` parameter of format is not needed of you.
  110. *
  111. * How to use it?
  112. * var timeagoLib = require('timeago.js');
  113. * var timeago = timeagoLib(); // all use default.
  114. * var timeago = timeagoLib('2016-09-10'); // the relative date is 2016-09-10, so the 2016-09-11 will be 1 day ago.
  115. * var timeago = timeagoLib(null, 'zh_CN'); // set default locale is `zh_CN`.
  116. * var timeago = timeagoLib('2016-09-10', 'zh_CN'); // the relative date is 2016-09-10, and locale is zh_CN, so the 2016-09-11 will be 1天前.
  117. **/
  118. function Timeago(nowDate, defaultLocale) {
  119. this.nowDate = nowDate;
  120. // if do not set the defaultLocale, set it with `zh_CN`
  121. this.defaultLocale = defaultLocale || 'zh_CN'; // use default build-in locale
  122. // for dev test
  123. // this.nextInterval = nextInterval;
  124. }
  125. // what the timer will do
  126. Timeago.prototype.doRender = function (node, date, locale) {
  127. var diff = diffSec(date, this.nowDate),
  128. self = this,
  129. tid;
  130. // delete previously assigned timeout's id to node
  131. node.innerHTML = formatDiff(diff, locale, this.defaultLocale);
  132. // waiting %s seconds, do the next render
  133. timers[tid = setTimeout(function () {
  134. self.doRender(node, date, locale);
  135. delete timers[tid];
  136. }, Math.min(nextInterval(diff) * 1000, 0x7FFFFFFF))] = 0; // there is no need to save node in object.
  137. // set attribute date-tid
  138. setTidAttr(node, tid);
  139. };
  140. /**
  141. * format: format the date to *** time ago, with setting or default locale
  142. * - date: the date / string / timestamp to be formated
  143. * - locale: the formated string's locale name, e.g. en / zh_CN
  144. *
  145. * How to use it?
  146. * var timeago = require('timeago.js')();
  147. * timeago.format(new Date(), 'pl'); // Date instance
  148. * timeago.format('2016-09-10', 'fr'); // formated date string
  149. * timeago.format(1473473400269); // timestamp with ms
  150. **/
  151. Timeago.prototype.format = function (date, locale) {
  152. return formatDiff(diffSec(date, this.nowDate), locale, this.defaultLocale);
  153. };
  154. /**
  155. * render: render the DOM real-time.
  156. * - nodes: which nodes will be rendered.
  157. * - locale: the locale name used to format date.
  158. *
  159. * How to use it?
  160. * var timeago = require('timeago.js')();
  161. * // 1. javascript selector
  162. * timeago.render(document.querySelectorAll('.need_to_be_rendered'));
  163. * // 2. use jQuery selector
  164. * timeago.render($('.need_to_be_rendered'), 'pl');
  165. *
  166. * Notice: please be sure the dom has attribute `datetime`.
  167. **/
  168. Timeago.prototype.render = function (nodes, locale) {
  169. if (nodes.length === undefined) nodes = [nodes];
  170. for (var i = 0, len = nodes.length; i < len; i++) {
  171. this.doRender(nodes[i], getDateAttr(nodes[i]), locale); // render item
  172. }
  173. };
  174. /**
  175. * setLocale: set the default locale name.
  176. *
  177. * How to use it?
  178. * var timeago = require('timeago.js')();
  179. * timeago.setLocale('fr');
  180. **/
  181. Timeago.prototype.setLocale = function (locale) {
  182. this.defaultLocale = locale;
  183. };
  184. /**
  185. * timeago: the function to get `timeago` instance.
  186. * - nowDate: the relative date, default is new Date().
  187. * - defaultLocale: the default locale, default is en. if your set it, then the `locale` parameter of format is not needed of you.
  188. *
  189. * How to use it?
  190. * var timeagoFactory = require('timeago.js');
  191. * var timeago = timeagoFactory(); // all use default.
  192. * var timeago = timeagoFactory('2016-09-10'); // the relative date is 2016-09-10, so the 2016-09-11 will be 1 day ago.
  193. * var timeago = timeagoFactory(null, 'zh_CN'); // set default locale is `zh_CN`.
  194. * var timeago = timeagoFactory('2016-09-10', 'zh_CN'); // the relative date is 2016-09-10, and locale is zh_CN, so the 2016-09-11 will be 1天前.
  195. **/
  196. function timeagoFactory(nowDate, defaultLocale) {
  197. return new Timeago(nowDate, defaultLocale);
  198. }
  199. /**
  200. * register: register a new language locale
  201. * - locale: locale name, e.g. en / zh_CN, notice the standard.
  202. * - localeFunc: the locale process function
  203. *
  204. * How to use it?
  205. * var timeagoFactory = require('timeago.js');
  206. *
  207. * timeagoFactory.register('the locale name', the_locale_func);
  208. * // or
  209. * timeagoFactory.register('pl', require('timeago.js/locales/pl'));
  210. **/
  211. timeagoFactory.register = function (locale, localeFunc) {
  212. locales[locale] = localeFunc;
  213. };
  214. /**
  215. * cancel: cancels one or all the timers which are doing real-time render.
  216. *
  217. * How to use it?
  218. * For canceling all the timers:
  219. * var timeagoFactory = require('timeago.js');
  220. * var timeago = timeagoFactory();
  221. * timeago.render(document.querySelectorAll('.need_to_be_rendered'));
  222. * timeagoFactory.cancel(); // will stop all the timers, stop render in real time.
  223. *
  224. * For canceling single timer on specific node:
  225. * var timeagoFactory = require('timeago.js');
  226. * var timeago = timeagoFactory();
  227. * var nodes = document.querySelectorAll('.need_to_be_rendered');
  228. * timeago.render(nodes);
  229. * timeagoFactory.cancel(nodes[0]); // will clear a timer attached to the first node, stop render in real time.
  230. **/
  231. timeagoFactory.cancel = function (node) {
  232. var tid;
  233. // assigning in if statement to save space
  234. if (node) {
  235. tid = getTidFromNode(node);
  236. if (tid) {
  237. clearTimeout(tid);
  238. delete timers[tid];
  239. }
  240. } else {
  241. for (tid in timers) clearTimeout(tid);
  242. timers = {};
  243. }
  244. };
  245. exports('timeago', timeagoFactory());
  246. });