github.com/wtsi-ssg/wrstat@v1.1.4-0.20221008232152-3030622a8cf8/server/static/tree/js/jquery.timeago.js (about) 1 /** 2 * Timeago is a jQuery plugin that makes it easy to support automatically 3 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). 4 * 5 * @name timeago 6 * @version 1.6.7 7 * @requires jQuery >=1.5.0 <4.0 8 * @author Ryan McGeary 9 * @license MIT License - http://www.opensource.org/licenses/mit-license.php 10 * 11 * For usage and examples, visit: 12 * http://timeago.yarp.com/ 13 * 14 * Copyright (c) 2008-2019, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) 15 */ 16 17 (function (factory) { 18 if (typeof define === 'function' && define.amd) { 19 // AMD. Register as an anonymous module. 20 define(['jquery'], factory); 21 } else if (typeof module === 'object' && typeof module.exports === 'object') { 22 factory(require('jquery')); 23 } else { 24 // Browser globals 25 factory(jQuery); 26 } 27 }(function ($) { 28 $.timeago = function (timestamp) { 29 if (timestamp instanceof Date) { 30 return inWords(timestamp); 31 } else if (typeof timestamp === "string") { 32 return inWords($.timeago.parse(timestamp)); 33 } else if (typeof timestamp === "number") { 34 return inWords(new Date(timestamp)); 35 } else { 36 return inWords($.timeago.datetime(timestamp)); 37 } 38 }; 39 var $t = $.timeago; 40 41 $.extend($.timeago, { 42 settings: { 43 refreshMillis: 60000, 44 allowPast: true, 45 allowFuture: false, 46 localeTitle: false, 47 cutoff: 0, 48 autoDispose: true, 49 strings: { 50 prefixAgo: null, 51 prefixFromNow: null, 52 suffixAgo: "ago", 53 suffixFromNow: "from now", 54 inPast: "any moment now", 55 seconds: "< a minute", 56 minute: "~ a minute", 57 minutes: "%d minutes", 58 hour: "~ an hour", 59 hours: "~ %d hours", 60 day: "a day", 61 days: "%d days", 62 month: "~ a month", 63 months: "%d months", 64 year: "~ a year", 65 years: "%d years", 66 wordSeparator: " ", 67 numbers: [] 68 } 69 }, 70 71 inWords: function (distanceMillis) { 72 if (!this.settings.allowPast && !this.settings.allowFuture) { 73 throw 'timeago allowPast and allowFuture settings can not both be set to false.'; 74 } 75 76 var $l = this.settings.strings; 77 var prefix = $l.prefixAgo; 78 var suffix = $l.suffixAgo; 79 if (this.settings.allowFuture) { 80 if (distanceMillis < 0) { 81 prefix = $l.prefixFromNow; 82 suffix = $l.suffixFromNow; 83 } 84 } 85 86 if (!this.settings.allowPast && distanceMillis >= 0) { 87 return this.settings.strings.inPast; 88 } 89 90 var seconds = Math.abs(distanceMillis) / 1000; 91 var minutes = seconds / 60; 92 var hours = minutes / 60; 93 var days = hours / 24; 94 var years = days / 365; 95 96 function substitute(stringOrFunction, number) { 97 var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; 98 var value = ($l.numbers && $l.numbers[number]) || number; 99 return string.replace(/%d/i, value); 100 } 101 102 var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || 103 seconds < 90 && substitute($l.minute, 1) || 104 minutes < 45 && substitute($l.minutes, Math.round(minutes)) || 105 minutes < 90 && substitute($l.hour, 1) || 106 hours < 24 && substitute($l.hours, Math.round(hours)) || 107 hours < 42 && substitute($l.day, 1) || 108 days < 30 && substitute($l.days, Math.round(days)) || 109 days < 45 && substitute($l.month, 1) || 110 days < 365 && substitute($l.months, Math.round(days / 30)) || 111 years < 1.5 && substitute($l.year, 1) || 112 substitute($l.years, Math.round(years)); 113 114 var separator = $l.wordSeparator || ""; 115 if ($l.wordSeparator === undefined) { separator = " "; } 116 return $.trim([prefix, words, suffix].join(separator)); 117 }, 118 119 parse: function (iso8601) { 120 var s = $.trim(iso8601); 121 s = s.replace(/\.\d+/, ""); // remove milliseconds 122 s = s.replace(/-/, "/").replace(/-/, "/"); 123 s = s.replace(/T/, " ").replace(/Z/, " UTC"); 124 s = s.replace(/([\+\-]\d\d)\:?(\d\d)/, " $1$2"); // -04:00 -> -0400 125 s = s.replace(/([\+\-]\d\d)$/, " $100"); // +09 -> +0900 126 return new Date(s); 127 }, 128 datetime: function (elem) { 129 var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); 130 return $t.parse(iso8601); 131 }, 132 isTime: function (elem) { 133 // jQuery's `is()` doesn't play well with HTML5 in IE 134 return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); 135 } 136 }); 137 138 // functions that can be called via $(el).timeago('action') 139 // init is default when no action is given 140 // functions are called with context of a single element 141 var functions = { 142 init: function () { 143 functions.dispose.call(this); 144 var refresh_el = $.proxy(refresh, this); 145 refresh_el(); 146 var $s = $t.settings; 147 if ($s.refreshMillis > 0) { 148 this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis); 149 } 150 }, 151 update: function (timestamp) { 152 var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp); 153 $(this).data('timeago', { datetime: date }); 154 if ($t.settings.localeTitle) { 155 $(this).attr("title", date.toLocaleString()); 156 } 157 refresh.apply(this); 158 }, 159 updateFromDOM: function () { 160 $(this).data('timeago', { datetime: $t.parse($t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title")) }); 161 refresh.apply(this); 162 }, 163 dispose: function () { 164 if (this._timeagoInterval) { 165 window.clearInterval(this._timeagoInterval); 166 this._timeagoInterval = null; 167 } 168 } 169 }; 170 171 $.fn.timeago = function (action, options) { 172 var fn = action ? functions[action] : functions.init; 173 if (!fn) { 174 throw new Error("Unknown function name '" + action + "' for timeago"); 175 } 176 // each over objects here and call the requested function 177 this.each(function () { 178 fn.call(this, options); 179 }); 180 return this; 181 }; 182 183 function refresh() { 184 var $s = $t.settings; 185 186 //check if it's still visible 187 if ($s.autoDispose && !$.contains(document.documentElement, this)) { 188 //stop if it has been removed 189 $(this).timeago("dispose"); 190 return this; 191 } 192 193 var data = prepareData(this); 194 195 if (!isNaN(data.datetime)) { 196 if ($s.cutoff === 0 || Math.abs(distance(data.datetime)) < $s.cutoff) { 197 $(this).text(inWords(data.datetime)); 198 } else { 199 if ($(this).attr('title').length > 0) { 200 $(this).text($(this).attr('title')); 201 } 202 } 203 } 204 return this; 205 } 206 207 function prepareData(element) { 208 element = $(element); 209 if (!element.data("timeago")) { 210 element.data("timeago", { datetime: $t.datetime(element) }); 211 var text = $.trim(element.text()); 212 if ($t.settings.localeTitle) { 213 element.attr("title", element.data('timeago').datetime.toLocaleString()); 214 } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { 215 element.attr("title", text); 216 } 217 } 218 return element.data("timeago"); 219 } 220 221 function inWords(date) { 222 return $t.inWords(distance(date)); 223 } 224 225 function distance(date) { 226 return (new Date().getTime() - date.getTime()); 227 } 228 229 // fix for IE6 suckage 230 document.createElement("abbr"); 231 document.createElement("time"); 232 }));