User:Mxn/CommentsInLocalTime.js
Appearance
< User:Mxn
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | This user script seems to have a documentation page at User:Mxn/CommentsInLocalTime. |
/**
* Comments in local time
* [[:vi:Wikipedia:Tin nhắn theo giờ địa phương]]
*
* 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]].
*/
/**
* Default settings for this gadget.
*/
window.LocalComments = $.extend(window.LocalComments, {
//* Names of tags that often directly contain timestamps.
proseTags: ["dd", "li", "p", "td"],
//* Names of tags that don’t contain timestamps either directly or indirectly.
codeTags: ["code", "input", "pre", "textarea"],
//* When true, this gadget refreshes timestamps periodically.
dynamic: true,
});
$(function () {
if ([-1, 0, 8, 100, 108, 118].indexOf(mw.config.get("wgNamespaceNumber")) !== -1
|| ["view", "submit"].indexOf(mw.config.get("wgAction")) === -1
|| mw.config.get("wgCanonicalSpecialPageName")
|| mw.util.getParamValue("disable") === "loco")
{
return;
}
//* Abbreviated month names in English.
var enAbbrMonths = mw.config.get("wgMonthNamesShort").splice(1);
//* Spelled-out month names in English.
var enFullMonths = mw.config.get("wgMonthNames").splice(1);
/**
* Regular expression matching all the timestamps inserted by this MediaWiki
* installation over the years.
*
* Until 2005:
* 18:16, 23 Dec 2004 (UTC)
* 2005–present:
* 08:51, 23 November 2015 (UTC)
*/
var dateRe = new RegExp("(\\d\\d):(\\d\\d), (\\d\\d?) (" +
enFullMonths.join("|") + "|" + enAbbrMonths.join("|") +
") (\\d{4}) \\(UTC\\)");
//* One-based captured group numbers in dateRe keyed by time unit.
var dateReGroups = {
year: 5,
month: 4,
dayOfMonth: 3,
hour: 1,
minute: 2,
};
var proseTags = window.LocalComments.proseTags.join("\n").toUpperCase().split("\n");
// Exclude <time> to avoid an infinite loop when iterating over text nodes.
var codeTags = $.merge(window.LocalComments.codeTags, ["time"]).join(", ");
/**
* Converts the regular expression search result into a Date object.
*
* @param {Object} result The result of calling exec() on a RegExp object.
* @returns {Date}
*/
function toDate(result) {
var month = enFullMonths.indexOf(result[dateReGroups.month]);
if (month === -1) month = enAbbrMonths.indexOf(result[dateReGroups.month]);
return new Date(Date.UTC(result[dateReGroups.year], month,
result[dateReGroups.dayOfMonth], result[dateReGroups.hour],
result[dateReGroups.minute]));
}
// 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) 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 && dateRe.test(node.data);
return isDateNode ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
});
// Mark up each timestamp found.
var prefixNode;
while ((prefixNode = iter.nextNode())) {
var result = dateRe.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);
// 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", toDate(result).toISOString());
$(dateNode).wrap(timeElt);
}
/**
* 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);
var now = moment();
var withinHours = Math.abs(then.diff(now, "hours", true))
<= moment.relativeTimeThreshold("h");
var text;
if (withinHours) {
// Within a day, show a relative time that’s easy to relate to.
text = then.fromNow();
}
else {
var dayDiff = then.diff(moment().startOf("day"), "days", true);
if (dayDiff > -6 && dayDiff < 7) {
// 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.
text = then.calendar();
}
else {
// The calendar() method uses an ambiguous “MM/DD/YYYY” format
// for faraway dates; spell things out for this international
// audience.
text = then.format("LLL");
}
}
$(elt).text(text);
// Add a tooltip with multiple formats.
elt.title = then.fromNow() + "\n" +
then.format("LLLL[\n]YYYY-MM-DDTHH:mmZ");
// 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() {
$(".localcomments").each(function (idx, elt) {
// Update every timestamp at least this once.
formatTimestamp(idx, elt);
if (!window.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", formatTimestamps);
});