User:HouseBlaster/MoveToDraft.js

/****************************************************************************** MoveToDraft - Version 2.5.7.1 - A script to move articles to draft space, including cleanup and author notification. - Moves page to draftspace - Checks if any files used are non-free - Checks if any redirects pointed to the page - Comments out non-free files, turn categories into links, add afc draft template, add redirects - Adds notification message on author talk page - Updates talk page banners - Logs draftification in user subpage - HouseBlaster's personal version: allows editing of the textbox, removes some headers. Otherwise, it functions the exact same way as User:MPGuy2824/MoveToDraft.js. derived from https://en.wikipedia.org/wiki/User:MPGuy2824/MoveToDraft.js /* jshint laxbreak: true, undef: true, maxerr:999, esversion:6 */ /* globals console, window, document, $, mw, OO */ // $.when(	// Resource loader modules	mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.Title']),	// Page ready	$.ready ).then(function { /* ========== Config ======================================================= */ var config = {	// MediaWiki configuration values	mw: mw.config.get( [ "wgArticleId", "wgCurRevisionId", "wgPageName", "wgUserGroups", "wgUserName", "wgMonthNames", "wgNamespaceNumber", "wgTitle" ] ),	// Script info	script: {		// For edit summaries		location: "User:HouseBlaster/MoveToDraft.js",		version: "2.5.7.1"	} };

/* ========== API ========================================================== */ var API = new mw.Api( {	ajax: {		headers: { 			"Api-User-Agent": "MoveToDraft/" + config.script.version + 				" ( https://en.wikipedia.org/wiki/User:MPGuy2824/MoveToDraft )"		}	} } );

var moveToDraft = function {

/* ========== Additional config ============================================ */ // Wikitext strings config.wikitext = { "editsummary":	window.m2d_editsummary || window.m2d_rationale || "AFC draft", "logMsg":		"#$1 moved to $2 at ", "notification":	window.m2d_notification || "Thanks for creating $1. Unfortunately, it is not ready for publishing$3.\nYour article is now a draft where you can improve it undisturbed for a while.\n\nPlease see more information at Help:Unreviewed new page.\nWhen the article is ready for publication, please click on the \"Submit your draft for review!\" button at the top of the page. ~", "notification_heading": "$1 moved to draftspace", "rationale":	window.m2d_rationale || "Not ready for mainspace, incubate in draftspace" }; // Custom change tag to be applied to all M2D actions, create at Special:Tags config.changeTags = 'moveToDraft'; config.draftReasons = [ "it has no sources", "it needs more sources to establish notability", "it has too many problems of language or grammar", "it is a poor translation", "it is promotional and reads like an advertisement", "you may have a possible Conflict of Interest" ]; config.minimumAgeInMinutes = 60; config.maximumAgeInDays = 90;

config.doNotLog = window.m2d_doNotLog ? true : false; // Page data -- to be retreived later from api config.pagedata = {}; config.pagedata.previousDraftification = false; config.pagedata.contributors = {}; config.pagedata.notifyList = [];

// Helper functions // - prettify an encoded page title (or at least replace underscores with // spaces) var getPageText = function(p) { var t = mw.Title.newFromText( decodeURIComponent(p) ); if (t) { return t.getPrefixedText; } else { return p.replace(/_/g, " "); } };

/** * makeApiErrorMsg * * Makes an error message, suitable for displaying to a user, from the values * that the MediaWiki Api passes to the failure callback function, e.g. * `new mw.Api.get(queryObject}).done(successCallback).fail(failureCallback)` * * @param {string} code * First paramater passed to failure callback function. * @param {jQuery.jqXHR} jqxhr * Second paramater passed to failure callback function. * @return {string} Error message details, with in a format like * "(API|HTTP) error: details" */ makeErrorMsg = function(code, jqxhr) { var details = ''; if ( code === 'http' && jqxhr.textStatus === 'error' ) { details = 'HTTP error ' + jqxhr.xhr.status; } else if ( code === 'http' ) { details = 'HTTP error: ' + jqxhr.textStatus; } else if ( code === 'ok-but-empty' ) { details = 'Error: Got an empty response from the server'; } else { details = 'API error: ' + code; }	return details; };

/** * makeLink * * Makes a link to a en.Wikipedia page that opens in a new tab/window. * * @requires {Module} mediawiki.util * @param {string} linktarget * The target page. * @param {string} linktext * Text to display in the link. Optional, if not provided the target will be used. * @return {jQuery} jQuery object containing the `` element. */ makeLink = function(linktarget, linktext) { if ( linktext == null ) { linktext = linktarget; }	return $('').attr({		'href': mw.util.getUrl(linktarget),		'target':'_blank'	}).text(linktext); };

/* ========== Tasks ======================================================== */

// Grab page data - initial author, current wikitext, any redirects, if Draft: // page already exists var grabPageData = function {

var patt_isRedirect = /^\s*#redirect/i;

var checkedPageTriageStatus = false;

// Function to check if all done var checkPageData = function { if (			config.pagedata.author != null &&			config.pagedata.oldwikitext != null &&			config.pagedata.redirects != null &&			checkedPageTriageStatus		) { //all done - go to next screen setTaskStatus(1, "done"); showContributorScreen; }	};

/* -- Initial author --- */

/* Try making an api call for just the first revision - but if that is a		redirect, then get 'max' number of revisions, and look for first non-redirect revision - use this as the initial author,	not the creator of the redirect. */	var processMaxRvAuthorQuery = function (result) { var revisions = result.query.pages[config.mw.wgArticleId].revisions; //blanking the contributor array config.pagedata.contributors = {}; for ( let i = 0; i < revisions.length; i++ ) { if ( !patt_isRedirect.test(revisions[i]["*"]) ) { if (config.pagedata.author == undefined) { config.pagedata.author = revisions[i].user; config.pagedata.creationTimestamp = revisions[i].timestamp; }

//Calculating contribs per editor if (config.pagedata.contributors[revisions[i].user] == undefined) { config.pagedata.contributors[revisions[i].user] = 1; } else { config.pagedata.contributors[revisions[i].user]++; }

//Find if there is a comment which indicats prev draftification if( revisions[i].comment.search(/moved page \[\[.*\]\] to \[\[Draft:/) !== -1) { config.pagedata.previousDraftification = true; }			}		}		// Check that we actually found an author (i.e. not all revisions were		// redirects) if ( config.pagedata.author == null ) { API.abort; var retry = confirm("Could not retrieve page author:\n"+makeErrorMsg(c, r)+"\n\nTry again?"); if ( retry ) { screen0; } else { $("#M2D-modal").remove; }		}

// Get the timestamp of the last edit config.pagedata.lastEditTimestamp = revisions[revisions.length - 1].timestamp;

sortContributorsByEdits;

checkPageData; };

var sortContributorsByEdits = function { // temporary array to ease sorting let sortable = []; for (let contributor in config.pagedata.contributors) { if (contributor !== config.mw.wgUserName) { sortable.push({"c": contributor,					"e": config.pagedata.contributors[contributor] }); }		}

sortable.sort(function(a, b) {			// make sure that the page creator is on the top			if(a.c === config.pagedata.author){				 return -100;			}			if(b.c === config.pagedata.author){				return 100;			}			// sorting by number of edits			return b.e - a.e;		});

config.pagedata.contributors = {}; for (let i in sortable) { if (i >= 5 ) { // only show the top 5 contributors break; }			config.pagedata.contributors [sortable[i].c] = sortable[i].e;		} };

var processAuthorQuery = function (result) { // Check if page is currently a redirect if ( result.query.pages[config.mw.wgArticleId].redirect ) { API.abort; alert("Error: " + config.mw.wgPageName + " is a redirect"); return; }

// query to look for first non-redirect revision API.get( {			action: "query",			pageids: config.mw.wgArticleId,			prop: "revisions",			rvprop: ["user", "content", "timestamp", "comment"],			rvlimit: "max",			rvdir: "newer"		} ) .done( processMaxRvAuthorQuery ) .fail( function(c,r) {			if ( r.textStatus === "abort" ) { return; }

API.abort; var retry = confirm("Could not retrieve page author:\n"+makeErrorMsg(c, r)+"\n\nTry again?"); if ( retry ) { screen0; } else { $("#M2D-modal").remove; }		} );		return;	};

//Get author setTaskStatus(0, "started"); API.get( {		action: "query",		pageids: config.mw.wgArticleId,		prop: ["revisions", "info"],		rvprop: "content",		rvlimit: 1,		rvdir: "newer"	} ) .done( processAuthorQuery ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

API.abort; var retry = confirm("Could not retrieve page author:\n"+makeErrorMsg(c, r)+"\n\nTry again?"); if ( retry ) { screen0; } else { $("#M2D-modal").remove; }	} );

setTaskStatus(0, "done"); setTaskStatus(1, "started");

/* -- Current wikitext - */

API.get( {		action: "query",		pageids: config.mw.wgArticleId,		prop: "revisions",		rvprop: "content"	} ) .done( function(result) {		config.pagedata.oldwikitext = result.query.pages[config.mw.wgArticleId].revisions[0]["*"];		checkPageData;	} ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

API.abort; var retry = confirm("Could not retrieve page wikitext:\n"+ makeErrorMsg(c, r)+"\n\nTry again?"); if ( retry ) { screen0; } else { $("#M2D-modal").remove; }	} );

//TODO(?): also get proposed Draft: page (to check if it is empty or not)

/* -- Redirects */ var redirectTitles = [];

var processRedirectsQuery = function(result) { if ( !result.query || !result.query.pages ) { // No results config.pagedata.redirects = false; checkPageData; return; }		// Gather redirect titles into array $.each(result.query.pages, function(_id, info) {			redirectTitles.push(info.title);		}); // Continue query if needed if ( result.continue ) { doRedirectsQuery($.extend(redirectsQuery, result.continue)); return; }

// Check if redirects were found if ( redirectTitles.length === 0 ) { config.pagedata.redirects = false; checkPageData; return; }

// Set redirects config.pagedata.redirects = ( redirectTitles.length === 0 ) ? false : redirectTitles; checkPageData; };

var redirectsQuery = { action: "query", pageids: config.mw.wgArticleId, generator: "redirects", grdlimit: 500 };	var doRedirectsQuery = function(q) { API.get( q ) .done( processRedirectsQuery ) .fail( function(c,r) {			if ( r.textStatus === "abort" ) { return; }

API.abort; var retry = confirm("Could not retrieve redirects:\n" + makeErrorMsg(c, r) +				"\n\nTry again? (or Cancel to skip)"); if ( retry ) { screen0; } else { config.pagedata.redirects = false; checkPageData; }		} );	};	doRedirectsQuery(redirectsQuery);

/* -- Review (Page Triage) status -- */

API.get( {		action: "pagetriagelist",		page_id: config.mw.wgArticleId	} ) .done( function(result) {		if ( !result.pagetriagelist.pages.length ) {			var keepGoing = confirm("WARNING: Page has already been reviewed by a New Page Patroller. Are you sure you want to draftify this page?");			if ( !keepGoing ) {				API.abort;				$("#M2D-modal").remove;				return;			}		}		checkedPageTriageStatus = true;		checkPageData;	} ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

API.abort; var retry = confirm("Could not retrieve page triage status:\n"+ makeErrorMsg(c, r)+"\n\nTry again?"); if ( retry ) { screen0; } else { $("#M2D-modal").remove; }	} );

};

//Sets the status of tasks in the progress screen var setTaskStatus = function(taskNumber, status, extraText) { if(taskNumber < 0 || taskNumber > 5 ) { //Invalid tasknumber return; }

switch ( status ) { case "started": $("#M2D-task" + taskNumber).css({"color":"#00F", "font-weight":"bold"}); $("#M2D-status" + taskNumber).text("In process"); break; case "done": $("#M2D-task" + taskNumber).css({"color":"#000", "font-weight":""}); $("#M2D-status" + taskNumber).html("&check;"); $("#M2D-status" + taskNumber).css("color", "green"); break; case "skipped": $("#M2D-task" + taskNumber).css({"color":"#F00", "font-weight":""}); $("#M2D-status" + taskNumber).text("Skipped!"); break; }	$("#M2D-status" + taskNumber) .css("margin", "0.75em");

if (extraText !== undefined) { $("#M2D-status" + taskNumber).append(" (" + extraText + ")"); } };

//Move page var movePage = function { setTaskStatus(0, "started");

// First check the page hasn't been draftified in the meantime API.get({		action: "query",		pageids: config.mw.wgArticleId,		format: "json",		formatversion: "2"	}).then(function(response) {		var page = response && response.query && response.query.pages &&			response.query.pages[0];		if (!page) {			return $.Deferred.reject;		} else if (page.missing) {			return $.Deferred.reject("moveToDraft-pagemissing");		} else if (page.ns === 118 /* Draft NS */) {			return $.Deferred.reject("moveToDraft-alreadydraft");		} else if (page.ns !== config.mw.wgNamespaceNumber) {			return $.Deferred.reject("moveToDraft-movednamespace");		}

return API.postWithToken( "csrf", {			action: "move",			fromid: config.mw.wgArticleId,			to: config.inputdata.newTitle,			movetalk: 1,			noredirect: 1,			reason: config.inputdata.rationale,			tags: config.changeTags		} ); })	.done( function { if (			-1 === $.inArray("sysop", config.mw.wgUserGroups) &&			-1 === $.inArray("extendedmover", config.mw.wgUserGroups)		) { // Newly created redirect to be tagged for speedy deletion tagRedirect; return; }		setTaskStatus(0, "done"); getImageInfo; } )	.fail( function(c,r) { if ( r && r.textStatus === "abort" ) { return; } else if (c === "moveToDraft-pagemissing") { alert("The page no longer appears to exists. It may have been deleted."); $("#M2D-modal").remove; window.location.reload; return; } else if (c === "moveToDraft-alreadydraft") { alert("Aborted: The page has already been moved to draftspace."); $("#M2D-modal").remove; window.location.reload; return; } else if (c === "moveToDraft-alreadydraft") { alert("Aborted: The page has already been moved out of mainspace."); $("#M2D-modal").remove; window.location.reload; return; }

var retry = confirm("Could not move page:\n"+ makeErrorMsg(c, r)+"\n\nTry again?"); if ( retry ) { movePage; } else { showOptionsScreen(true); }	} ); };

var tagRedirect = function { $("#M2D-status0").html("Done, Tagging redirect for speedy deletion..."); API.postWithToken( "csrf", {		action: "edit",		title: config.mw.wgPageName,		prependtext: '\n',		summary: 'R2 speedy deletion request (article moved to draftspace)',		tags: config.changeTags	} ) .done( function {		setTaskStatus(0, "done");		getImageInfo;	} ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not tag redirect for speedy deletion:\n"+			makeErrorMsg(c, r) + "\n\nTry again?"); if ( retry ) { tagRedirect; } else { setTaskStatus(0, "skipped"); getImageInfo; }	} ); };

//Find which images are non-free var getImageInfo = function { setTaskStatus(1, "started");

var processImageInfo = function(result) { var nonfreefiles = []; if ( result && result.query ) { $.each(result.query.pages, function(id, page) {				if ( id > 0 && page.categories ) {					nonfreefiles.push(page.title);				}			}); }		editWikitext(nonfreefiles); };

API.get( {		action: "query",		pageids: config.mw.wgArticleId,		generator: "images",		gimlimit: "max",		prop: "categories",		cllimit: "max",		clcategories: "Category:All non-free media",	} ) .done( function(result){		setTaskStatus(1, "done");		processImageInfo(result);	} ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not find if there are non-free files:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { getImageInfo; } else { setTaskStatus(1, "skipped"); editWikitext([]); }	} );

};

//Comment out non-free files, turn categories into links, add afc draft template, list any redirects var editWikitext = function(nonfreefiles) { setTaskStatus(2, "started");

var redirectsList = ( !config.pagedata.redirects ) ? "" : "\n"+ "\n";

var wikitext = "" + config.pagedata.author + "\n" + redirectsList + config.pagedata.oldwikitext.replace(/\[\[\s*[Cc]ategory\s*:/g, "[[:Category:") +		"\n";

// non-free files // (derived from WP:XFDC - https://en.wikipedia.org/wiki/User:Evad37/XFDcloser.js ) if ( nonfreefiles.length > 0 ) { // Start building regex strings var normal_regex_str = "(";		var gallery_regex_str = "("; var free_regex_str = "(";		for (let i=0; i<nonfreefiles.length; i++ ) {			// Take off namespace prefix			filename = nonfreefiles[i].replace(/^.*?:/, "");			// For regex matching: first character can be either upper or lower case, special			// characters need to be escaped, spaces can be either spaces or underscores			filename_regex_str = "[" + mw.util.escapeRegExp(filename.slice(0, 1).toUpperCase) +			mw.util.escapeRegExp(filename.slice(0, 1).toLowerCase) + "]" +			mw.util.escapeRegExp(filename.slice(1)).replace(/ /g, "[ _]");			// Add to regex strings			normal_regex_str += "\\[\\[\\s*(?:[Ii]mage|[Ff]ile)\\s*:\\s*" + filename_regex_str +			"\\s*\\|?.*?(?:(?:\\[\\[.*?\\]\\]).*?)*\\]\\]";			gallery_regex_str += "^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + filename_regex_str + ".*?$";			free_regex_str += "\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" +			filename_regex_str;

if ( i+1 === nonfreefiles.length ) { normal_regex_str += ")(?![^<]*?-->)";				gallery_regex_str += ")(?![^<]*?-->)"; free_regex_str += ")(?![^<]*?-->)";			} else {				normal_regex_str += "|";				gallery_regex_str += "|";				free_regex_str += "|";			}		}

// Check for normal file usage, i.e. 		var normal_regex = new RegExp( normal_regex_str, "g"); wikitext = wikitext.replace(normal_regex, "");

// Check for gallery usage, i.e. instances that must start on a new line, eventually // preceded with some space, and must include File: or Image: prefix var gallery_regex = new RegExp( gallery_regex_str, "mg" ); wikitext = wikitext.replace(gallery_regex, "");

// Check for free usages, for example as template argument, might have the File: or Image: // prefix excluded, but must be preceeded by an | var free_regex = new RegExp( free_regex_str, "mg" ); wikitext = wikitext.replace(free_regex, ""); }

API.postWithToken( "csrf", {		action: "edit",		pageid: config.mw.wgArticleId,		text: wikitext,		summary: config.wikitext.editsummary,		tags: config.changeTags	} ) .done( function{		setTaskStatus(2, "done");		notifyContributors;	} ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not edit draft artice:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { editWikitext(nonfreefiles); } else { setTaskStatus(2, "skipped"); notifyContributors; }	} );

};

var notifyContributors = function { if(config.pagedata.notifyList.length === 0) { setTaskStatus(3, "skipped"); updateTalk; return; }	setTaskStatus(3, "started"); for (let i in config.pagedata.notifyList) { notifyContributor( config.pagedata.notifyList[i] ); }	setTaskStatus(3, "done"); updateTalk; };

var notifyContributor = function( contributor ) { if ( !config.inputdata.notifyEnable ) { updateTalk; return; }

API.postWithToken( "csrf", {		action: "edit",		title: "User talk:" + contributor,		section: "new",		sectiontitle: config.inputdata.notifyMsgHead,		text: config.inputdata.notifyMsg,		tags: config.changeTags	} ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not edit author talk page:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { notifyContributor( contributor ); }	} ); };

var updateTalk = function { setTaskStatus(4, "started");

//if page exists, do a regex search/repace for class/importances parameters var processTalkWikitext = function(result) { var talk_id = result.query.pageids[0]; if ( talk_id < 0 ) { setTaskStatus(4, "done", "talk page does not exist"); draftifyLog; return; }		var old_talk_wikitext = result.query.pages[talk_id].revisions[0]["*"]; var new_talk_wikitext = old_talk_wikitext.replace(/(\|\s*(?:class|importance)\s*=\s*)[^\|}]*(?=[^}]*}})/g, "$1"); if ( new_talk_wikitext === old_talk_wikitext ) { setTaskStatus(4, "done", "no changes needed"); draftifyLog; return; }

API.postWithToken( "csrf", {			action: "edit",			pageid: talk_id,			section: "0",			text: new_talk_wikitext,			summary: 'Remove class/importance from project banners',			tags: config.changeTags		} ) .done( function{			setTaskStatus(4, "done");			draftifyLog;		} ) .fail( function(c,r) {			if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not edit draft's talk page:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { updateTalk; } else { setTaskStatus(4, "skipped"); draftifyLog; }		} );	};

//get talk page wikitext (section 0) API.get( {		action: "query",		titles: config.inputdata.newTitle.replace("Draft:", "Draft talk:"),		prop: "revisions",		rvprop: "content",		rvsection: "0",		indexpageids: 1	} ) .done( processTalkWikitext ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not find draft's talk page:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { updateTalk; } else { setTaskStatus(4, "skipped"); draftifyLog; }	} );

};

var draftifyLog = function { if (config.doNotLog) { $("#M2D-finished, #M2D-abort").toggle; return; }

setTaskStatus(5, "started");

var logpage = "User:" + config.mw.wgUserName + "/Draftify_log"; var monthNames = config.mw.wgMonthNames.slice(1); var now = new Date; var heading = "== " + monthNames[now.getUTCMonth] + " " + now.getUTCFullYear + " =="; var headingPatt = RegExp(heading);

var processLogWikitext = function(result) { var logpage_wikitext = "";

var id = result.query.pageids[0]; if ( id < 0 ) { var createlog = confirm("Log draftification (at " + logpage + ") ?"); if ( !createlog ) { setTaskStatus(5, "skipped"); $("#M2D-finished, #M2D-abort").toggle; return; }			logpage_wikitext = "This is a log of pages moved to draftspace using the MoveToDraft script."; } else { logpage_wikitext = result.query.pages[id].revisions[0]["*"].trim; }

if ( !headingPatt.test(logpage_wikitext) ) { logpage_wikitext += "\n\n" + heading; }		logpage_wikitext += "\n" + config.inputdata.logMsg;

API.postWithToken( "csrf", {			action: "edit",			title: logpage,			text: logpage_wikitext,			summary: 'Logging '+config.inputdata.newTitle+'',			tags: config.changeTags		} ) .done( function{			setTaskStatus(5, "done");			$("#M2D-finished, #M2D-abort").toggle;		} ) .fail( function(c,r) {			if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not edit log page:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { draftifyLog; } else { setTaskStatus(5, "skipped"); $("#M2D-finished, #M2D-abort").toggle; }		} );	};

//get log page wikitext API.get( {		action: "query",		titles: logpage,		prop: "revisions",		rvprop: "content",		indexpageids: 1	} ) .done( processLogWikitext ) .fail( function(c,r) {		if ( r.textStatus === "abort" ) { return; }

var retry = confirm("Could not find log page:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip"); if ( retry ) { draftifyLog; } else { setTaskStatus(5, "skipped"); $("#M2D-finished, #M2D-abort").toggle; }	} ); };

var setupHeader = function(subText) { $("#M2D-interface-header").append(		$(" ").text("X")			.attr('title', 'Close')			.css('float', 'right')			.click(function{ $("#M2D-modal").remove; }),		$(' ')			.append( makeLink(config.script.location, 'Move To Draft'), ' (v' + config.script.version + ') ', subText )			.css({"font-weight": "bold", "font-size": "large", "margin": "0.25em", "text-align": "center"})	); };

var notifyChange = function { $('#M2D-option-message-head').prop('disabled', !this.checked); for (let key in config.draftReasons) { $("#M2D-option-reasons-checkbox-"+key).prop("disabled", !this.checked); }	$("#M2D-option-reasons-checkbox-other").prop("disabled", !this.checked); $("#M2D-reason-other").prop("disabled", !this.checked);

if (this.checked) { $('#M2D-option-message-preview-outer').show; } else { $('#M2D-option-message-preview-outer').hide; } };

// --- Interface screens --- //0) Initial screen var screen0 = function {	$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty;	setupHeader("...");

$("#M2D-interface-content").text("Loading..."); $("#M2D-interface-content").append(		$('').attr('id', 'M2D-tasks').css("color", "#888").append( $('').attr('id', 'M2D-task0').append(				'Getting contributor data',				$(' ').attr('id','M2D-status0')			), $('').attr('id', 'M2D-task1').append(				'Getting page data',				$(' ').attr('id','M2D-status1')			) )	);

$("#M2D-interface-footer").append(		$(' ').attr('id', 'M2D-abort').text('Abort uncompleted tasks'),		$(' ').attr('id', 'M2D-finished').hide.append( 'Finished!', $(' ').attr('id', 'M2D-close').text('Close') .css('margin-left', '0.5em') )	);

$("#M2D-close").click( function{		$("#M2D-modal").remove;		window.location.reload;	} ); grabPageData; };

//1) Contributors var showContributorScreen = function {

var numContributors = Object.keys(config.pagedata.contributors).length; if( numContributors === 0) { console.log("No notifiable contributors");

//show next screen showOptionsScreen; return; } else if( numContributors === 1) { console.log("Only one contributor. Moving to next screen");

const singleContributor = Object.keys(config.pagedata.contributors)[0] config.pagedata.notifyList.push( singleContributor );

//show next screen showOptionsScreen; return; }

$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty; setupHeader(": contributors");

$("#M2D-interface-content").append(		$(' ')			.css('padding-bottom', '0.5em')			.text("Choose contributors to send notifications to: ")		) .css('margin', '1em');

for ( const [key, edits] of Object.entries( config.pagedata.contributors ) ) { var contributor = `${key}`.replace(/ /g, "_");

$('#M2D-interface-content').append(			$(' ').append( $(' ').attr({'id':'M2D-option-authors-checkbox-'+contributor,						'name':'contributors', 'type':'checkbox', 'value':contributor}), $(' ').attr({'id':'M2D-option-authors-label-'+contributor,						'for':'M2D-option-authors-checkbox-'+contributor}) .text(`${key}`+" - "+`${edits}`+" edit" + ((`${edits}`!=1)?"s":"") ) .css({'margin-left':'0.5em'}), $(' ')			)		);

if( contributor === config.pagedata.author ) { $('#M2D-option-authors-label-'+contributor).append(' (Page Creator)'); }

if( config.pagedata.notifyList.includes( contributor ) ) { $('#M2D-option-authors-checkbox-'+contributor).prop('checked', true); }	}

$("#M2D-interface-footer").append(		$(' ').attr('id', 'M2D-next').text('Next'),		$(' ').attr('id', 'M2D-cancel').css('margin-left','3em').text('Cancel')	);

$("#M2D-cancel").click(function{		$("#M2D-modal").remove;	});

$("#M2D-next").click(function{		var markedCheckboxes = document.querySelectorAll('input[name="contributors"]:checked');		config.pagedata.notifyList = [];		for (let checkbox of markedCheckboxes ) {			config.pagedata.notifyList.push( checkbox.value );		}

//show next screen showOptionsScreen; }); };

//2) User inputs /** * * @param {boolean} restoreValues Restore previously set values */ var showOptionsScreen = function(restoreValues) {	$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty;	setupHeader(": options");

$("#M2D-interface-content").append(		$(' ').css('margin-bottom','0.5em').append( $(' ')			.attr({'id': 'M2D-warning'}) .css({				display: 'block',				color: 'darkred',				'font-weight': 'bold',			}), $(' ').attr('for','M2D-option-newtitle').append(				'Move to ',				$('').text('Draft:')			), $(' ').attr({'type':'text', 'name':'M2D-option-newtitle', 'id':'M2D-option-newtitle'}) .css('margin-left', '0.25em') ),

$(' ').css('margin-bottom','0.5em').append(			$(' ').attr({'for':'M2D-option-movelog', 'id':'M2D-option-movelog-label'})				.css('display','block').text('Reason for move (log summary):'),			$(' ').attr({'rows':'1', 'name':'M2D-option-movelog', 'id':'M2D-option-movelog'})				.css('width','99%')		),

$(' ').attr({'id':'M2D-option-author'}).append(			$(' ').attr({'type':'checkbox', 'id':'M2D-option-message-enable'})				.css('margin-right', '0.25em')				.prop('checked', true),			$(' ').attr({'for':'M2D-option-message-enable'}).append( $('').text( 'Notify contributor(s): '))		),

$(' ').attr({'for':'M2D-option-message-head', 'id':'M2D-option-message-head-label'}) .css({'display':'block', 'margin-top':'0.5em'}).text('Notification heading'), $(' ').attr({'id':'M2D-option-message-head', 'rows':'1'}) .css({'width':'99%', 'margin-bottom':'0.5em'}),

$(' ').attr({'id':'M2D-option-reasons-label'}) .css({'display':'block', 'margin-top':'0.5em'}).text('Reason(s):'), $(' ').attr({'id':'M2D-option-reasons'}) .css({'width':'99%', 'columns': '2'}),

$(' ').attr({'id':'M2D-option-reasons-other'}) .css({'width':'99%', 'margin-bottom':'0.5em'}),

$(' ').attr({'for':'M2D-option-message', 'id':'M2D-option-message-label'}) .css('display','block').text('Notification message:'), $(' ').attr({'id':'M2D-option-message', 'rows':'6'}) .css('width','99%'), );

for (let key in config.draftReasons) { $("#M2D-option-reasons").append(			$(' ').append( $(' ').attr({'id':'M2D-option-reasons-checkbox-'+key, 'type':'checkbox', 'value':key}) .change( reasonChange ), $(' ').attr({'id':'M2D-option-reasons-label-'+key, 'for':'M2D-option-reasons-checkbox-'+key}) .text(config.draftReasons[key].charAt(0).toUpperCase + config.draftReasons[key].slice(1)) .css({'margin-left':'0.5em'}), $(' ')			)		);	}

//text box and label $("#M2D-option-reasons-other").append(		$(' ').attr({'id':'M2D-option-reasons-checkbox-other', 'type':'checkbox', 'value':'other'})			.css({'float':'left'}),		$(' ').attr({'for':'M2D-reason-other', 'id':'M2D-reason-other-label'})			.css({'float':'left', 'margin':'auto 0.5em'}).text('Other/additional reasons:'),		$(' ').attr({'id':'M2D-reason-other', 'rows':'1'})			.css({'width':'75%'})			.on("input keyup paste", reasonChange )	);

//Adding a warning, if needed setDraftifyWarning("#M2D-warning");

//Setting one of the checkboxes as checked by default $('#M2D-option-reasons-checkbox-0').prop('checked', true);

$('#M2D-option-movelog').val(config.wikitext.rationale); $('#M2D-option-newtitle').val(getPageText(config.mw.wgPageName)).change(function {		$('#M2D-option-message-head').val( $('#M2D-option-message-head').val.trim .replace(/\[\[Draft\:.*?\|/, "[[Draft:" + $('#M2D-option-newtitle').val.trim + "|")		);		$('#M2D-option-message').val(			$('#M2D-option-message').val.trim			.replace(/\[\[Draft\:.*?\|/, "[[Draft:" + $('#M2D-option-newtitle').val.trim + "|")		);	});

$('#M2D-option-message-enable').change(notifyChange);

if (config.pagedata.notifyList.length === 0) { $("#M2D-option-author").append( " None "); $("#M2D-option-message-enable").prop("checked", false); $("#M2D-option-message-enable").prop("disabled", true); notifyChange; } else { $("#M2D-option-author").append(			" " + config.pagedata.notifyList.toString.replace(/,/g, ", ") + " "		); }

if( Object.keys(config.pagedata.contributors).length > 1) { $("#M2D-option-author").append(			$('').text("(edit recipients)")				.click(showContributorScreen)		); }

$('#M2D-option-message-head').val(config.wikitext.notification_heading.replace(/\$1/g, getPageText(config.mw.wgPageName))); reasonChange;

$("#M2D-interface-footer").append(		$(' ').attr('id', 'M2D-next').text('Continue'),		$(' ').attr('id', 'M2D-cancel').css('margin-left','3em').text('Cancel')	);

$("#M2D-cancel").click(function{		$("#M2D-modal").remove;	});

if (restoreValues) { $('#M2D-option-movelog').val(config.inputdata.rationale); $('#M2D-option-newtitle').val(config.inputdata.newTitle); $('#M2D-option-author').val(config.inputdata.authorName); $('#M2D-option-message-enable').prop('checked', config.inputdata.notifyEnable); $('#M2D-option-message-head').val(config.inputdata.notifyMsgHead); $('#M2D-option-message').val(config.inputdata.notifyMsg); }

$("#M2D-next").click(function{		//Gather inputs		config.inputdata = {			rationale:		$('#M2D-option-movelog').val.trim,			newTitle: 		"Draft:" + $('#M2D-option-newtitle').val.trim,			authorName: 	$('#M2D-option-author').val.trim,			notifyEnable:	$('#M2D-option-message-enable').prop('checked'),			notifyMsgHead:	$('#M2D-option-message-head').val.trim,			notifyMsg:		$('#M2D-option-message').val.trim		};		config.inputdata.logMsg = config.wikitext.logMsg			.replace(/\$1/g, getPageText(config.mw.wgPageName))			.replace(/\$2/g, config.inputdata.newTitle);

//Verify inputs var errors=[]; if ( config.inputdata.newTitle.length === 0 ) { errors.push("Invalid draft title"); }

if ( config.inputdata.rationale.length === 0 ) { errors.push("Move log reason is empty"); }		if ( config.inputdata.notifyEnable ) { if ( config.inputdata.notifyMsgHead.length === 0 ) { errors.push("Notification heading is empty"); }			if ( config.inputdata.notifyMsg.length === 0 ) { errors.push("Notification message is empty"); }		}		if ( errors.length >= 1 ) { alert("Error:\n\n" + errors.join(";\n")); return; }

//start process off showProgressScreen; }); };

//Checks if draftification is appropriate and warns the user accordingly var setDraftifyWarning = function( divId ) { var now = new Date;

var articleCreatedOn = new Date( config.pagedata.creationTimestamp ); var articleAgeInDays = Math.round( ( now - articleCreatedOn ) / ( 1000 * 60 * 60 * 24 ) );

var articleEditedOn = new Date( config.pagedata.lastEditTimestamp ); var lastEditAgeInMinutes = Math.round( ( now - articleEditedOn ) / ( 1000 * 60 ) );

if ( lastEditAgeInMinutes < config.minimumAgeInMinutes ) { $(divId).text(''); $(divId).append(			"Draftifying may not be appropriate per ",			makeLink("WP:DRAFTIFY"),			", since this article was edited less than ",			config.minimumAgeInMinutes,			" minutes ago."		); } else if ( articleAgeInDays > config.maximumAgeInDays ) { $(divId).text(''); $(divId).append(			"Draftifying may not be appropriate per ",			makeLink("WP:DRAFTIFY"),			", since this article is more than ",			config.maximumAgeInDays,			" days old."		); } else if ( config.pagedata.previousDraftification ) { $(divId).text(''); $(divId).append(			"Draftifying may not be appropriate per ",			makeLink("WP:DRAFTIFY"),			", since this article has been previously draftified."		); } };

//Called when the state of any of the reason checkboxes changes var reasonChange = function { var reasons = []; for (let key in config.draftReasons) { if ( $("#M2D-option-reasons-checkbox-"+key).prop("checked") == true ) { reasons.push(key); }	}

//Cloning the array var draftifyReasons = JSON.parse(JSON.stringify(config.draftReasons));

//Other reasons text box var otherText = $("#M2D-reason-other").val; if ( otherText !== '' ) { draftifyReasons.other = otherText; reasons.push('other'); $('#M2D-option-reasons-checkbox-other').prop("checked", true); } else { $('#M2D-option-reasons-checkbox-other').prop("checked", false); }

var reasonText = ""; if (reasons.length === 0) { $('#M2D-next').prop('disabled', true); } else { $('#M2D-next').prop('disabled', false); reasonText = " because " + draftifyReasons[reasons[0]] + ""; if (reasons.length > 1) { for (let i = 1; i < (reasons.length - 1); i++) { reasonText += ", " + draftifyReasons[reasons[i]] + ""; }			reasonText += " and " + draftifyReasons[reasons[reasons.length-1]] + ""; }	}

$('#M2D-option-message').val(config.wikitext.notification			.replace(/\$1/g, getPageText(config.mw.wgPageName))			.replace(/\$3/g, reasonText)	);

//Converting the message text into a preview API.get( {		action: 'parse',		text: "==" + $('#M2D-option-message-head').val + "==\n" + $('#M2D-option-message').val,		disableeditsection: true,		contentmodel: 'wikitext'	} ) .done( function(result) {		$('#M2D-option-message-preview').html( result.parse.text['*'] );	} )

};

//3) Progress indicators var showProgressScreen = function {	$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty;	setupHeader(": In progress...");

$("#M2D-interface-content").append(		$('').attr('id', 'M2D-tasks').css("color", "#888").append( $('').attr('id', 'M2D-task0').append(				'Moving page',				$(' ').attr('id','M2D-status0')			), $('').attr('id', 'M2D-task1').append(				'Checking images',				$(' ').attr('id','M2D-status1')			), $('').attr('id', 'M2D-task2').append(				'Editing page wikitext',				$(' ').attr('id','M2D-status2')			), config.inputdata.notifyEnable ? $('').attr('id', 'M2D-task3').append(					'Notifying contributor/s',					$(' ').attr('id','M2D-status3')				) : '',			$('').attr('id', 'M2D-task4').append(				'Updating talk page banners',				$(' ').attr('id','M2D-status4')			),

$('').attr('id', 'M2D-task5').append(				'Logging',				config.doNotLog					? $(' ').attr('font-size', '90%' ).text('disabled')					: $(' ').attr('id','M2D-status5')			) )	);

$("#M2D-interface-footer").append(		$(' ').attr('id', 'M2D-abort').text('Abort uncompleted tasks'),		$(' ').attr('id', 'M2D-finished').hide.append( 'Finished!', $(' ').attr('id', 'M2D-close').text('Close') .css('margin-left', '0.5em') )	);

$("#M2D-close").click( function{		$("#M2D-modal").remove;		window.location.reload;	} ); $("M2D-abort").click( function{		API.abort;		$("#M2D-modal").remove;		window.location.reload;	} );

//Start the first task. The rest are done sequentially as each task is completed (or skipped). movePage; };

// --- Add link to 'More' menu (or user-specified portlet) which starts everything --- mw.util.addPortletLink( (window.m2d_portlet||'p-cactions'), '#', 'Move to draft', 'ca-m2d', null, null, "#ca-move"); $('#ca-m2d').on('click', function(e) {	e.preventDefault;	// Add interface shell	$('body').prepend(' '+ ' '+			' '+			' '+			' '+			' '+			' '+		' '+	' ');

// Interface styling $("#M2D-modal").css({		"position": "fixed",		"z-index": "1001",		"left": "0",		"top": "0",		"width": "100%",		"height": "100%",		"overflow": "auto",		"background-color": "rgba(0,0,0,0.4)"	}); $("#M2D-interface").css({		"background-color": "#f0f0f0",		"margin": "7% auto",		"padding": "2px 20px",		"border": "1px solid #888",		"width": "80%",		"max-width": "60em",		"font-size": "90%"	}); $("#M2D-interface-content").css("min-height", "7em"); $("#M2D-interface-footor").css("min-height", "3em");

// Initial interface content screen0; });

// End of function moveToDraft };

/* ========== Log draftifications for a user ==================================================== */ function logDraftifications(username, fromDate) { var targetUser = username; if (!targetUser && targetUser!=="") { var pageNameParts = config.mw.wgPageName.split('/'); targetUser = (pageNameParts.length > 1) ? pageNameParts[1] : ''; }	$('#mw-content-text').empty; // TODO: Form for setting user var today = new Date.toISOString.slice(0,10); var MoveToDraftEpoch = "2017-05-29"; $('#mw-content-text').append(		$(` <label for="draftifyUsername">User: <input type="text" name="username" id="draftifyUsername" /> <label for="draftifyFromDate">From date (and earlier) <input type="date" id="draftifyFromDate" name="fromDate" value="${fromDate || today}" /> <input type="submit" value="Show" /> `)	);	$('#draftifyUsername').val(targetUser); $('#draftifyLogForm').on('submit', function(e) {		e.preventDefault;		$('#draftifyLog, #draftifyLogWikitext').show;		logDraftifications($('#draftifyUsername').val, $('#draftifyFromDate').val);	});

$('#mw-content-text').append(		$(` <textarea id="draftifyLogWikitext" disabled="disabled" rows="10"> `)	);

$('#draftifyLogWikitext').val(`{|class="wikitable" !scope='col'|From !scope='col'|To !scope='col'|Time !scope='col'|User !scope='col'|Reason
 * }`);

var query = { action: "query", format: "json", list: "logevents", leprop: "title|timestamp|comment|details|user", letype: "move", lenamespace: "0", lelimit: "500", lestart: (fromDate || today) + "T23:59:59Z" };	if (targetUser) { query.leuser = targetUser; }

var continueInfo = {};

function onLoadMoreClick(e) { e.preventDefault; $('#draftifyStatus').empty.text("Loading..."); searchAndShowResults; }

function parseLogTable(wikitext) { API.post({			"action": "parse",			"format": "json",			"text": wikitext,			"prop": "text",			"contentmodel": "wikitext"		}).then(function(response) {			$parsedLogTable = $(response.parse.text["*"]);			$('#draftifyLog tbody').empty.append( $parsedLogTable.find('tr').slice(1) );		});	}

function searchAndShowResults { API.get( $.extend({}, query, continueInfo) ) .then(function(response) {				// Store continuing info, if any				continueInfo = response.continue || {};				// Reset status, add a "Load more" if there are more results				$('#draftifyStatus').empty.append( response.continue ? $('<a>').css("cursor", "pointer").text('Load more').click(onLoadMoreClick) : null );				// Filter to only MoveToDraft script moves				var draftifyEvents = response.query && response.query.logevents && response.query.logevents.filter(function(logevent) { return logevent.params.target_ns === 118; // Moved to Draft namespace });				var noDraftifyEvents = !draftifyEvents || !draftifyEvents.length;

switch(true) { case noDraftifyEvents && !response.continue: $('#draftifyStatus').empty.text(							$('#draftifyLog tbody tr').length == 0 ? "No results" : "No further results"						); break; case noDraftifyEvents: // Continue with next batch of results, otherwise table will initially have no results but a load more link, // or clicking "Load more" will appear to show "Loading..." but not actually add any results searchAndShowResults; break; case !response.continue: $('#draftifyStatus').empty.text("No further results"); /* falls through */ default: draftifyEvents.forEach(function(logevent) {							var fromTitle = logevent.title;							var toTitle = logevent.params.target_title;							var timeOfMove = new Date(logevent.timestamp).toUTCString.replace("GMT", "(UTC)");							var user = logevent.user;							var comment = logevent.comment;							var wikitext = $('#draftifyLogWikitext').val.replace("|}", `|- $('#draftifyLogWikitext').val(wikitext); parseLogTable(wikitext); });				}			});	}
 * ${fromTitle}
 * ${toTitle}
 * ${timeOfMove}
 * ${user}
 * ${comment}
 * }`);

// Run by default, unless page loaded without a /username suffix if (username || username==="") { searchAndShowResults; } else { $('#draftifyLog, #draftifyLogWikitext').hide; }

// End of function logDraftifications }

/* ========== Setup ============================================================================= */ // Access draftifications using Special:Draftify_log/USER_NAME var isDraftifyLogPage = config.mw.wgPageName.indexOf("Special:Draftify_log") === 0; var isUserPage = config.mw.wgNamespaceNumber === 2 || config.mw.wgNamespaceNumber === 3; if (isDraftifyLogPage) { document.title = "Draftify log - Wikipedia"; $('h1').text("Draftify log"); $('#mw-content-text').empty .text("Loading...") .before(		$(' ').append( 'Note: This page only works with the ', $('<a>').attr('href','/wiki/User:MPGuy2824/MoveToDraft').text('MoveToDraft'), ' userscript installed.' ),		$(' ')	);	logDraftifications; } else if (isUserPage) { var user = config.mw.wgTitle.split('/')[0]; var url = mw.util.getUrl("Special:Draftify_log/" + user); mw.util.addPortletLink( (window.m2d_portlet||'p-cactions'), url, 'Draftify log', 'ca-m2dlog', null, null, "#ca-move"); }

// Only operate in article namespace if( config.mw.wgNamespaceNumber !== 0 ) { return; }

// Only operate for existing pages if ( config.mw.wgCurRevisionId === 0 ) { return; }

moveToDraft;

}); //