User:Evad37/GAreviewHelper.js

// /* global vars */ var garhTalkpage, garhReview_number, topic, fullsubtopic, garhFatalError;

/* == Main function == */ $( function($) {

// Only run in talk namespace //if ( mw.config.get('wgNamespaceNumber') !== 1 ) return;

// Only run when there is a GA review transcluded and current user is the reviewer var $GAreviewHeadingSpan = $("span[id^='GA_Review']"); if ( $GAreviewHeadingSpan.length === 0 ) return; var GAreviewer = $GAreviewHeadingSpan.last.parent.next.next.next.find("b:contains('Reviewer')").first.next.text; console.log("GAreviewer = " + GAreviewer); if ( GAreviewer !== mw.config.get('wgUserName') ) return;

// Only run when there is either a GA nominee or GA template present var $GAnomineeTable = $("table.plainlinks.tmbox.tmbox-notice:contains('good article nominee')"); if ( $GAnomineeTable.length === 0 ) { var $GATable = $("table.plainlinks.tmbox.tmbox-notice:contains('good article criteria')"); console.log($GATable); console.log($GATable.length); if ( $GATable.length !== 0 ) { passOnlyHelper; return; } else { return; }	}	// Only run when a GA review has been started if ( $GAnomineeTable.find("a.external:contains('start review')").length !== 0 ) return;

// Get review page number, set talkpage and review page global vars var review_url = $GAnomineeTable.find("a").eq(3).attr("href"); garhReview_number = review_url.match(/\d+$/)[0]; garhTalkpage = mw.config.get('wgPageName'); //garhReviewpage = garhTalkpage + "/GA" + garhReview_number;

// Only run if current user is the reviewer //if ( $("b:contains('Reviewer')").last.next.text !== mw.config.get('wgUserName') ) return; // .last used because there might be multiple reviews transcluded on a talk page // Add action links var outer_div = " "; var actions_style = "width:65%; margin:auto; padding:0.3em 0; border:2px solid #fff; -moz-border-radius:8px; -webkit-border-radius:8px; border-radius:8px; text-align:center; cursor:pointer;"; var actions_text_style = "color:#fff;font-size:115%;font-weight:bold;"; var actions = " " + "" + outer_div + "Pass " + outer_div + "Fail " + outer_div + "Hold " + outer_div + "2nd Opinion " + " Processing... " + " " + 	" ";

$GAnomineeTable.children.append(actions);

$("#GARH_Pass").click( passGA ); $("#GARH_Fail").click( failGA ); $("#GARH_Hold").click( holdGA ); $("#GARH_2ndO").click( secOpGA );

});

/* == Common Functions == */

//Hide actions, show message function hideActions { $("div.GARH_action").hide; $("div.GARH_msg").show; }

//Show actions, hide message function showActions { $("div.GARH_action").show; $("div.GARH_msg").hide; }

function updateMsg(new_message, new_icon_url) { $("span#GARH_msg_text").text(new_message); if ( new_icon_url ) { $("img#GARH_msg_icon").attr("src", new_icon_url); } }

function prependMsg(message, icon_url, appendInstead) { if ( icon_url ) { message = "<img src='" + icon_url + "'/> " + message; }

if ( appendInstead ) { $("div.GARH_msg").append(message); } else { $("div.GARH_msg").prepend(message + " "); } }

function errorMsg(msg, error_code, error_result, isFatal) { var type, icon; if ( isFatal ) { garhFatalError = true; type = "Error: "; icon = "https://upload.wikimedia.org/wikipedia/en/thumb/1/15/Ambox_warning_pn.svg/20px-Ambox_warning_pn.svg.png"; } else { type = "Warning: "; icon = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Information_icon4.svg/20px-Information_icon4.svg.png"; }

var error_details; if ( error_code === "http" ) { error_details = " Error details: HTTP error: " + error_result.textStatus + " "; } else if ( error_code === "ok-but-empty" ) { error_details = " Error details: Error: Got an empty response from the server "; } else if ( error_code ) { error_details = " Error details: API error: " + fcode + " "; }

updateMsg(msg, icon); if ( error_details ) prependMsg(error_details, null, true);

}

function encodeSubtopic(input) { if ( input ) { return input.replace(/\W/g, ""); } else { return ""; } }

function openLastDiff(page) { window.open("https://en.wikipedia.org/w/index.php?title=" + mw.util.rawurlencode(page) + "&diff=cur&oldid=prev"); }

//Edit status parameter in GA nominee template on talk page function editStatus(new_status, edit_sum) { new mw.Api.get( {		action: 'query',		titles: garhTalkpage,		prop: [ 'revisions', 'info' ],		rvprop: 'content',		indexpageids: 1,		rawcontinue: ''	} ).done( function( result, jqXHR ) {		var id = result.query.pageids;		var contents = result.query.pages[ id ].revisions[ 0 ][ '*' ];		var nominee_template = contents.match(/{{\s*GA nominee[^}]*}}/i)[0];		var updated_nominee_template = nominee_template.replace(/\|\s*status\s*=[^|}]*/, "|status=" + new_status);		var updated_contents = contents.replace(/{{\s*GA nominee[^}]*}}/i, updated_nominee_template);

new mw.Api.postWithToken( 'edit', {			action: 'edit',			title: garhTalkpage,			text: updated_contents,			summary: edit_sum		} ).done( function( result, jqXHR ) {			//TODO: new messaging system		} ).fail( function( code, result ) {			errorMsg("Could not update {" + "{GA nominee}" + "} status", code, result, true);		} ); } ).fail( function( code, result ) { errorMsg("Could not read contents of talk page; could not update {" + "{GA nominee}" + "} status", code, result, true); } );

}

//Replace GA nominee template on talk page (preserving subtopic) function replaceNomineeTemplate(content_before, content_after, edit_sum, passed) { new mw.Api.get( {		action: 'query',		titles: garhTalkpage,		prop: [ 'revisions', 'info' ],		rvprop: 'content',		indexpageids: 1,		rawcontinue: ''	} ).done( function( result, jqXHR ) {		var id = result.query.pageids;		var contents = result.query.pages[ id ].revisions[ 0 ][ '*' ];		var nomineeTemplate = contents.match(/{{\s*GA nominee[^}]*}}/i)[0];		var subtopic = nomineeTemplate.match(/\|\s*subtopic\s*=[^|}]*/)[0].split("=")[1].trim;		var updated_contents = contents.replace(/{{\s*GA nominee[^}]*}}/i, content_before + subtopic + content_after);

if ( passed ) { updated_contents = replaceClass(updated_contents); }

new mw.Api.postWithToken( 'edit', {			action: 'edit',			title: garhTalkpage,			text: updated_contents,			summary: edit_sum		} ).done( function( result, jqXHR ) {				if ( passed ) {					prependMsg("Replaced {" + "{GA nominee}" + "} with {" + "{GA}" + "}", "https://upload.wikimedia.org/wikipedia/en/thumb/f/fb/Yes_check.svg/16px-Yes_check.svg.png");					listGA(subtopic);				} else {					prependMsg("Replaced {" + "{GA nominee}" + "} with {" + "{FailedGA}" + "}", "https://upload.wikimedia.org/wikipedia/en/thumb/f/fb/Yes_check.svg/16px-Yes_check.svg.png");					updateMsg("Finished!", "https://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Symbol_confirmed.svg/16px-Symbol_confirmed.svg.png");				}		} ).fail( function( code, result ) {			errorMsg("Could not replace {" + "{GA nominee}" + "}", code, result, true);		} );

} ).fail( function( code, result ) { errorMsg("Could not read contents of talk page; could not replace {" + "{GA nominee}" + "}", code, result, true); } );

}

/* == Pass-only version (helps with passing steps 3 and 6 only) == */ function passOnlyHelper { console.log("passOnlyHelper"); garhTalkpage = mw.config.get('wgPageName');

//Add a collapsible section with buttons [update class] and [list as GA] //TODO: Show in expanded state if previous revision had a GA nominee template

var garh_tools = " " + "<td colspan='2'>" + "<div id='GARH_tools' class='NavFrame collapsed' style='border:none;'> GAreviewHelper tools " + "<button onclick='passOnlyUpdateClass'>Update class to GA " + "<button onclick='passOnlyListGA'>List as GA " + "<div class='GARH_msg' style='padding: 0.5em; margin:0 0.3em 0.3em; background:#fff; display:none;'><img id='GARH_msg_icon' src='https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/Ajax-loader.gif/16px-Ajax-loader.gif'/> <span id='GARH_msg_text'>Processing... " + "   ";

console.log(garh_tools); $gaBox = $("table.plainlinks.tmbox.tmbox-notice:contains('good article criteria')"); $gaBox.children.append(garh_tools);

}

function passOnlyUpdateClass {

updateMsg("Updating class...", "https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/Ajax-loader.gif/16px-Ajax-loader.gif"); $("div.GARH_msg").show;

new mw.Api.get( {		action: 'query',		titles: garhTalkpage,		prop: [ 'revisions', 'info' ],		rvprop: 'content',		indexpageids: 1,		rawcontinue: ''	} ).done( function( result, jqXHR ) {		var id = result.query.pageids;		var contents = result.query.pages[ id ].revisions[ 0 ][ '*' ];		var updated_contents = replaceClass(contents);

new mw.Api.postWithToken( 'edit', {			action: 'edit',			title: garhTalkpage,			text: updated_contents,			summary: 'Reassessed as GA-class (using GAreviewHelper)'		} ).done( function( result, jqXHR ) {				updateMsg("Finished!", "https://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Symbol_confirmed.svg/16px-Symbol_confirmed.svg.png");				prependMsg(" (<a onclick='location.reload' style='cursor:pointer;'>Reload this page</a> &middot; <a onclick=openLastDiff('" + garhTalkpage + "') style='cursor:pointer;'>Show diff</a>) ", null, true);		} ).fail( function( code, result ) {			errorMsg("Could not update class", code, result, true);		} );

} ).fail( function( code, result ) { errorMsg("Could not read contents of talk page; could not update class", code, result, true); } );

}

function passOnlyListGA { updateMsg("Processing...", "https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/Ajax-loader.gif/16px-Ajax-loader.gif"); $("div.GARH_msg").show;

new mw.Api.get( {		action: 'query',		titles: garhTalkpage,		prop: [ 'revisions', 'info' ],		rvprop: 'content',		indexpageids: 1,		rawcontinue: ''	} ).done( function( result, jqXHR ) {		var id = result.query.pageids;		var contents = result.query.pages[ id ].revisions[ 0 ][ '*' ];		var gaTemplate = contents.match(/{{\s*GA[^}]*}}/i)[0];		var topic = gaTemplate.match(/\|\s*topic\s*=[^|}]*/)[0].split("=")[1].trim;

listGA(topic);

} ).fail( function( code, result ) { errorMsg("Could not read contents of talk page; could not list article", code, result, true); } );

}

/* == PASS-related functions == */ function passGA { hideActions; replaceNomineeTemplate("", "Passed GA review (using GAreviewHelper)", true); }

function replaceClass(content) { var topSection, restOfPage; var firstHeadingIndex = content.indexOf("=="); if ( firstHeadingIndex !== -1 ) { topSection = content.slice(0, firstHeadingIndex); restOfPage = content.slice(firstHeadingIndex); } else { topSection = content; restOfPage = ""; }

//Replace |class=[anything] with |class=GA var newContent = topSection.replace(/\|(?:\s|\n)*class(?:\s|\n)*=[^\|}]*/gm, "|class=GA");

//Add |class=GA to likely wikiproject banners without a class parameter var likelyWikiprojectBanners = topSection.match(/{{(?:wp|wikiproject)[^}]*}}/gi); if ( likelyWikiprojectBanners ) { likelyWikiprojectBanners.forEach( function(banner, index) {			if ( !banner.match(/\|(?:\s|\n)*class(?:\s|\n)*=/g) ) {				// banner does not already have class parameter				var updatedBanner = banner.replace(/}}/, "|class=GA}}");				newContent = newContent.replace(banner, updatedBanner);			}		}); }	return newContent + restOfPage; }

// List on appropriate WP:GA subpage, in user-specified section function listGA(subtopic) { //Find topic from subtopic new mw.Api.get( {		action: "expandtemplates",		text: "{" + "{GA/Topic|" + subtopic + "}" + "}",		prop: "wikitext"	} ).done( function( result, jqXHR ) {		topic = result.expandtemplates.wikitext;

//Find full-form subtopic new mw.Api.get( {			action: "expandtemplates",			text: "{" + "{GA/Subtopic|" + subtopic + "}" + "}",			prop: "wikitext"		} ).done( function( result, jqXHR ) {			fullsubtopic = result.expandtemplates.wikitext;			 //console.log("subtopic = " + subtopic);			  //console.log("topic = " + topic);			  //console.log("fullsubtopic = " + fullsubtopic);			getListSections(topic);		} ).fail( function( code, result ) {			errorMsg("Could not find GA subtopic for " + subtopic, code, result, true);		} );

} ).fail( function( code, result ) { errorMsg("Could not find GA topic for " + subtopic, code, result, true); } ); }

function getListSections(topic) { new mw.Api.get( {		action: 'query',		titles: 'Wikipedia:Good_articles/' + topic.replace(/\s/g, "_"),		prop: [ 'revisions', 'info' ],		rvprop: 'content',		indexpageids: 1,		rawcontinue: ''	} ).done( function( result, jqXHR ) {		var id = result.query.pageids;		//make all headings level 2 so sections can be counted, remove any hidden anchors		var contents = result.query.pages[ id ].revisions[ 0 ][ '*' ].replace(/^={6}/gm, "===== =").replace(/={6}$/gm, "= ==== =").replace(/={3,5}/g, "==").replace(/{{anchor[^}]*}}/g,"");		 //console.log(contents);		//make an array of subtopics		var _subtopics = contents.match(/\[\[#[^|\]]*/g);		  //console.log(_subtopics);		var subtopics = _subtopics.map(function(value) { return value.slice(3); });		//make an array of the headings				var headings = contents.split("==").filter(function(value,index) { return index % 2; });		for ( var ii=0; ii<headings.length; ii++ ) {			if ( ii<headings.length-1 && headings[ii+1].includes("=") && !headings[ii].includes("=") ) {				headings[ii] = false;			} else {				headings[ii] = headings[ii].replace(/=/g, "");			}		}		//make html string with options for to select		var options;		if ( topic === fullsubtopic ) {			options = "<span id='garh_choose'> To list at WP:GA, choose a sub-section of " + topic + ": ";		} else {			options = "<span id='garh_choose'>  To list at WP:GA, choose a sub-section of " + topic + " / " + fullsubtopic + ": ";		}		var count_subtopics = 0;		headings.forEach(function(value,index) { if ( value && value.trim !== " " ) { var section_num = index + 1; options = options + " <button onclick=doListing(" + section_num + ")>" + value.trim + " "; } else if ( index !== 0 ) { if ( value ) { options = options + " <span style='display:none;' id='" + encodeSubtopic(subtopics[count_subtopics]) + "' class='garh_choose_subtopic'>"; }				count_subtopics++; }		});		options = options + " ";		updateMsg("Waiting");		prependMsg(options, null, true);		var span_fullsubtopic_id = "span#" + encodeSubtopic(fullsubtopic);		if ( $(span_fullsubtopic_id).length !== 0 ) {			$(span_fullsubtopic_id).show;		} else {			$("span.garh_choose_subtopic").show;		}	} ).fail( function( code, result ) {		errorMsg("Could not find GA subsections for " + subtopic, code, result, true);	} ); }

function doListing(edit_sec_num) { $("span#garh_choose").hide; updateMsg("Listing..."); // Get current list items, add new page var listpage_title = "User:Evad37/sandbox/Wikipedia:Good_articles/" + topic.replace(/\s/g, "_"); new mw.Api.get( {		action: 'query',		titles: listpage_title,		prop: [ 'revisions', 'info' ],		rvprop: 'content',		indexpageids: 1,		rawcontinue: ''	} ).done( function( result, jqXHR ) {		var id = result.query.pageids;		//make all headings level 2 so sections can be counted, remove any hidden anchors		var contents = result.query.pages[ id ].revisions[ 0 ][ '*' ].replace(/^={6}/gm, "===== =").replace(/={6}$/gm, "= =====").replace(/={3,5}/g, "==").split("==");		var section_heading = contents[(edit_sec_num * 2) - 1].trim;		 //console.log("section_heading =" + section_heading);		var section_contents = contents[edit_sec_num * 2].trim;		  //console.log("section_contents = " + section_contents);		var section_array = section_contents.split("\n");		var section_pre  = "";		var section_post = "";		var section_link_array = [];		var seen_a_link = false;		section_array.forEach(function(val,ii) { if ( !val.includes("[[") ) {				if ( !seen_a_link ) {					section_pre = section_pre + val + "\n";				} else if ( !val.match(/\d(?:\s| )article/) ) {					section_post = section_post + val + "\n";				}			} else {				section_link_array.push(val);				seen_a_link = true;			}		});		 //console.log("popped = " + popped);		  //console.log(section_array);

// format new entry - check if article uses displaytitle or defaultsort and adjust accordingly // var new mw.Api.get( {			action: 'query',			titles: garhTalkpage.replace("User_talk:", "User:"),			prop: [ 'pageprops', 'info' ],			inprop: 'displaytitle',			ppprop: 'defaultsort',			indexpageids: 1,			rawcontinue: 		} ).done( function( result, jqXHR ) {			var id = result.query.pageids;			var article_wikilink;			var displayTitle = result.query.pages[ id ].displaytitle;			var articleTitle = result.query.pages[ id ].title;			var defaultSort = result.query.pages[ id ].pageprops.defaultsort;			if ( displayTitle !== articleTitle ) {				article_wikilink = "" + displayTitle.replace(/<\/?i>/g, "") + "";			} else if ( defaultSort ) {				article_wikilink = "" + defaultSort + "";			} else {				article_wikilink = "" + articleTitle + "";			}

var number_of_articles = section_link_array.push(article_wikilink + " –"); section_link_array.sort(function(a, b){				var article_a = a.replace(/(?:\[\[|\]\]|[^\|\]\[]*\||)/g, "").toLowerCase.replace(/^the\s/, "");				var article_b = b.replace(/(?:\[\[|\]\]|[^\|\]\[]*\||)/g, "").toLowerCase.replace(/^the\s/, "");				var a_numbers, a_non_numbers, b_numbers, b_non_numbers, max_num_iterations, max_nonnum_iterations, ii;				if ( article_a.match(/^\d+/) && article_b.match(/^\d+/) ) {					// both contain (and start with) numbers...					// split into arrays of number-groups and non-number groups					a_numbers = article_a.match(/\d+/g);					a_non_numbers = article_a.match(/\D+/g);					b_numbers = article_b.match(/\d+/g);					b_non_numbers = article_b.match(/\D+/g);					// iterate through groups, sorting numerically if the numbers differ (e.g "10/11th army", "10/12th army")					// or sorting alphabetically if the non-numbers differ (e.g. (e.g "10/11th army", "10/11th brigade") // but first determine max iterations so that we don't attempt to access non-existant array values if ( a_numbers.length < b_numbers.length ) { max_num_iterations = a_numbers.length; } else { max_num_iterations = b_numbers.length; }					if ( a_non_numbers && b_non_numbers ) { if ( a_non_numbers.length < b_non_numbers.length ) { max_nonnum_iterations = a_non_numbers.length; } else { max_nonnum_iterations = b_non_numbers.length; }					} else { max_nonnum_iterations = 0; }					//loop through, comparing for (ii=0; ii<max_num_iterations; ii++) { // check if different numbers, sort numerical if ( a_numbers[ii] !== b_numbers[ii] ) return parseInt(a_numbers[ii])-parseInt(b_numbers[ii]); // check if different non-numbers, sort alpha if ( ii < max_nonnum_iterations && a_non_numbers[ii] !== b_non_numbers[ii] ) return a_non_numbers[ii].localeCompare(b_non_numbers[ii]); }					// if end of the loop is reached, sort alpha (should sort the shorter one first) return article_a.localeCompare(article_b);

} else if ( article_a.match(/\D+(?=\d)/) && article_b.match(/\D+(?=\d)/) && article_a.match(/\D+(?=\d)/)[0]===article_b.match(/\D+(?=\d)/)[0] ) { // both start with same (non-digits) string, and then have some numbers... // split into arrays of number-groups and non-number groups a_numbers = article_a.match(/\d+/g); a_non_numbers = article_a.match(/\D+/g); b_numbers = article_b.match(/\d+/g); b_non_numbers = article_b.match(/\D+/g); // iterate through groups, sorting numerically if the numbers differ (e.g "US 10/11th army", "US 10/12th army") // or sorting alphabetically if the non-numbers differ (e.g. (e.g "US 10/11th army", "US 10/11th brigade")					// but first determine max iterations so that we don't attempt to access non-existant array values					if ( a_non_numbers.length < b_non_numbers.length ) {						max_nonnum_iterations = a_non_numbers.length;					} else {						max_nonnum_iterations = b_non_numbers.length;					}					if ( a_numbers.length < b_numbers.length ) {						max_num_iterations = a_numbers.length;					} else {						max_num_iterations = b_numbers.length;					}					//loop through, comparing 					for (ii=0; ii<max_nonnum_iterations; ii++) {						// check if different non-numbers, sort alpha						if ( a_non_numbers[ii] !== b_non_numbers[ii] ) return a_non_numbers[ii].localeCompare(b_non_numbers[ii]);						// check if different numbers, sort numerical						if ( ii < max_num_iterations && a_numbers[ii] !== b_numbers[ii] ) return parseInt(a_numbers[ii])-parseInt(b_numbers[ii]); }					// if end of the loop is reached, sort alpha (should sort the shorter one first) return article_a.localeCompare(article_b); } else { // no digits - just sort alphabetically return article_a.localeCompare(article_b); }			});			var updated_section_contents = section_pre + section_link_array.join("\n") + "\n (" + number_of_articles + " articles) \n" + section_post;

new mw.Api.postWithToken( 'edit', {				action: 'edit',				title: listpage_title,				section: edit_sec_num,				text: "=====" + section_heading + "=====\n" + updated_section_contents.trim,				summary: "Listing " + garhTalkpage.slice(5) + " (using GAreviewHelper)"			} ).done( function( result, jqXHR ) {				updateMsg("Finished!", "https://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Symbol_confirmed.svg/16px-Symbol_confirmed.svg.png");				prependMsg(" (<a onclick='location.reload' style='cursor:pointer;'>Reload this page</a> &middot; <a onclick=openLastDiff('" + listpage_title + "') style='cursor:pointer;'>Show listing diff</a>) ", null, true);			} ).fail( function( code, result ) {				errorMsg("Could not update GA listing", code, result, true);			} ); } ).fail( function( code, result ) { errorMsg("Could not find article", code, result, true); } );

} ).fail( function( code, result ) { errorMsg("Could not find '" + "Wikipedia:Good_articles/" + topic.replace(/\s/g, "_") + "'; could not update GA listing", code, result, true); } );

}

/* == FAIL-related functions == */ function failGA { hideActions; var append_comment = prompt("Enter result/comment"); if ( append_comment === null ) { showActions; return; } else if ( append_comment === "" ) { updateMsgText("Working..."); } else { updateMsgText("Working..."); appendToReview(append_comment, "Fail (using GAreviewHelper)"); }	replaceNomineeTemplate("", "Failed GA review (using GAreviewHelper)", false); }

/* == HOLD-related functions == */ function holdGA { hideActions; var append_comment = prompt("Enter comment"); if ( append_comment === null ) { showActions; return; } else if ( append_comment === "" ) { updateMsgText("Working..."); } else { updateMsgText("Working..."); appendToReview(append_comment, "On hold (using GAreviewHelper)"); }	editStatus("onhold", "GA review on hold (using GAreviewHelper)"); }

/* == 2nd Opinion-related functions == */ function secOpGA { hideActions; var append_comment = prompt("Specify in what way you are looking for a second opinion"); if ( append_comment === null ) { showActions; return; } else if ( append_comment === "" ) { updateMsgText("Working..."); } else { updateMsgText("Working..."); appendToReview(append_comment, "Second opinion requested (using GAreviewHelper)"); }	editStatus("2ndopinion", "Second opinion requested for GA review (using GAreviewHelper)"); } //