User:Enterprisey/delsort.js
Appearance
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:Enterprisey/delsort. |
//<nowiki>
( function ( $, mw ) {
mw.loader.load( "jquery.chosen" );
mw.loader.load( "mediawiki.ui.input", "text/css" );
var afdcCategories = { "m": "Media and music", "o": "Organization, corporation, or product", "b": "Biographical", "s": "Society topics", "w": "Web or Internet", "g": "Games or sports", "t": "Science and technology", "f": "Fiction and the arts", "p": "Places and transportation", "i": "Indiscernible or unclassifiable topic", "u": "Not sorted yet" };
var ADVERTISEMENT = " ([[User:Enterprisey/delsort|assisted]])";
var currentAfdcCat = "";
var currentDelsortCategories = [];
if ( mw.config.get( "wgPageName" ).indexOf("Wikipedia:Articles_for_deletion/") != -1 &&
mw.config.get( "wgPageName" ).indexOf("Wikipedia:Articles_for_deletion/Log/") == -1) {
var portletLink = mw.util.addPortletLink("p-cactions", "#", "Delsort", "pt-delsort", "Perform deletion sorting");
// Load list of delsort categories
var delsortCategoriesPromise = $.ajax( {
url: "https://en.wikipedia.org/w/index.php?action=raw&title=" + encodeURIComponent( "Wikipedia:WikiProject Deletion sorting/Computer-readable.json" ) + "&maxage=86400&smaxage=86400",
dataType: "json"
} )
$( portletLink ).click( function ( e ) {
e.preventDefault();
// Validation for new custom fields
var validateCustomCat = function ( container ) {
var categoryName = container.children( "input" ).first().val();
$.getJSON(
mw.util.wikiScript("api"),
{
format: "json",
action: "query",
prop: "pageprops",
titles: "Wikipedia:WikiProject Deletion sorting/" + categoryName
}
).done( function ( data ) {
var setStatus = function ( status ) {
var text = "Not sure";
var imageSrc = "https://upload.wikimedia.org/wikipedia/commons/a/ad/Question_mark_grey.png";
switch( status ) {
case "d":
text = "Doesn't exist";
imageSrc = "https://upload.wikimedia.org/wikipedia/commons/5/5f/Red_X.svg";
break;
case "e":
text = "Exists";
imageSrc = "https://upload.wikimedia.org/wikipedia/commons/1/16/Allowed.svg";
break;
}
container.children( ".category-status" ).empty()
.append( $( "<img>", { "src": imageSrc,
"style": "padding: 0 5px; width: 20px; height: 20px" } ) )
.append( text );
};
if( data && data.query && data.query.pages ) {
if( data.query.pages.hasOwnProperty( "-1" ) ) {
setStatus( "d" );
} else {
setStatus( "e" );
}
} else {
setStatus( "n" );
}
} );
};
// Define a function to add a new custom field, used below
var addCustomField = function ( e ) {
$( "<div>" )
.appendTo( "#delsort-td" )
.css( "width", "100%" )
.css( "margin", "0.25em auto" )
.append( $( "<input>" )
.attr( "type", "text" )
.addClass( "mw-ui-input mw-ui-input-inline custom-delsort-field" )
.change( function ( e ) {
validateCustomCat( $( this ).parent() );
} ) )
.append( $( "<span>" ).addClass( "category-status" ) )
.append( " (" )
.append( $( "<img>", { "src": "https://upload.wikimedia.org/wikipedia/commons/a/a2/Crystal_128_reload.svg",
"style": "width: 15px; height: 15px; cursor: pointer" } )
.click( function ( e ) {
validateCustomCat( $( this ).parent() );
} ) )
.append( ")" )
.append( $( "<button>" )
.addClass( "mw-ui-button mw-ui-destructive mw-ui-quiet" )
.text( "Remove" )
.click( function () {
$( this ).parent().remove();
} ) );
};
$( "#mw-content-text" ).prepend(
'<div style="border: thin solid rgb(197, 197, 197); box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.25); border-radius: 3px; padding: 5px; position: relative;" id="delsort">' +
' <div id="delsort-title" style="font-size: larger; font-weight: bold; text-align: center;">Select a deletion sorting category</div>' +
' <table style="margin: 2em auto; border-collapse: collapse;" id="delsort-table">' +
' <tr style="font-size: larger"><th>AFDC</th><th>DELSORT</th></tr>' +
' <tr>' +
' <td style="padding-right: 10px;">' +
' <table id="afdc">' +
' </table>' +
' </td>' +
' <td style="border-left: solid black thick; padding-left: 10px; vertical-align: top;" id="delsort-td">' +
' <select multiple="multiple" data-placeholder="Select a deletion sorting category..."></select>' +
' <button id="add-custom-button" class="mw-ui-button mw-ui-progressive mw-ui-quiet">Add custom</button>' +
' </td>' +
' </tr>' +
' </table>' +
' <button style="position: absolute; top: 5px; right: 5px;" id="close-button" class="mw-ui-button mw-ui-destructive mw-ui-quiet">Close</button>' +
'</div>' );
$( "#add-custom-button" ).click( addCustomField );
$( "#close-button" ).click( function () { $( "#delsort" ).remove(); } );
var afdcHtml = "";
Object.keys( afdcCategories ).forEach( function ( code, i ) {
if ( i % 2 === 0 ) afdcHtml += "<tr>";
afdcHtml += "<td><input type='radio' name='afdc' value='" + code + "' id='afdc-" + code + "' /><label for='afdc-" + code + "'>" + afdcCategories[ code ] + "</label></td>";
if ( i % 2 !== 0 ) afdcHtml += "</tr>";
} );
// If there are an odd number of AFDC cats, we need to close off the last row
if ( Object.keys( afdcCategories ).length % 2 !== 0 ) afdcHtml += "</tr>";
$( "#afdc" ).html( afdcHtml );
// Build the deletion sorting categories
delsortCategoriesPromise.done( function ( delsortCategories ) {
$.each( delsortCategories, function ( groupName, categories ) {
var group = $( "<optgroup>" )
.appendTo( "#delsort select" )
.attr( "label", groupName );
$.each( categories, function ( index, category ) {
group.append( $( "<option>" )
.val( category )
.text( category )
.addClass( "delsort-category" ) );
} );
} );
getWikitext( mw.config.get( "wgPageName" ) ).then( function ( wikitext ) {
autofillAfdc( wikitext );
// Autofill the delsort box
var DELSORT_RE = /:<small class="delsort-notice">(.+?)<\/small>/g;
var DELSORT_LIST_RE = /\[\[Wikipedia:WikiProject Deletion sorting\/(.+?)\|.+?\]\]/;
var delsortMatch;
var delsortListMatch;
do {
delsortMatch = DELSORT_RE.exec( wikitext );
if( delsortMatch !== null ) {
delsortListMatch = DELSORT_LIST_RE.exec( delsortMatch[1] );
if( delsortListMatch !== null ) {
currentDelsortCategories.push( delsortListMatch[1] );
var delsortOption = document.querySelector( "option.delsort-category[value='" + delsortListMatch[1] + "']" );
if( delsortOption ) {
delsortOption.selected = true;
}
}
}
} while( delsortMatch );
// Now that we've updated the underlying <select>, ask Chosen to
// update the visible search box
$( "#delsort select" ).trigger( "chosen:updated" );
} ); // end getWikitext
} ); // end delsortCategoriesPromise
// Initialize the special chosen.js select box
// (some code stolen from http://stackoverflow.com/a/27445788)
$( "#delsort select" ).chosen();
$( "#delsort .chzn-container" ).css( "text-align", "left" );
// Add the button that triggers sorting
$( "#delsort" ).append( $( "<div>" )
.css( "text-align", "center" )
.append( $( "<button> ")
.addClass( "mw-ui-button" )
.addClass( "mw-ui-progressive" )
.attr( "id", "sort-button" )
.text( "Save changes" )
.click( function () {
// Make a status list
$( "#delsort" ).append( $( "<ul> ")
.attr( "id", "status" ) );
// Build a list of categories
var categories = $( "#delsort select" ).val() || [];
$( ".custom-delsort-field" ).each( function ( index, element ) {
categories.push( $( element ).val() );
} );
categories = categories.filter( Boolean ); // remove empty strings
categories = removeDups( categories );
// Only allow categories that aren't already there
categories = categories.filter( function ( elem ) {
return currentDelsortCategories.indexOf( elem ) < 0;
} );
// Obtain the target AFDC category, brought to you by http://stackoverflow.com/a/24886483/1757964
var afdcTarget = document.querySelector("input[name='afdc']:checked").value;
// Actually do the delsort
saveChanges( categories, afdcTarget );
} ) ) );
} );
} // End if ( mw.config.get( "wgPageName" ).indexOf('Wikipedia:Articles_for_deletion/') ... )
/*
* Autofills the AFDC radio button group based on the current
* page's wikitext
*/
function autofillAfdc( wikitext ) {
var regexMatch = /REMOVE THIS TEMPLATE WHEN CLOSING THIS AfD(?:\|(.*))?}}/.exec( wikitext );
if ( regexMatch ) {
var templateParameter = regexMatch[1];
if ( templateParameter ) {
currentAfdcCat = templateParameter;
if ( templateParameter.length === 1 ) {
var currentClass = templateParameter.toLowerCase();
$( "#afdc-" + currentClass ).prop( "checked", true );
}
}
}
}
/*
* Saves the changes to the current discussion page by adding delsort notices (if applicable) and updating the AFDC cat
*/
function saveChanges( cats, afdcTarget ) {
var changingAfdcCat = currentAfdcCat.toLowerCase() !== afdcTarget;
// Indicate to the user that we're doing some deletion sorting
$( "#delsort-table" ).remove();
$( "#delsort #sort-button" )
.text( "Sorting " + ( changingAfdcCat ? "and categorizing " : "" ) + "discussion..." )
.prop( "disabled", true )
.fadeOut( 400, function () {
$( this ).remove();
} );
var categoryTitleComponent = ( cats.length === 1 ) ? ( "the \"" + cats[0] + "\" category" ) : ( cats.length + " categories" );
var afdcTitleComponent = changingAfdcCat ? " and categorizing it as " + afdcCategories[ afdcTarget ] : "";
$( "#delsort-title" )
.html( "Sorting discussion into " + categoryTitleComponent + afdcTitleComponent + "<span id=\"delsort-dots\"></span>" );
// Start the animation, using super-advanced techniques
var animationInterval = setInterval( function () {
$( "#delsort-dots" ).text( $( "#delsort-dots" ).text() + "." );
if( $( "#delsort-dots" ).text().length > 3 ) {
$( "#delsort-dots" ).text( "" );
}
}, 600 );
// Place (a) notification(s) on the discussion and update its AFDC cat
var editDiscussionDeferred = postDelsortNoticesAndUpdateAfdc( cats, afdcTarget );
// List the discussion at the DELSORT pages
var deferreds = cats.map( listAtDelsort );
// We still have to wait for the discussion to be edited
deferreds.push( editDiscussionDeferred );
// When everything's done, say something
$.when.apply( $, deferreds ).then( function () {
// Call the done hook
if( window.delsortDoneHook ) {
window.delsortDoneHook();
}
// We're done!
$( "#delsort-title" )
.text( "Done " + ( changingAfdcCat ? "updating the discussion's AFDC category and " : "" ) + "sorting discussion into " + categoryTitleComponent + "." );
showStatus( "<b>Done!</b> " + ( changingAfdcCat ? "The discussion's AFDC was updated and it was" : "Discussion was" ) + " sorted into " + categoryTitleComponent + ". (" )
.append( $( "<a>" )
.text( "reload" )
.attr( "href", "#" )
.click( function () { document.location.reload( true ); } ) )
.append( ")" );
clearInterval( animationInterval );
} );
}
/*
* Adds a new status to the status list, and returns the newly-displayed element.
*/
function showStatus( newStatus ) {
return $( "<li>" )
.appendTo( "#delsort ul#status" )
.html( newStatus );
}
/*
* Adds some notices to the discussion page that this discussion was sorted.
*/
function postDelsortNoticesAndUpdateAfdc( cats, afdcTarget ) {
var changingAfdcCat = currentAfdcCat.toLowerCase() !== afdcTarget,
deferred = $.Deferred(),
statusElement = showStatus( "Updating the discussion page..." );
getWikitext( mw.config.get( "wgPageName" ) ).then( function ( wikitext ) {
try {
statusElement.html( "Processing wikitext..." );
// Process wikitext
// First, add delsort notices
wikitext += createDelsortNotices( cats );
// Then, update the AFDC category
var afdcMatch = wikitext.match( /REMOVE THIS TEMPLATE WHEN CLOSING THIS AfD/ );
if ( afdcMatch && afdcMatch[ 0 ] ) {
var afdcMatchIndex = wikitext.indexOf( afdcMatch[ 0 ] ) + afdcMatch[ 0 ].length,
charAfterTemplateName = wikitext[ afdcMatchIndex ];
if ( charAfterTemplateName === "}" ) {
wikitext = wikitext.slice( 0, afdcMatchIndex ) + "|" + afdcTarget.toUpperCase() + wikitext.slice( afdcMatchIndex );
} else if ( charAfterTemplateName === "|" ) {
wikitext = wikitext.replace( "|" + currentAfdcCat + "}}", "|" + afdcTarget.toUpperCase() + "}}" );
}
}
statusElement.html( "Processed wikitext. Saving..." );
var catPlural = ( cats.length === 1 ) ? "" : "s";
$.ajax( {
url: mw.util.wikiScript( "api" ),
type: "POST",
dataType: "json",
data: {
format: "json",
action: "edit",
title: mw.config.get( "wgPageName" ),
summary: "Updating nomination page with notices" + ( changingAfdcCat ? " and new AFDC cat" : "" ) + ADVERTISEMENT,
token: mw.user.tokens.get( "csrfToken" ),
text: wikitext
}
} ).done ( function ( data ) {
if ( data && data.edit && data.edit.result && data.edit.result == "Success" ) {
statusElement.html( cats.length + " notice" + catPlural + " placed on the discussion!" );
if ( changingAfdcCat ) {
if ( currentAfdcCat ) {
var formattedCurrentAfdcCat = currentAfdcCat.length === 1 ? afdcCategories[ currentAfdcCat.toLowerCase() ] : currentAfdcCat;
showStatus( "Discussion's AFDC category was changed from " + formattedCurrentAfdcCat + " to " + afdcCategories[ afdcTarget ] + "." );
} else {
showStatus( "Discussion categorized under " + afdcCategories[ afdcTarget ] + " with AFDC." );
}
}
deferred.resolve();
} else {
statusElement.html( "While editing the current discussion page, the edit query returned an error. =(" );
deferred.reject();
}
} ).fail ( function() {
statusElement.html( "While editing the current discussion page, the AJAX request failed." );
deferred.reject();
} );
} catch ( e ) {
statusElement.html( "While getting the current page content, there was an error." );
console.log( "Current page content request error: " + e.message );
deferred.reject();
}
} ).fail( function () {
statusElement.html( "While getting the current content, there was an AJAX error." );
deferred.reject();
} );
return deferred;
}
/*
* Turns a list of delsort categories into a number of delsort template notice substitutions.
*/
function createDelsortNotices( cats ) {
if ( Array.isArray(cats) && ! cats.length ) return '';
var appendText = "\n{{subst:Deletion sorting/multi";
cats.forEach( function ( cat ) {
appendText += "|" + cat;
} );
return appendText + "|sig=~~" + "~~}}"; // string concat to prevent it from being transformed into my signature
}
/*
* Adds a listing at the DELSORT page for the category.
*/
function listAtDelsort( cat ) {
// Make a status element just for this category
var statusElement = showStatus( "Listing this discussion at DELSORT/" +
cat + "..." );
// Clarify our watchlist behavior for this edit
var allowedWatchlistBehaviors = ["watch", "unwatch", "preferences",
"nochange"];
var watchlistBehavior = "nochange"; // no watchlist change by default
if( window.delsortWatchlist && allowedWatchlistBehaviors.indexOf(
window.delsortWatchlist.toLowerCase() ) >= 0 ) {
watchlistBehavior = window.delsortWatchlist.toLowerCase();
}
var listTitle = "Wikipedia:WikiProject Deletion sorting/" + cat;
// First, get the current wikitext for the DELSORT page
return $.getJSON(
mw.util.wikiScript("api"),
{
format: "json",
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
rvlimit: 1,
titles: listTitle,
redirects: "true",
formatversion: 2,
}
).then( function ( data ) {
var wikitext = data.query.pages[0].revisions[0].slots.main.content;
var properTitle = data.query.pages[0].title;
try {
statusElement.html( "Got the DELSORT/" + cat + " listing wikitext, processing..." );
// Actually edit the content to include the new listing
var newDelsortContent = wikitext.replace("directly below this line -->", "directly below this line -->\n\{\{" + mw.config.get("wgPageName") + "\}\}");
// Then, replace the DELSORT listing with the new content
$.ajax( {
url: mw.util.wikiScript( "api" ),
type: "POST",
dataType: "json",
data: {
format: "json",
action: "edit",
title: properTitle,
summary: "Listing [[" + mw.config.get("wgPageName") + "]]" + ADVERTISEMENT,
token: mw.user.tokens.get( "csrfToken" ),
text: newDelsortContent,
watchlist: watchlistBehavior
}
} ).done ( function ( data ) {
if ( data && data.edit && data.edit.result && data.edit.result == "Success" ) {
statusElement.html( "Listed page at <a href=" + mw.util.getUrl( listTitle ) + ">the " + cat + " deletion sorting list</a>!" );
} else {
statusElement.html( "While listing at DELSORT/" + cat + ", the edit query returned an error. =(" );
}
} ).fail ( function() {
statusElement.html( "While listing at DELSORT/" + cat + ", the ajax request failed." );
} );
} catch ( e ) {
statusElement.html( "While getting the DELSORT/" + cat + " content, there was an error." );
console.log( "DELSORT content request error: " + e.message );
//console.log( "DELSORT content request response: " + JSON.stringify( data ) );
}
} ).fail( function () {
statusElement.html( "While getting the DELSORT/" + cat + " content, there was an AJAX error." );
} );
}
/**
* Gets the wikitext of a page with the given title (namespace required).
*/
function getWikitext( title ) {
return $.getJSON(
mw.util.wikiScript("api"),
{
format: "json",
action: "query",
prop: "revisions",
rvprop: "content",
rvslots: "main",
rvlimit: 1,
titles: title,
formatversion: 2,
}
).then( function ( data ) {
return data.query.pages[0].revisions[0].slots.main.content;
} );
}
/**
* Removes duplicates from an array.
*/
function removeDups( arr ) {
var obj = {};
for( var i = 0; i < arr.length; i++ ) {
obj[arr[i]] = 0;
}
return Object.keys( obj );
}
}( jQuery, mediaWiki ) );
//</nowiki>