mic_none

User:Ahecht/Scripts/massrevdel.js Source: en.wikipedia.org/wiki/User:Ahecht/Scripts/massrevdel.js

function wpMassRevDelCheckRev(event) {
	var textarea = document.getElementById('wpMassRevDelPages'),
		lines = textarea.value.split('\n').filter(line => line.trim() !== '');
	if (event.target.checked) {
		if (!lines.includes(event.target.value)) {
			lines.push(event.target.value);
		}
	} else {
		var i = lines.indexOf(event.target.value);
		if (i !== -1) {
			lines.splice(i, 1);
		}
	}
	textarea.value = lines.join('\n');
}

mw.loader.using(['mediawiki.api', 'mediawiki.Title'], function () {
	"use strict";

	var config = mw.config.get(['wgNamespaceNumber', 'wgCanonicalSpecialPageName', 'wgTitle', 'wgUserGroups', 'skin']);

    const chunkSize = 50;
		    
	function removeBlanks(arr) {
		var ret = [];
		var i, len;
		for (i = 0, len = arr.length; i < len; i++) {
			var s = arr[i];
			s = s.trim();
			if (s) {
				ret.push(s);
			}
		}
		return ret;
	}
	
	function doMassRevDel() {
		document.getElementById("wpMassRevDelSubmit").disabled = true;
		var ids = document.getElementById("wpMassRevDelPages").value.split("\n");
		ids = removeBlanks(ids);
		if (!ids.length) {
			return;
		}
		var
			api = new mw.Api(),
			wpMassRevDelReasons = document.getElementById("wpMassRevDelReasons").value,
			wpMassRevDelReason = document.getElementById("wpMassRevDelReason").value,
			wpMassRevDelItems = document.querySelectorAll('#wpMassRevDel input[type="checkbox"]:checked'),
			hide = Array.from(wpMassRevDelItems).map(checkbox => checkbox.value).join('|'),
			deleted = 0,
			failed = [],
			error = [],
			deferreds = [],
			reason = wpMassRevDelReasons == "other" ?
				wpMassRevDelReason :
				wpMassRevDelReasons + (wpMassRevDelReason ? " (" + wpMassRevDelReason + ")" : ""),
			onSuccess = function (data) {
				//console.log(data);
				deleted += (data?.revisiondelete?.items.length || 0);
				document.getElementById("wpMassRevDelSubmit").value = "(" + deleted + ")";
			};

		function makeDeleteFunc(idString) {
			return function () {
				return $.Deferred(function (deferred) {
					var query = {
						format: 'json',
						action: 'revisiondelete',
						type: 'revision',
						ids: idString,
						hide: hide,
						reason: reason
					};
					//console.log(query);
					var promise = api.postWithToken('csrf', query);
					promise.done(onSuccess);
					promise.fail(function (code, obj) {
						failed.push(idString);//console.error(idString);
						error.push(obj.error.info);//console.error(obj.error.info);
					});
					promise.always(function () {
						deferred.resolve();
					});
				});
			};
		}

		// Make a chain of deferred objects. We chain them rather than execute them in
		// parallel so that we don't make 1000 simultaneous delete requests and bring the
		// site down. We use deferred objects rather than the promise objects returned
		// from the API request so that the chain continues even if some ids gave
		// errors.
		var deferred = makeDeleteFunc(ids[0])();
		for (var i = 1, len = ids.length; i < len; i++) {
			deferred = deferred.then(makeDeleteFunc(ids[i]));
		}

		// Show the output and do cleanup once all the requests are done.
		$.when(deferred).then(function () {
			document.getElementById("wpMassRevDelSubmit").value = "Done (" + deleted + ")";
			if (failed.length) {
				var $failedList = $('<ul>');
				for(var x = 0; x < failed.length; x++) {
					// Link the titles in the "failed" array
					var failedTitle = mw.Title.newFromText(failed[x]);
					var $failedItem = $('<li>');
					if (failedTitle) {
						$failedItem.append( $('<a>')
							.attr('href', failedTitle.getUrl())
							.text(failed[x])
						);
					} else {
						$failedItem.text(failed[x]);
					}
					$failedItem.append(document.createTextNode(': ' + error[x]));
					$failedList.append($failedItem);
				}
				$('#wpMassRevDelFailedContainer')
					.append($('<br />'))
					.append($('<b>')
						.text('Failed deletions:')
					)
					.append($failedList);
			}
		});
	}
	 
	function MassRevDelform() {
	    var bodyContent;
		switch (mw.config.get('skin')) {
			case 'modern':
				bodyContent = 'mw_contentholder';
				break;
			case 'cologneblue':
				bodyContent = 'article';
				break;
			case 'monobook':
			case 'vector':
			default:
				bodyContent = 'bodyContent';
				break;
		}
		var pageHtml = 
			'<p>Ahecht\'s mass-revision-deletion tool based on <a href="https://demo.azizisearch.com/lite/wikipedia/page/User:Animum/massdelete.js">Animum\'s mass-deletion tool</a>.</p>' +
			'<form id="wpMassRevDel" name="wpMassRevDel">' +
				'<b>If you abuse this tool, it\'s <i>your</i> fault, not mine.</b>' +
				'<div id="wpMassRevDelFailedContainer"></div>' +
				'<br /><br />' +
				'Revisions to delete (one on each line, please):<br />' +
					'<textarea tabindex="1" accesskey="," name="wpMassRevDelPages" id="wpMassRevDelPages" rows="10" cols="80"></textarea>' +
				'<br /><br /><table style="background-color:transparent">' +
					'<tr><td>Common reasons:</td>' +
						'<td colspan="3"><select id="wpMassRevDelReasons">' +
							'<optgroup label="Other reason">' +
								'<option value="other">Other reason</option>' +
							'</optgroup>' +
							'<optgroup label="Criteria for revision deletion">' +
								'<option value="[[WP:RD1|RD1]]: Blatant violations of the [[WP:C|copyright policy]].">RD1: Blatant violations of the copyright policy</option>' +
								'<option value="[[WP:RD2|RD2]]: Grossly insulting, degrading, or offensive material.">RD2: Grossly insulting, degrading, or offensive material</option>' +
								'<option value="[[WP:RD2|RD2]]: Serious [[WP:BLP|BLP]] violations.">RD2: Serious BLP violations</option>' +
								'<option value="[[WP:RD3|RD3]]: Purely [[WP:DE|disruptive]] material.">RD3: Purely disruptive material</option>' +
								'<option value="[[WP:RD5|RD5]]: Other valid deletion under [[WP:DEL#REASON|deletion policy]].">RD5: Other valid deletion</option>' +
								'<option value="[[WP:RD6|RD6]]: Non-contentious housekeeping, RevDel corrections, notes, conversion.">RD6: Non-contentious housekeeping</option>' +
								'<option value="[[WP:F5|F5]]: Orphaned non-free file(s)."></option>' +
							'</optgroup>' +
						'</select></td>' +
					'</tr><tr><td>Other/additional reason:</td>' +
						'<td colspan="3"><input type="text" id="wpMassRevDelReason" name="wpMassRevDelReason" maxlength="255" style="box-sizing:border-box;width:100%"/></td>' +
					'</tr><tr><td>Items to delete:</td>' +
						'<td><input type="checkbox" id="wpMassRevDelSummary" name="wpMassRevDelSummary" value="comment"><label for "wpMassRevDelSummary>Edit summary</label></td>' +
						'<td><input type="checkbox" id="wpMassRevDelText" name="wpMassRevDelText" value="content"><label for "wpMassRevDelText>Revision text</label></td>' +
						'<td><input type="checkbox" id="wpMassRevDelUser" name="wpMassRevDelUser" value="content"><label for "wpMassRevDelUser>Username/IP address</label></td>' +
					'</tr><tr><td><input type="button" id="wpMassRevDelSubmit" name="wpMassRevDelSubmit" value="Delete" /></td></tr>' +
				'</table>' +
			'</form>';
		if (config.wgCanonicalSpecialPageName) {
			document.querySelector('style').sheet.insertRule(".mw-pager-body [type='checkbox'] {vertical-align: middle;margin: 0 4px");
			document.querySelector('.mw-pager-body').insertAdjacentHTML('afterbegin',
				'<div class="mw-checkbox-toggle-controls">Select: <a class="mw-checkbox-all" role="button" onclick="' + 
					"$( 'li input[type=&quot;checkbox&quot;]' ).prop( 'checked', true ).trigger( 'change' );" +
				'">All</a>, <a class="mw-checkbox-none" role="button"  onclick="' +
					"$( 'li input[type=&quot;checkbox&quot;]' ).prop( 'checked', false ).trigger( 'change' );" +
				'">None</a>, <a class="mw-checkbox-invert" role="button"  onclick="' +
					"$( 'li input[type=&quot;checkbox&quot;]' ).prop( 'checked', function ( i, val ) {return !val} ).trigger( 'change' );" +
				'">Invert</a></div>'
			);
			document.querySelectorAll('.mw-revdelundel-link').forEach(span => {
			    const idsValue = new URLSearchParams(span.querySelector('a').search).get('ids');
			    if (idsValue) {
			        span.insertAdjacentHTML(
			        	'afterend',
			        	`<input type="checkbox" name="wpMassRevDelId" value="${idsValue}" onchange="wpMassRevDelCheckRev(event)">`
			        );
			    }
			});
			document.getElementById(bodyContent).innerHTML += pageHtml;
		} else {
			document.title = "Ahecht's mass-revision-deletion tool - Wikipedia, the free encyclopedia";
			document.getElementsByTagName("h1")[0].textContent = "Ahecht's mass-revision-deletion tool";
			document.getElementById(bodyContent).innerHTML = pageHtml;
		}
		document.getElementById("wpMassRevDelReasons").onchange = function() {
			var maxlength = (document.getElementById("wpMassRevDelReasons").value == "other" ? 255 : 252-document.getElementById("wpMassRevDelReasons").value.length); //It's 252 because of the three characters (" ()") in addition to the selected summary.
			document.getElementById("wpMassRevDelReason").setAttribute("maxlength", maxlength);
		};
		document.getElementById("wpMassRevDelSubmit").addEventListener("click", function (e) {
			doMassRevDel();
		});
	}
	 
	if (config.wgNamespaceNumber == -1 &&
		(
			config.wgTitle.toLowerCase() == "massrevdel" ||
			config.wgCanonicalSpecialPageName == "Contributions"
		) &&
		/sysop/.test(config.wgUserGroups)
	) {
		MassRevDelform();
	}

});