User:Enterprisey/live-reload.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// vim: ts=4 sw=4 et ai
//<nowiki>
if( mw.config.get( "wgAction" ) === "history" || mw.config.get( "wgPageName" ) === "Special:AbuseLog" ) {
    $.when( mw.loader.using( [ "mediawiki.api", "mediawiki.util" ] ), $.ready ).then( function () {
        var monthNames = mw.config.get( "wgMonthNames" );
        var IP_RGX = /^(([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{1,4}$|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/; // incorporates https://stackoverflow.com/a/14960927/1757964

        function addNew( numNew ) {
            window.liveReloadCount += numNew;
            document.title = "(" + window.liveReloadCount + ") " + document.title.replace( /^\(.+?\)\s+?/, "" );
        }

        // thanks https://stackoverflow.com/a/26434619/1757964
        function parseIsoTimestamp( timestamp ) {
            var dt = timestamp.split( /[: T-]/ ).map( parseFloat );
            return new Date( Date.UTC( dt[0], dt[1] - 1, dt[2], dt[3] || 0, dt[4] || 0, dt[5] || 0, 0 ) );
        }

        function parseAbuseLogTimestamp( timestamp ) {
            var match = timestamp.match( /^(\d\d):(\d\d),\s+(\d\d?)\s+(\w+)\s+(\d+)/ );
            //                            1      2         3         4       5
            return new Date( Date.UTC( parseInt( match[5] ), monthNames.indexOf( match[4] ) - 1, parseInt( match[3] ), parseInt( match[1] ), parseInt( match[2] ) ) );
        }

        function formatAbuseLogTimestamp( dateObj ) {
            var iso = dateObj.toISOString(); // e.g. 2011-10-05T14:48:00.000Z
            return iso.substring( 11, 13 ) + ":" + iso.substring( 14, 16 ) + ", " + iso.substring( 8, 10 ) + " " + monthNames[parseInt( iso.substring( 5, 7 ) )] + " " + iso.substring( 0, 4 );
        }

        // A list of every type of page supported by the script, and what to do on them.
        var CONFIGURATIONS = [
            {
                enable: function () { return mw.config.get( "wgAction" ) === "history"; },
                params: function () {
                    return {
                        action: "query",
                        prop: "revisions",
                        titles: mw.config.get( "wgPageName" ).replace( /_/g, " " ),
                        rvprop: "ids|timestamp|user|comment|size",
                        rvendid: parseInt( $( "#pagehistory li" ).get( 0 ).dataset.mwRevid ) + 1,
                        formatversion: "2"
                    };
                },
                handler: function ( data ) {
                    var revs = data.query.pages[0].revisions;
                    if( !revs ) {
                        return;
                    }
                    var i;
                    for( i = 0; i < revs.length; i++ ) {
                        if( $( "li[data-mw-revid=" + revs[i].revid + "]" ).length ) {
                            break;
                        }
                    }
                    revs = revs.slice( 0, i );
                    if( revs.length ) {
                        addNew( revs.length );

                        var previousTopEntry = $( "#pagehistory li" ).first();
                        $( "#pagehistory" ).prepend( revs.map( function ( rev, index ) {
                            var previousRevSize = ( index + 1 ) < revs.length ? revs[ index + 1 ].size : parseInt( previousTopEntry.find( ".history-size" ).text().match( /^(\d+|,)+/ )[0].replace( /,/g, "" ) );
                            var bytesDiff = rev.size - previousRevSize;
                            bytesDiff = ( ( bytesDiff > 0 ) ? "+" : "" ) + bytesDiff;
                            var previousRevid = ( index + 1 ) < revs.length ? revs[ index + 1 ].revid : previousTopEntry.get( 0 ).dataset.mwRevid;
                            return $( "<li>", { "data-mw-revid": rev.revid, "class": "live-reload-new" } ).append(
                                "(",
                                $( "<a>" )
                                    .attr( "href", mw.util.getUrl( "Special:Diff/" + rev.revid ) )
                                    .text( "prev" ),
                                ")&emsp;",
                                $( "<a>" )
                                    .attr( "href", mw.util.getUrl( mw.config.get( "wgPageName" ), { oldid: rev.revid } ) )
                                    .text( rev.timestamp.replace( /[TZ]/g, " " ) ),
                                $( "<span>", { "class": "history-user" } )
                                    .append( $( "<a>", { "class": "mw-userlink userlink" + ( rev.anon ? " mw-anonuserlink" : "" ) } )
                                        .attr( "href", mw.util.getUrl( rev.anon ? ( "Special:Contributions/" + rev.user ) : ( "User:" + rev.user ) ) )
                                        .text( rev.user ) ),
                                " ",
                                $( "<span>", { "class": "history-size mw-diff-bytes" } ).text( rev.size + " bytes" ),
                                " ",
                                $( "<span>", {
                                    "class": "mw-diff-bytes mw-plusminus-" + ( bytesDiff > 0 ? "pos" : ( bytesDiff < 0 ? "neg" : "null" ) )
                                } ).text( bytesDiff ),
                                " ",
                                $( "<span>", { "class": "comment" } ).text( "(" + rev.comment + ")" ),
                                " ",
                                $( "<span>", { "class": "mw-changeslist-links" } ).append(
                                    $( "<span>" ).append(
                                        $( "<span>", { "class": "mw-history-undo" } ).append(
                                            $( "<a>" )
                                                .attr( "href", mw.util.getUrl( mw.config.get( "wgPageName" ), {
                                                    action: "edit",
                                                    undo: rev.revid,
                                                    undoafter: previousRevid
                                                } ) )
                                                .text( "undo" ) ) ) )
                            );
                        } ) );

                        var content = $( revs.map( function ( rev ) { return "li[data-mw-revid='" + rev.revid + "']"; } ).join( "," ) );
                        mw.hook( "wikipage.content" ).fire( content );

                        // Calculate the number of consecutive edits made by the most recent editor
                        var numConsecutive = 1;
                        var firstEls = $( "#pagehistory li:lt(10)" );
                        var firstUser = firstEls[0].querySelector( ".mw-userlink" ).textContent;
                        for( var i = 1; i < 12; i++ ) {
                            if( firstEls[i] && firstEls[i].querySelector( ".mw-userlink" ).textContent === firstUser ) {
                                numConsecutive++;
                            } else {
                                break;
                            }
                        }
                        var consecutive = numConsecutive > 10 ? "more than 10" : numConsecutive;

                        // Move the rollback link to the first edit
                        var token = decodeURIComponent( $( ".mw-rollback-link a" ).attr( "href" ).match( /token=(.+)$/ )[1] );
                        $( ".mw-rollback-link" ).parent().remove();
                        $( "#pagehistory li" ).first().find( ".mw-changeslist-links" ).prepend(
                            $( "<span>" ).append(
                                $( "<span>", { "class": "mw-rollback-link" } ).append(
                                    $( "<a>" )
                                        .text( "rollback: " + consecutive + " edit" + ( numConsecutive === 1 ? "" : "s" ) )
                                        .attr( "href", mw.util.getUrl( mw.config.get( "wgPageName" ), {
                                            action: "rollback",
                                            from: firstUser,
                                            token: token
                                        } ) ) ) ) );
                    }
                }
            },
            {
                enable: function () { return mw.config.get( "wgPageName" ) === "Special:AbuseLog" && window.location.search.indexOf( "wpSearchFilter" ) >= 0; },
                params: function () {
                    var firstEntry = $( "ul" ).first().find( "li" ).first();
                    var timestamp;
                    if( firstEntry.get( 0 ).dataset.timestamp ) {
                        timestamp = parseIsoTimestamp( firstEntry.get( 0 ).dataset.timestamp );
                    } else {
                        timestamp = parseAbuseLogTimestamp( firstEntry.text() );
                    }
                    timestamp.setSeconds( timestamp.getSeconds() + 1 );
                    var filter = decodeURIComponent( window.location.search ).match( /wpSearchFilter=((?:\d+|\|)+)($|&|#)/ )[1];
                    return {
                        action: "query",
                        list: "abuselog",
                        aflprop: "ids|user|title|action|result|timestamp",
                        aflend: timestamp.toISOString(),
                        aflfilter: filter,
                        formatversion: "2",
                    };
                },
                handler: function ( data ) {
                    var items = data.query.abuselog;
                    if( !items || !items.length ) {
                        return;
                    }
                    addNew( items.length );
                    var newElements = items.map( function ( item ) {

                        // MediaWiki blocks registration of usernames that look like IPs, so this is safe
                        var isAnon = IP_RGX.test( item.user );
                        return $( "<li>", { "class": "live-reload-new", "data-timestamp": item.timestamp } ).append(
                            formatAbuseLogTimestamp( parseIsoTimestamp( item.timestamp ) ),
                            ": ",
                            $( "<a>", { "class": "mw-userlink userlink" + ( isAnon ? " mw-anonuserlink" : "" ) } )
                                .attr( "href", mw.util.getUrl( "Special:Contributions/" + item.user ) )
                                .text( item.user ),
                            $( "<span>", { "class": "mw-usertoollinks" } ).append(
                                " (",
                                $( "<a>", { "class": "mw-usertoollinks-talk userlink" } ).attr( "href", mw.util.getUrl( "User talk:" + item.user, { "vanarticle": item.title } ) ).text( "talk" ),
                                ")" ),
                            " triggered ",
                            $( "<a>" )
                                .attr( "href", mw.util.getUrl( "Special:AbuseFilter/" + item.filter_id ) )
                                .text( "filter " + item.filter_id ),
                            ", performing the action \"",
                            item.action,
                            "\" on ",
                            $( "<a>" )
                                .attr( "href", mw.util.getUrl( item.title ) )
                                .text( item.title ),
                            ". Actions taken: ",
                            item.result,
                            " (",
                            $( "<a>" )
                                .attr( "href", mw.util.getUrl( "Special:AbuseLog/" + item.id ) )
                                .text( "details" ),
                            " | ",
                            $( "<a>" )
                                .attr( "href", mw.util.getUrl( "Special:AbuseLog/examine/log/" + item.id ) )
                                .text( "examine" ),
                            ")" );
                    } );
                    $( "ul" ).first().prepend( newElements );
                    newElements.forEach( function ( e ) { mw.hook( "wikipage.content" ).fire( e ); } );
                }
            }
        ];

        var config = CONFIGURATIONS.filter( function ( c ) { return c.enable(); } );
        if( config.length > 1 ) {
            throw new Error( "More than one configuration was enabled!" );
        }
        config = config[0];

        function start( intervalInMinutes ) {
            var api = new mw.Api();
            function go() {
                api.get( config.params() ).then( config.handler );
            }

            var intervalId = window.setInterval( go, intervalInMinutes * 60 * 1000 );
            window.liveReloadCount = 0;
            mw.util.addCSS( ".live-reload-new { background-color: #cfc; }" );
            $( "#mw-history-compare,.mw-specialpage-summary" ).before(
                $( "<div>" ).append(
                    $( "<button>" )
                        .addClass( "mw-ui-button" )
                        .text( "Reset live count" )
                        .click( function () {
                            window.liveReloadCount = 0;
                            document.title = document.title.replace( /^\(.+?\)\s+?/, "" );
                            $( ".live-reload-new" ).removeClass( "live-reload-new" );
                        } ),
                    " ",
                    $( "<button>" )
                        .addClass( "mw-ui-button" )
                        .text( "Disable live count" )
                        .click( function () {
                            window.clearInterval( intervalId );
                            $( this ).parent().remove();
                        } )
                )
                    .css( { "margin": "0.5em 0" } )
            );
        }

        var link = mw.util.addPortletLink( "p-cactions", "#",
            "Live reload (1 min)", "pt-livereload-one-minute",
            "live reload this page every minute" );
        if( link ) {
            link.addEventListener( "click", function () { start( 1 ); } );
        }

        var link2 = mw.util.addPortletLink( "p-cactions", "#",
            "Live reload (custom)", "pt-livereload-custom",
            "live reload this page every X minutes" );
        if( link2 ) {
            link2.addEventListener( "click", function () {
                var interval = window.prompt( "Interval, in minutes" );
                if( interval ) {
                    start( parseFloat( interval ) );
                }
            } );
        }
    } );
}
//</nowiki>