User:Enterprisey/orcp-helper.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.
//<nowiki>
( function ( $, mw ) {

    function specificElementId ( username ) {
        var prefix = "orcp-" + username.replace( /[^a-z0-9]/g, "_" ) + "-";
        return {
            "rating": prefix + "rating-field",
            "comment": prefix + "comment-field",
            "button": prefix + "rate-button",
            "placeholder": prefix + "placeholder",
            "cancel": prefix + "cancel-button"
        };
    }

    function specificElement ( username ) {
        var ids = specificElementId( username );

        // Object "mapping" taken from http://stackoverflow.com/a/14810722
        return Object.keys( ids ).reduce( function ( previous, current ) {
            previous[ current ] = $( "#" + ids[ current ] );
            return previous;
        }, {} );
    }

    function makeActionLink ( username ) {
        return $( "<a>" ).css( "cursor", "pointer" ).text( "add rating" ).attr( "href", "#" ).on( "click", function ( e ) {
            console.log( "Link for " + username + " was clicked." );
            buildPanel( $( this ), username );
            $( this ).replaceWith( "<span id='" + specificElementId( username ).placeholder + "'>add rating</a>" );
            return false;
        } );
    }

    /*
     * target is the "add request" link that was clicked to trigger this.
     */
    function buildPanel ( target, username ) {
        var ids = specificElementId( username ),
            createSeparator = function () { return $( "<span class='separator'>|</span>" ); };
        target.parent().parent().after( `
        <div class="orcp-rating">
          <table>
            <tr>
              <th><label for="${ ids.rating }">Rating</label></th>
              <th><label for="${ ids.comment }">Comment</label></th>
              <th>&nbsp;</th>
            </tr>
            <tr>
              <td>
                <input type="text" id="${ ids.rating }" class="mw-ui-input mw-ui-input-inline" value="5" maxlength="3" style="width:3em" />
                <span style="margin-right: 6px"> / 10</span>
              </td>
              <td>
                <textarea id="${ ids.comment }" class="mw-ui-input" rows="1" />
              </td>
              <td>
                <button id="${ ids.button }" class="mw-ui-button mw-ui-constructive">Rate</button>
                <button id="${ ids.cancel }" class="mw-ui-button mw-ui-destructive mw-ui-quiet">Cancel</button>
              </td>
            </tr>
          </table>
        </div>`);
        specificElement( username ).rating.blur( function () { validate( username ); } );
        specificElement( username ).comment.blur( function () { validate( username ); } );
        specificElement( username ).button.click( function () { saveRating( username, $( this ).parent() ); } );
        specificElement( username ).cancel.click( function () {

            // This statement walks back up the DOM to the .orcp-rating div
            $( this ).parent().parent().parent().parent().parent().remove();
            specificElement( username ).placeholder.replaceWith( makeActionLink( username ) );
        } );
    }

    function validate ( username ) {
        var userInput = specificElement( username ).rating.val(),
            validRating = /^[0-9]+(\.[0-9]+)?$/.test( userInput ) &&
                ( parseInt( userInput, 10 ) <= 10 );
        specificElement( username ).rating.toggleClass( "invalid", !validRating );
        specificElement( username ).button.prop( "disabled", !validRating );
        return validRating;
    }

    // The argument "that" is the entire panel div
    function saveRating ( username, that ) {
        if ( !validate( username ) ) return;
        $( specificElement( username ).button ).prop( "disabled", true );
        $( specificElement( username ).rating ).prop( "disabled", true );
        $( specificElement( username ).comment ).prop( "disabled", true );
        var statusElement = $( "<span style='margin-left:10px' class='status'>Saving rating...</span>" ).appendTo( that ),
            setStatus = function ( status, callback ) { statusElement.fadeOut( function () { statusElement.html( status ).fadeIn( callback ); } ) };
        setStatus( "Getting wikitext..." );
        var wikitext;
        $.getJSON(
            mw.util.wikiScript('api'),
            {
                format: 'json',
                action: 'query',
                prop: 'revisions',
                rvprop: 'content',
                rvlimit: 1,
                titles: "Wikipedia:Requests for adminship/Optional RfA candidate poll"
            }
        ).done( function ( data ) {
            try {
                var pageId = Object.keys(data.query.pages)[0];
                wikitext = data.query.pages[pageId].revisions[0]['*'];
                setStatus( "Got this page's wikitext, processing..." );

                var headerMatch = ( new RegExp( "(==\\s*" + username + "\\s*==)" ) ).exec( wikitext );
                var sectionTextPlus = wikitext.slice( headerMatch.index + headerMatch[1].length );

                var sectionText = sectionTextPlus;
                if( sectionTextPlus.indexOf( "==" ) >= 0 ) {
                    sectionText = sectionText.substring( 0, sectionText.indexOf( "==" ) );
                }
                sectionText = sectionText.trim();

                // Make the new wikitext
                var rating = specificElement( username ).rating.val();
                var newLine = "*'''" + rating + "/10''' - " + specificElement( username ).comment.val() + " ~~~~";
                var newWikitext = wikitext.replace( sectionText, sectionText + "\n" + newLine );

                setStatus( "Wikitext processed. Saving..." );
                ( new mw.Api() ).postWithEditToken( {
                    action: "edit",
                    title: "Wikipedia:Requests for adminship/Optional RfA candidate poll",
                    summary: rating + "/10 for " + username + " ([[User:Enterprisey/orcp-helper|orcp-helper.js]])",
                    text: newWikitext
                } ).done ( function ( data ) {
                    if ( data && data.edit && data.edit.result && data.edit.result == 'Success' ) {
                        setStatus( "Rating saved! (<a href='javascript:void(0)' class='reload'>Reload</a>)" );
                        $( ":animated" ).promise().done( function() {
                            that.children( "span.status" ).children( "a" ).click( function () { document.location.reload( true ); } );
                        } );
                        specificElement( username ).cancel.text( "Done" );
                    } else {
                        setStatus( "While saving, the edit query returned an error. =(" );
                    }
                } ).fail ( function() {
                    setStatus( "While saving, the AJAX request failed." );
                } );
            } catch ( e ) {
                setStatus( "While getting the wikitext, there was an error." );
                console.log( "Content request error: " + e.message );
                console.log( "Content request response: " + JSON.stringify( data ) );
            }
        } ).fail( function () {
            setStatus( "While getting the wikitext, there was an AJAX error." );
        } );
    }
        
    // Function is called when dependencies are loaded and document is
    // ready.
    function onReady() {
        $( `<style>
        .orcp-rating {
            box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
            padding: 5px;
            padding-left: 19px;
            display: inline-block;
        }

        .orcp-rating input {
            padding: 4px 4px 4px 8px;
            height: 35px;
        }

        .orcp-rating input.invalid {
                box-shadow: 0px 0px 0px 2px #D11D13 inset;
                border-color: #D11D13;
        }

        .orcp-rating textarea {
            height: auto;
        }

        .orcp-rating textarea.mw-ui-input {
            min-height: 0;
        }

        .orcp-rating th {
            text-align: left;
        }
        </style>` ).appendTo( "head" );
        $( "h2:not(:contains('Instructions')) .mw-editsection" ).each( function ( index, element ) {
            var username = $( this ).parent().text().replace( /\[[\s\S]+\]/, "" );

            // We shouldn't be able to rate ourselves
            if( mw.config.get( "wgUserName" ) === username ) return;

            // We shouldn't be able to rate people twice
            var testLink = function ( link ) { return link.href.indexOf( mw.config.get( "wgUserName" ) ) >= 0; };
            var linksInComments = $( this ).parent().nextUntil( "h2" ).filter( "ul" ).children().children( "a" ).toArray();
            if( linksInComments.some( testLink ) ) return;
            $( this ).children( ".mw-editsection-bracket" ).last()
                .before( " | " )
                .before( makeActionLink( username ) );
        } );
    } // End function onReady

	if ( mw.config.get( 'wgPageName' ) === "Wikipedia:Requests_for_adminship/Optional_RfA_candidate_poll" ) {
	    mw.loader.load( "mediawiki.ui.input", "text/css" );
    	mw.loader.using( [ "mediawiki.api", "mediawiki.util" ] ).then( function () {
	    	$( document ).ready( onReady );
    	} );
	}
}( jQuery, mediaWiki ) );
//</nowiki>