MediaWiki:Gadget-relativetime.js
Revision as of 08:22, 16 February 2022 by Soulgazer (talk | contribs) (Created page with "// Don't load CommentsInLocalTime for namespaces it is disabled for. if ( [-1, 0, 8].indexOf(mw.config.get("wgNamespaceNumber")) === -1 ) { // w:en:User:Mxn/CommentsInLocalTime // en.wikipedia.org/wiki/User:Mxn/CommentsInLocalTime.js /** * Comments in local time * User:Mxn/CommentsInLocalTime * * Adjust timestamps in comment signatures to use easy-to-understand, relative * local time instead of absolute UTC time. * * Inspired by [[Wikipedia:C...")
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
// Don't load CommentsInLocalTime for namespaces it is disabled for. if ( [-1, 0, 8].indexOf(mw.config.get("wgNamespaceNumber")) === -1 ) { // [[w:en:User:Mxn/CommentsInLocalTime]] // en.wikipedia.org/wiki/User:Mxn/CommentsInLocalTime.js /** * Comments in local time * [[User:Mxn/CommentsInLocalTime]] * * Adjust timestamps in comment signatures to use easy-to-understand, relative * local time instead of absolute UTC time. * * Inspired by [[Wikipedia:Comments in Local Time]]. * * @author [[User:Mxn]] */ /** * Default settings for this gadget. */ window.LocalComments = $.extend({ // USER OPTIONS //////////////////////////////////////////////////////////// /** * When false, this gadget does nothing. */ enabled: true, /** * Formats to display inline for each timestamp, keyed by a few common * cases. * * If a property of this object is set to a string, the timestamp is * formatted according to the documentation at * <http://momentjs.com/docs/#/displaying/format/>. * * If a property of this object is set to a function, it is called to * retrieve the formatted timestamp string. See * <http://momentjs.com/docs/#/displaying/> for the various things you can * do with the passed-in moment object. */ formats: { /** * Within a day, show a relative time that’s easy to relate to. */ day: function (then) { return then.fromNow(); }, /** * Within a week, show a relative date and specific time, still helpful * if the user doesn’t remember today’s date. Don’t show just a relative * time, because a discussion may need more context than “Last Friday” * on every comment. */ week: function (then) { return then.calendar(); }, /** * The calendar() method uses an ambiguous “MM/DD/YYYY” format for * faraway dates; spell things out for this international audience. */ other: "LLL", }, /** * Formats to display in each timestamp’s tooltip, one per line. * * If an element of this array is a string, the timestamp is formatted * according to the documentation at * <http://momentjs.com/docs/#/displaying/format/>. * * If an element of this array is a function, it is called to retrieve the * formatted timestamp string. See <http://momentjs.com/docs/#/displaying/> * for the various things you can do with the passed-in moment object. */ tooltipFormats: [ function (then) { return then.fromNow(); }, "LLLL", "YYYY-MM-DDTHH:mmZ", ], /** * When true, this gadget refreshes timestamps periodically. */ dynamic: true, }, { // SITE OPTIONS //////////////////////////////////////////////////////////// /** * Numbers of namespaces to completely ignore. See [[Wikipedia:Namespace]]. */ excludeNamespaces: [-1, 0, 8, 100, 108, 118], /** * Names of tags that often directly contain timestamps. * * This is merely a performance optimization. This gadget will look at text * nodes in any tag other than the codeTags, but adding a tag here ensures * that it gets processed the most efficient way possible. */ proseTags: ["dd", "li", "p", "td"], /** * Names of tags that don’t contain timestamps either directly or * indirectly. */ codeTags: ["code", "input", "pre", "textarea"], /** * Expected format or formats of the timestamps in existing wikitext. If * very different formats have been used over the course of the wiki’s * history, specify an array of formats. * * This option expects parsing format strings * <http://momentjs.com/docs/#/parsing/string-format/>. */ parseFormat: "H:m, D MMM YYYY", /** * Regular expression matching all the timestamps inserted by this MediaWiki * installation over the years. This regular expression should more or less * agree with the parseFormat option. * * Until 2005: * 18:16, 23 Dec 2004 (UTC) * 2005–present: * 08:51, 23 November 2015 (UTC) */ parseRegExp: /\d\d:\d\d, \d\d? (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w* \d{4} \(UTC\)/, /** * UTC offset of the wiki's default local timezone. See * [[mw:Manual:Timezone]]. */ utcOffset: 0, }, window.LocalComments); $(function () { if (!LocalComments.enabled || LocalComments.excludeNamespaces.indexOf(mw.config.get("wgNamespaceNumber")) !== -1 || ["view", "submit"].indexOf(mw.config.get("wgAction")) === -1 || mw.util.getParamValue("disable") === "loco") { return; } var proseTags = LocalComments.proseTags.join("\n").toUpperCase().split("\n"); // Exclude <time> to avoid an infinite loop when iterating over text nodes. var codeTags = $.merge(LocalComments.codeTags, ["time"]).join(", "); // Look in the content body for DOM text nodes that may contain timestamps. // The wiki software has already localized other parts of the page. var root = $("#wikiPreview, #mw-content-text")[0]; if (!root || !("createNodeIterator" in document)) return; var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, { acceptNode: function (node) { // We can’t just check the node’s direct parent, because templates // like [[Template:Talkback]] and [[Template:Resolved]] may place a // signature inside a nondescript <span>. var isInProse = proseTags.indexOf(node.parentElement.nodeName) !== -1 || !$(node).parents(codeTags).length; var isDateNode = isInProse && LocalComments.parseRegExp.test(node.data); return isDateNode ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; }, }); // Mark up each timestamp found. function wrapTimestamps() { var prefixNode; while ((prefixNode = iter.nextNode())) { var result = LocalComments.parseRegExp.exec(prefixNode.data); if (!result) continue; // Split out the timestamp into a separate text node. var dateNode = prefixNode.splitText(result.index); var suffixNode = dateNode.splitText(result[0].length); // Determine the represented time. var then = moment.utc(result[0], LocalComments.parseFormat); if (!then.isValid()) { // Many Wikipedias started out with English as the default // localization, so fall back to English. then = moment.utc(result[0], "H:m, D MMM YYYY", "en"); } if (!then.isValid()) continue; then.utcOffset(-LocalComments.utcOffset); // Wrap the timestamp inside a <time> element for findability. var timeElt = $("<time />"); // MediaWiki core styles .explain[title] the same way as // abbr[title], guiding the user to the tooltip. timeElt.addClass("localcomments explain"); timeElt.attr("datetime", then.toISOString()); $(dateNode).wrap(timeElt); } } /** * Returns a formatted string for the given moment object. * * @param {Moment} then The moment object to format. * @param {String} fmt A format string or function. * @returns {String} A formatted string. */ function formatMoment(then, fmt) { return (fmt instanceof Function) ? fmt(then) : then.format(fmt); } /** * Reformats a timestamp marked up with the <time> element. * * @param {Number} idx Unused. * @param {Element} elt The <time> element. */ function formatTimestamp(idx, elt) { var iso = $(elt).attr("datetime"); var then = moment(iso, moment.ISO_8601); var now = moment(); var withinHours = Math.abs(then.diff(now, "hours", true)) <= moment.relativeTimeThreshold("h"); var formats = LocalComments.formats; var text; if (withinHours) { text = formatMoment(then, formats.day || formats.other); } else { var dayDiff = then.diff(moment().startOf("day"), "days", true); if (dayDiff > -6 && dayDiff < 7) { text = formatMoment(then, formats.week || formats.other); } else text = formatMoment(then, formats.other); } $(elt).text(text); // Add a tooltip with multiple formats. elt.title = $.map(LocalComments.tooltipFormats, function (fmt, idx) { return formatMoment(then, fmt); }).join("\n"); // Register for periodic updates. var withinMinutes = withinHours && Math.abs(then.diff(now, "minutes", true)) <= moment.relativeTimeThreshold("m"); var withinSeconds = withinMinutes && Math.abs(then.diff(now, "seconds", true)) <= moment.relativeTimeThreshold("s"); var unit = withinSeconds ? "seconds" : (withinMinutes ? "minutes" : (withinHours ? "hours" : "days")); $(elt).attr("data-localcomments-unit", unit); } /** * Reformat all marked-up timestamps and start updating timestamps on an * interval as necessary. */ function formatTimestamps() { wrapTimestamps(); $(".localcomments").each(function (idx, elt) { // Update every timestamp at least this once. formatTimestamp(idx, elt); if (!LocalComments.dynamic) return; // Update this minute’s timestamps every second. if ($("[data-localcomments-unit='seconds']").length) { setInterval(function () { $("[data-localcomments-unit='seconds']").each(formatTimestamp); }, 1000 /* ms */); } // Update this hour’s timestamps every minute. setInterval(function () { $("[data-localcomments-unit='minutes']").each(formatTimestamp); }, 60 /* s */ * 1000 /* ms */); // Update today’s timestamps every hour. setInterval(function () { $("[data-localcomments-unit='hours']").each(formatTimestamp); }, 60 /* min */ * 60 /* s */ * 1000 /* ms */); }); } mw.loader.using("moment", function () { wrapTimestamps(); formatTimestamps(); }); }); }