Logo.png

Welcome to TV Channel Lists.

If you wish to contribute to and edit TV channel listings, please log in or request an account. Questions? Ask the admins.

You cannot watch TV channels on this website. TV Channel Lists is not affiliated with any TV provider/channel and cannot answer questions regarding your TV service.

MediaWiki:Gadget-CommentsInLocalTime.js

From TVCL - TV Channel Lists
Jump to navigation Jump to search

Note: After saving, 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: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
window.LocalComments = $.extend(window.LocalComments, {
	formats: {
		day: function (then) { return then.fromNow(); },
		week: function (then) { return then.calendar(); },
		other: "LLL",
	},
	tooltipFormats: [
		function (then) { return then.fromNow(); },
		"LLLL",
	],
});

if (mw.config.get('wgNamespaceNumber') % 2 === 1) {
    /**
     * 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: function (then) {
                var pref = mw.user.options.values.date;
                return then.format(window.LocalComments.formatOptions[pref] || "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"],
        
        /**
         * An object mapping the date format user options provided by this MediaWiki
         * installation to corresponding Moment.js format strings. The user can
         * choose a preferred date format in
         * [[Special:Preferences#mw-prefsection-rendering-dateformat]]. See
         * [[mw:Manual:Date formatting]]. These formats determine the default
         * timestamp display format.
         * 
         * These formats come from
         * <https://doc.wikimedia.org/mediawiki-core/1.34.0/php/MessagesEn_8php.html#a2fc93ea5327f655d3ed306e221ee33f0>.
         * When customizing these formats for a different wiki’s content language,
         * consult the language’s corresponding message file’s `$dateFormats`
         * variable. Use only the messages with the “both” suffix, and remove that
         * suffix from each key. The MediaWiki date format syntax is described in
         * <https://doc.wikimedia.org/mediawiki-core/1.34.0/php/classLanguage.html#a94f84f82d7f954c4cb2e191d22c6e6a6>
         * and [[mw:Help:Extension:ParserFunctions##time]]. The Moment.js syntax is
         * described in <https://momentjs.com/docs/#/parsing/string-format/>.
         * 
         * @todo Automatically convert MediaWiki date format syntax to Moment.js
         *       date format syntax.
         */
        formatOptions: {
            mdy: "HH:mm, MMMM D, YYYY", // H:i, F j, Y
            dmy: "HH:mm, D MMMM YYYY", // H:i, j F Y
            ymd: "HH:mm, YYYY MMMM D", // H:i, Y F j
            "ISO 8601": "YYYY-MM-DDTHH:mm:ss", // xnY-xnm-xnd"T"xnH:xni:xns
        },
        
        /**
         * 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;
            },
        });
        
        /**
         * Marks 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();
        });
    });
}