מדיה ויקי:סקריפטים/107.js

מתוך ויקימילון, מיזם רב לשוני ליצירת מילון חופשי שיתופי.

הערה: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.

  • פיירפוקס / ספארי: להחזיק את המקש Shift בעת לחיצה על טעינה מחדש (Reload) או ללחוץ על צירוף המקשים Ctrl-F5 או Ctrl-R (במחשב מק: ⌘-R).
  • גוגל כרום: ללחוץ על צירוף המקשים Ctrl-Shift-R (במחשב מק: ⌘-Shift-R).
  • אינטרנט אקספלורר / אדג': להחזיק את המקש Ctrl בעת לחיצה על רענן (Refresh) או ללחוץ על צירוף המקשים Ctrl-F5.
  • אופרה: ללחוץ על Ctrl-F5.
/**
 * 
 * סקריפט זה מקל על הטיפול בטרולים ובמשחיתים.
 * הוא מאפשר לשחזר את כל העריכות שביצע משתמש מסוים, למחוק את כל הדפים שיצר,
 * לחסום אותו ולהסתיר את כל עריכותיו בלחיצת כפתור.
 * למידע נוסף ולהוראות שימוש ראו:
 * [[ויקיפדיה:סקריפטים/107.js/מידע]]
 * 
 * This tool allows you to handle vandalism more easily.
 * It adds a button on "Contribution" pages, that lets you
 * rollback all edits made by the relevant user/IP,
 * as well as delete all pages created by him/her, block him/her
 * and hide all his/her edits – all in one single operation.
 * 
 * Written by: [[User:Guycn2]] @ he.wikipedia.org
 * 
 * If you export this script to your wiki, please include the above lines.
 * 
 */

if (
	mw.config.get("wgCanonicalSpecialPageName") === "Contributions" &&
	mw.config.get("wgRelevantUserName") !== mw.config.get("wgUserName")
) {
	
	mw.loader.load(
		"//he.wikipedia.org/w/index.php?title=מדיה_ויקי:סקריפטים/107.css" +
		"&action=raw&ctype=text/css", "text/css"
	);
	
	$.when(mw.loader.using(["mediawiki.util", "mediawiki.api"]), $.ready).done(function() {
		
		"use strict";
		
		var relevantUser = mw.config.get("wgRelevantUserName"),
		    api = new mw.Api(),
		    currentUserRights = [],
		    relevantUserGender = "",
		    interfaceMessages = {},
		    isRelevantUserAnon = mw.util.isIPAddress(relevantUser),
		    contribsPeriod = 0,
		    queryLimit = 0,
		    latestEdits = [],
		    totalEdits = 0,
		    createdPages = [],
		    totalPages = 0,
		    revIDs = [],
		    totalIDs = 0,
		    tagExists = true,
		    $dialog = {},
		    isRollbackChecked = false,
		    isDeleteChecked = false,
		    isBlockChecked = false,
		    isRevDeleteChecked = false,
		    isHideSummariesChecked = false,
		    cleanerSummary = "",
		    blockDuration = "",
		    totalTasks = 0,
		    currentTask = 0,
		    failedBlock = false,
		    editNumber = 0,
		    rollbackPercent = 0,
		    failedRollbacks = false,
		    pageNumber = 0,
		    deletePercent = 0,
		    failedDeletions = false,
		    idNumber = 0,
		    revDeletePercent = 0,
		    failedRevDeletions = false;
		
		api.get({
			list: "users",
			usprop: "rights|gender",
			ususers: relevantUser + "|" + mw.config.get("wgUserName")
		}).done(function(data) {
			
			var relevantUserRights = data.query.users[0].rights;
			
			currentUserRights = data.query.users[1].rights;
			relevantUserGender = data.query.users[0].gender;
			
			if (
				isRelevantUserAnon ||
				(
					typeof relevantUserRights !== "undefined" &&
					relevantUserRights.indexOf("autopatrol") < 0
				)
			) {
				
				$.getScript(
					"//he.wikipedia.org/w/index.php?title=מדיה_ויקי:סקריפטים/107.js/config.js" +
					"&action=raw&ctype=text/javascript",
					function() {
						
						interfaceMessages =
							window.vandalCleanerConfig.messages[mw.config.get("wgUserLanguage")] ||
							window.vandalCleanerConfig.messages.en;
						
						contribsPeriod = window.vandalCleanerConfig.contribsPeriod;
						queryLimit = window.vandalCleanerConfig.queryLimit;
						
						$("#contentSub").after(
							$("<button>", {
								type: "button",
								"class": "mw-ui-button mw-ui-destructive",
								id: "vandal-cleaner-button",
								title: i18n("contribsLinkTooltip"),
								text: i18n("contribsLinkText")
							}).on("click", function() {
								initializeTool(setOldestContribsDate());
							})
						);
						
					}
				);
				
			}
			
		});
		
		function i18n(key) {
			
			key = interfaceMessages[key] || key;
			
			var isAnonPattern = /<<ISANON\|(.*?)\|(.*?)(>>)/g,
			    genderPattern = /<<GENDER\|(.*?)\|(.*?)(>>)/g,
			    extendedGenderPattern = /<<GENDER\|(.*?)\|(.*?)\|(.*?)(>>)/g;
			
			key = key
				.replace(isAnonPattern, isRelevantUserAnon ? "$1" : "$2")
				.replace(
					extendedGenderPattern,
					relevantUserGender === "female" ? "$2" : (relevantUserGender === "male" ? "$1" : "$3")
				)
				.replace(genderPattern, relevantUserGender === "female" ? "$2" : "$1")
				.replace(/\$RELEVANTUSER/g, relevantUser)
				.replace(/\$CONTRIBSPERIOD/g, contribsPeriod)
				.replace(/\$QUERYLIMIT/g, queryLimit)
				.replace(/(<<|>>)/g, "");
			
			return key;
			
		}
		
		function initializeTool(oldestContribsDate) {
			
			if ($("#vandal-cleaner-interface-dialog").length) {
				console.log("Vandal Cleaner dialog is already open.");
				return;
			}
			
			mw.util.$content.append(
				$("<div>", {
					"class": "vandal-cleaner-dark-cover"
				}).append(
					$("<img>", {
						id: "vandal-cleaner-loading-image",
						src: "//upload.wikimedia.org/wikipedia/commons/1/14/Ajax-loader_bert2_blue-white.gif",
						alt: i18n("loadingInterface")
					})
				)
			);
			
			var $loadingCover = $(".vandal-cleaner-dark-cover").fadeTo("_default", 1);
			
			api.get({
				list: "usercontribs",
				ucuser: relevantUser,
				ucprop: "title",
				ucshow: "top|!new",
				uclimit: queryLimit,
				ucend: oldestContribsDate
			}).done(function(editsQuery) {
				
				latestEdits = editsQuery.query.usercontribs;
				totalEdits = latestEdits.length;
				
				api.get({
					list: "usercontribs",
					ucuser: relevantUser,
					ucprop: "title",
					ucshow: "new",
					uclimit: queryLimit,
					ucend: oldestContribsDate
				}).done(function(pagesQuery) {
					
					createdPages = pagesQuery.query.usercontribs;
					totalPages = createdPages.length;
					
					api.get({
						list: "usercontribs",
						ucuser: relevantUser,
						ucprop: "ids",
						ucshow: "!new",
						uclimit: queryLimit,
						ucend: oldestContribsDate
					}).done(function(idsQuery) {
						
						revIDs = idsQuery.query.usercontribs;
						totalIDs = revIDs.length;
						
						// Check if [[MediaWiki:Tag-VandalCleaner]] exists to determine
						// whether or not actions made using this script should be tagged.
						
						api.get({
							titles: "MediaWiki:Tag-VandalCleaner"
						}).done(function(tagQuery) {
							
							if (tagQuery.query.pages[-1]) {
								
								// If [[MediaWiki:Tag-VandalCleaner]] does not exist, then the actual
								// "VandalCleaner" tag is also not likely to exist. In this case,
								// all actions made using this script must not use that tag; otherwise
								// they will fail with the "tags-apply-not-allowed-one" error.
								
								tagExists = false;
								
							}
							
							$.when(mw.loader.using("jquery.ui", createDialog)).done(function() {
								
								$loadingCover.fadeOut("fast", function() {
									$(this).remove();
								});
								
							});
							
						});
						
					});
					
				});
				
			});
			
		}
		
		function setOldestContribsDate() {
			
			var date = new Date();
			date.setTime(date.getTime() - (contribsPeriod * 24 * 60 * 60 * 1000));
			
			var year = date.getUTCFullYear(),
			    month = date.getUTCMonth() + 1,
			    day = date.getUTCDate(),
			    hours = date.getUTCHours(),
			    minutes = date.getUTCMinutes(),
			    seconds = date.getUTCSeconds();
			
			return year + "-" +
			       (month < 10 ? "0" + month : month) + "-" +
			       (day < 10 ? "0" + day : day) + "T" +
			       (hours < 10 ? "0" + hours : hours) + ":" +
			       (minutes < 10 ? "0" + minutes : minutes) + ":" +
			       (seconds < 10 ? "0" + seconds : seconds) + "Z";
			
		}
		
		function createDialog() {
			
			$dialog = $("<div>", {
				id: "vandal-cleaner-interface-dialog",
				"data-state": "selection"
			});
			
			var $para1 = $("<p>", {
			    	text: i18n("para1")
			    }),
			    
			    $para2 = $("<p>", {
			    	text: i18n("para2")
			    }),
			    
			    $para3 = $("<p>", {
			    	text: i18n("para3")
			    }),
			    
			    $rollbackField = $("<div>", {
			    	"class": "vandal-cleaner-field"
			    }),
			    
			    $deleteField = $("<div>", {
			    	"class": "vandal-cleaner-field"
			    }),
			    
			    $blockField = $("<div>", {
			    	"class": "vandal-cleaner-field"
			    }),
			    
			    $revDeleteField = $("<div>", {
			    	"class": "vandal-cleaner-field"
			    }),
			    
			    $summary = $("<div>", {
			    	id: "vandal-cleaner-edit-summary"
			    });
			
			$rollbackField.append(
				$("<input>", {
					type: "checkbox",
					"class": "vandal-cleaner-field-tog",
					id: "vandal-cleaner-rollback-tog"
				}),
				$("<label>", {
					"class": "vandal-cleaner-field-label",
					"for": "vandal-cleaner-rollback-tog",
					text: i18n("rollbackLabel")
				}),
				$("<div>", {
					"class": "vandal-cleaner-field-info-wrapper"
				}).append(
					$("<p>", {
						"class": "vandal-cleaner-field-description",
						text: i18n("rollbackDesc")
					}),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-rollback-no-permission",
						text: i18n("cannotRollback")
					}),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-rollback-too-many",
						text: i18n("tooManyEdits")
					})
				)
			);
			
			$deleteField.append(
				$("<input>", {
					type: "checkbox",
					"class": "vandal-cleaner-field-tog",
					id: "vandal-cleaner-delete-tog"
				}),
				$("<label>", {
					"class": "vandal-cleaner-field-label",
					"for": "vandal-cleaner-delete-tog",
					text: i18n("deleteLabel")
				}),
				$("<div>", {
					"class": "vandal-cleaner-field-info-wrapper"
				}).append(
					$("<p>", {
						"class": "vandal-cleaner-field-description",
						text: i18n("deleteDesc")
					}),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-delete-no-permission",
						text: i18n("cannotDelete")
					}),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-delete-too-many",
						text: i18n("tooManyPages")
					})
				)
			);
			
			$blockField.append(
				$("<input>", {
					type: "checkbox",
					"class": "vandal-cleaner-field-tog",
					id: "vandal-cleaner-block-tog"
				}),
				$("<label>", {
					"class": "vandal-cleaner-field-label",
					"for": "vandal-cleaner-block-tog",
					text: i18n("blockLabel")
				}),
				$("<div>", {
					"class": "vandal-cleaner-field-info-wrapper"
				}).append(
					$("<p>", {
						"class": "vandal-cleaner-field-description",
						text: i18n("blockDesc")
					}),
					$("<p>", {
						id: "vandal-cleaner-block-duration-container"
					}).append(
						$("<label>", {
							"for": "vandal-cleaner-block-duration-dropdown",
							text: i18n("blockDuration")
						}),
						" ",
						$("<span>", {
							id: "vandal-cleaner-block-duration-current"
						}),
						" ",
						$("<span>", {
							id: "vandal-cleaner-block-duration-change-container"
						}).append(
							$("<span>", {
								text: "("
							}),
							$("<a>", {
								id: "vandal-cleaner-block-duration-change",
								role: "button",
								href: "#",
								text: i18n("changeBlockDuration")
							}),
							$("<span>", {
								text: ")"
							})
						),
						$("<select>", {
							id: "vandal-cleaner-block-duration-dropdown"
						})
					),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-block-no-permission",
						text: i18n("cannotBlock")
					})
				)
			);
			
			$revDeleteField.append(
				$("<input>", {
					type: "checkbox",
					"class": "vandal-cleaner-field-tog",
					id: "vandal-cleaner-revdelete-tog"
				}),
				$("<label>", {
					"class": "vandal-cleaner-field-label",
					"for": "vandal-cleaner-revdelete-tog",
					text: i18n("revDeleteLabel")
				}),
				$("<div>", {
					"class": "vandal-cleaner-field-info-wrapper"
				}).append(
					$("<p>", {
						"class": "vandal-cleaner-field-description",
						text: i18n("revDeleteDesc")
					}),
					$("<p>", {
						id: "vandal-cleaner-revdelete-advanced-container"
					}).append(
						$("<input>", {
							type: "checkbox",
							id: "vandal-cleaner-revdelete-hide-summaries-tog"
						}),
						$("<label>", {
							"for": "vandal-cleaner-revdelete-hide-summaries-tog",
							text: i18n("hideSummaries")
						})
					).hide(),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-revdelete-no-permission",
						text: i18n("cannotRevDelete")
					}),
					$("<p>", {
						"class": "vandal-cleaner-field-attention",
						id: "vandal-cleaner-revdelete-too-many",
						text: i18n("tooManyRevisions")
					})
				)
			);
			
			$summary.append(
				$("<p>").append(
					$("<label>", {
						"for": "vandal-cleaner-summary-input",
						text: i18n("summaryInfo")
							.replace(
								/\$DEFAULTSUMMARY/g,
								window.vandalCleanerSummary || i18n("summaryValue")
							)
					})
				),
				$("<input>", {
					type: "text",
					id: "vandal-cleaner-summary-input",
					placeholder: i18n("summaryPlaceholder"),
					value: window.vandalCleanerSummary || i18n("summaryValue"),
					maxlength: "500"
				})
			);
			
			$dialog.append(
				$para1, $para2, $para3, $rollbackField, $deleteField,
				$blockField, $revDeleteField, $summary
			).dialog({
				
				title: i18n("dialogTitle"),
				resizable: false,
				draggable: false,
				open: function() {
					
					$dialog.parent().addClass("vandal-cleaner-dialog-wrapper");
					
					if (currentUserRights.indexOf("rollback") < 0) {
						
						$rollbackField.find("#vandal-cleaner-rollback-tog").prop("disabled", true);
						$rollbackField.find("#vandal-cleaner-rollback-no-permission").show();
						
					} else {
						
						$rollbackField.find("#vandal-cleaner-rollback-tog").prop("checked", true);
						
						if (totalEdits >= queryLimit) {
							
							$rollbackField.find("#vandal-cleaner-rollback-too-many").show();
							
							$rollbackField.find("#vandal-cleaner-rollback-tog").on("change", function() {
								$rollbackField.find("#vandal-cleaner-rollback-too-many").slideToggle("fast");
							});
							
						}
						
					}
					
					if (currentUserRights.indexOf("delete") < 0) {
						
						$deleteField.find("#vandal-cleaner-delete-tog").prop("disabled", true);
						$deleteField.find("#vandal-cleaner-delete-no-permission").show();
						
					} else {
						
						$deleteField.find("#vandal-cleaner-delete-tog").prop("checked", true);
						
						if (totalPages >= queryLimit) {
							
							$deleteField.find("#vandal-cleaner-delete-too-many").show();
							
							$deleteField.find("#vandal-cleaner-delete-tog").on("change", function() {
								$deleteField.find("#vandal-cleaner-delete-too-many").slideToggle("fast");
							});
							
						}
						
					}
					
					if (currentUserRights.indexOf("block") < 0) {
						
						$blockField.find("#vandal-cleaner-block-duration-container").hide();
						$blockField.find("#vandal-cleaner-block-tog").prop("disabled", true);
						$blockField.find("#vandal-cleaner-block-no-permission").show();
						
					} else {
						
						for (let i = 0; i < window.vandalCleanerConfig.blockOptions.length; i++) {
							
							let blockOption = window.vandalCleanerConfig.blockOptions[i];
							
							$blockField.find("#vandal-cleaner-block-duration-dropdown").append(
								$("<option>", {
									value: blockOption.duration,
									text: i18n(blockOption.message)
								})
							);
							
							if (blockOption.makeDefault === "anon" && isRelevantUserAnon) {
								
								$blockField.find("option[value='" + blockOption.duration + "']")
									.prop("selected", true);
								
								$blockField.find("#vandal-cleaner-block-duration-current")
									.text(i18n(blockOption.message));
								
							} else if (blockOption.makeDefault === "registered" && !isRelevantUserAnon) {
								
								$blockField.find("option[value='" + blockOption.duration + "']")
									.prop("selected", true);
								
								$blockField.find("#vandal-cleaner-block-duration-current")
									.text(i18n(blockOption.message));
								
							}
							
						}
						
						$blockField.find("#vandal-cleaner-block-tog").prop("checked", true);
						
						$blockField.find("#vandal-cleaner-block-tog").on("change", function() {
							$blockField.find("#vandal-cleaner-block-duration-container").slideToggle("fast");
						});
						
						$blockField.find("#vandal-cleaner-block-duration-change").on("click", function(e) {
							
							e.preventDefault();
							
							$blockField.find(
								"#vandal-cleaner-block-duration-current," +
								"#vandal-cleaner-block-duration-change-container"
							).hide();
							
							$blockField.find("#vandal-cleaner-block-duration-dropdown").show();
							
						});
						
					}
					
					if (currentUserRights.indexOf("deleterevision") < 0) {
						
						$revDeleteField.find("#vandal-cleaner-revdelete-tog").prop("disabled", true);
						$revDeleteField.find("#vandal-cleaner-revdelete-no-permission").show();
						
					} else {
						
						$revDeleteField.find("#vandal-cleaner-revdelete-tog").on("change", function() {
							
							$revDeleteField.find("#vandal-cleaner-revdelete-advanced-container").slideToggle("fast");
							
							if (totalIDs >= queryLimit) {
								$revDeleteField.find("#vandal-cleaner-revdelete-too-many").slideToggle("fast");
							}
							
						});
						
					}
					
					$summary.find("#vandal-cleaner-summary-input").on("keypress", function(e) {
						
						if (e.which === 13) {
							runCleaner();
						}
						
					});
					
				},
				close: function() {
					$dialog.remove();
				},
				buttons: [
					
					{
						id: "vandal-cleaner-run-btn",
						text: i18n("runCleaner"),
						click: runCleaner
					},
					
					{
						text: i18n("cancel"),
						click: function() {
							$dialog.dialog("close");
						}
					}
					
				]
				
			});
			
		}
		
		function runCleaner() {
			
			isRollbackChecked =
				$dialog.find("#vandal-cleaner-rollback-tog").prop("checked");
			
			isDeleteChecked =
				$dialog.find("#vandal-cleaner-delete-tog").prop("checked");
			
			isBlockChecked =
				$dialog.find("#vandal-cleaner-block-tog").prop("checked");
			
			blockDuration =
				$dialog.find("#vandal-cleaner-block-duration-dropdown").val();
			
			isRevDeleteChecked =
				$dialog.find("#vandal-cleaner-revdelete-tog").prop("checked");
			
			isHideSummariesChecked =
				$dialog.find("#vandal-cleaner-revdelete-hide-summaries-tog").prop("checked");
			
			cleanerSummary =
				$dialog.find("#vandal-cleaner-summary-input").val();
			
			if (!isRollbackChecked && !isDeleteChecked && !isBlockChecked && !isRevDeleteChecked) {
				alert(i18n("noActionSelected"));
				return;
			}
			
			totalTasks =
				(isBlockChecked ? 1 : 0) +
				(isRollbackChecked ? totalEdits : 0) +
				(isDeleteChecked ? totalPages : 0) +
				(isRevDeleteChecked ? totalIDs : 0);
			
			var $pleaseWait = $("<h5>", {
			    	text: i18n("pleaseWait")
			    }),
			    
			    $totalProgress = $("<div>", {
			    	id: "vandal-cleaner-total-progress-container"
			    }).append(
			    	$("<div>", {
			    		id: "vandal-cleaner-total-progress-value"
			    	})
			    ),
			    
			    $blockStatus = $("<p>", {
			    	"class": "vandal-cleaner-status vandal-cleaner-status-working",
			    	id: "vandal-cleaner-block-status",
			    	text: i18n("blocking")
			    }).append(
			    	$("<span>", {
			    		text: " ... "
			    	}).append(
			    		$("<bdi>", {
			    			id: "vandal-cleaner-block-percent",
			    			text: "0"
			    		})
			    	)
			    ),
			    
			    $blockFailed = $("<p>", {
			    	"class": "vandal-cleaner-failure",
			    	id: "vandal-cleaner-block-failed",
			    	text: i18n("blockFailed")
			    }),
			    
			    $rollbackStatus = $("<p>", {
			    	"class": "vandal-cleaner-status vandal-cleaner-status-working",
			    	id: "vandal-cleaner-rollback-status",
			    	text: i18n("rollbacking")
			    }).append(
			    	$("<span>", {
			    		text: " ... "
			    	}).append(
			    		$("<bdi>", {
			    			id: "vandal-cleaner-rollback-percent",
			    			text: "0"
			    		})
			    	)
			    ),
			    
			    $rollbackNoEdits = $("<p>", {
			    	"class": "vandal-cleaner-info",
			    	id: "vandal-cleaner-rollback-no-edits",
			    	text: i18n("rollbackNoEdits")
			    }),
			    
			    $rollbackFailed = $("<p>", {
			    	"class": "vandal-cleaner-failure",
			    	id: "vandal-cleaner-rollback-failed",
			    	text: i18n("rollbackFailed")
			    }),
			    
			    $deleteStatus = $("<p>", {
			    	"class": "vandal-cleaner-status vandal-cleaner-status-working",
			    	id: "vandal-cleaner-delete-status",
			    	text: i18n("deleting")
			    }).append(
			    	$("<span>", {
			    		text: " ... "
			    	}).append(
			    		$("<bdi>", {
			    			id: "vandal-cleaner-delete-percent",
			    			text: "0"
			    		})
			    	)
			    ),
			    
			    $deleteNoPages = $("<p>", {
			    	"class": "vandal-cleaner-info",
			    	id: "vandal-cleaner-delete-no-pages",
			    	text: i18n("deleteNoPages")
			    }),
			    
			    $deleteFailed = $("<p>", {
			    	"class": "vandal-cleaner-failure",
			    	id: "vandal-cleaner-delete-failed",
			    	text: i18n("deleteFailed")
			    }),
			    
			    $revDeleteStatus = $("<p>", {
			    	"class": "vandal-cleaner-status vandal-cleaner-status-working",
			    	id: "vandal-cleaner-revdelete-status",
			    	text: i18n("revDeleting")
			    }).append(
			    	$("<span>", {
			    		text: " ... "
			    	}).append(
			    		$("<bdi>", {
			    			id: "vandal-cleaner-revdelete-percent",
			    			text: "0"
			    		})
			    	)
			    ),
			    
			    $revDeleteNoEdits = $("<p>", {
			    	"class": "vandal-cleaner-info",
			    	id: "vandal-cleaner-revdelete-no-edits",
			    	text: i18n("revDeleteNoEdits")
			    }),
			    
			    $revDeleteFailed = $("<p>", {
			    	"class": "vandal-cleaner-failure",
			    	id: "vandal-cleaner-revdelete-failed",
			    	text: i18n("revDeleteFailed")
			    });
			
			$dialog.children().remove().end()
				.attr("data-state", "working")
				.append(
					$pleaseWait, $totalProgress, $blockStatus, $blockFailed, $rollbackStatus,
					$rollbackNoEdits, $rollbackFailed, $deleteStatus, $deleteNoPages,
					$deleteFailed, $revDeleteStatus, $revDeleteNoEdits, $revDeleteFailed
				)
				.dialog({
					buttons: [],
					close: function() {
						window.location.reload();
					}
				});
			
			if (isBlockChecked) {
				hermeticBlock();
			} else if (isRollbackChecked) {
				massRollback();
			} else if (isDeleteChecked) {
				massDelete();
			} else if (isRevDeleteChecked) {
				massRevDelete();
			}
			
		}
		
		function hermeticBlock() {
			
			var $status = $dialog.find("#vandal-cleaner-block-status");
			hideTopBorderIfNeeded($status);
			$status.fadeIn();
			
			api.postWithToken("csrf", {
				action: "block",
				user: relevantUser,
				expiry: blockDuration,
				nocreate: true,
				autoblock: true,
				noemail: true,
				reblock: true,
				reason: cleanerSummary,
				tags: tagExists ? "VandalCleaner" : ""
			}).then(
				function() {
					$dialog.find("#vandal-cleaner-block-percent").text("100");
					$status
						.removeClass("vandal-cleaner-status-working")
						.addClass("vandal-cleaner-status-complete");
				},
				function() {
					$status.hide();
					$dialog.find("#vandal-cleaner-block-failed").show();
					$dialog.find("#vandal-cleaner-total-progress-value").attr("data-failure", "yes");
					failedBlock = true;
				}
			).always(function() {
				moveOn("block");
			});
			
		}
		
		function massRollback() {
			
			var $status = $dialog.find("#vandal-cleaner-rollback-status");
			
			if (totalEdits === 0) {
				
				let $noEdits = $dialog.find("#vandal-cleaner-rollback-no-edits");
				hideTopBorderIfNeeded($noEdits);
				$noEdits.fadeIn();
				moveOn("rollback", $status);
				
			} else {
				
				if (editNumber === 0) {
					hideTopBorderIfNeeded($status);
					$status.fadeIn();
				}
				
				api.postWithToken("rollback", {
					action: "rollback",
					title: latestEdits[editNumber].title,
					user: relevantUser,
					summary: cleanerSummary,
					tags: tagExists ? "VandalCleaner" : ""
				}).fail(function() {
					$dialog.find("#vandal-cleaner-rollback-failed").fadeIn();
					$dialog.find("#vandal-cleaner-total-progress-value").attr("data-failure", "yes");
					failedRollbacks = true;
				}).always(function() {
					moveOn("rollback", $status);
				});
				
			}
			
		}
		
		function massDelete() {
			
			var $status = $dialog.find("#vandal-cleaner-delete-status");
			
			if (totalPages === 0) {
				
				let $noPages = $dialog.find("#vandal-cleaner-delete-no-pages");
				hideTopBorderIfNeeded($noPages);
				$noPages.fadeIn();
				moveOn("delete", $status);
				
			} else {
				
				if (pageNumber === 0) {
					hideTopBorderIfNeeded($status);
					$status.fadeIn();
				}
				
				api.postWithToken("csrf", {
					action: "delete",
					title: createdPages[pageNumber].title,
					reason: cleanerSummary,
					tags: tagExists ? "VandalCleaner" : ""
				}).fail(function() {
					$dialog.find("#vandal-cleaner-delete-failed").fadeIn();
					$dialog.find("#vandal-cleaner-total-progress-value").attr("data-failure", "yes");
					failedDeletions = true;
				}).always(function() {
					moveOn("delete", $status);
				});
				
			}
			
		}
		
		function massRevDelete() {
			
			var $status = $dialog.find("#vandal-cleaner-revdelete-status");
			
			if (totalIDs === 0) {
				
				let $noEdits = $dialog.find("#vandal-cleaner-revdelete-no-edits");
				hideTopBorderIfNeeded($noEdits);
				$noEdits.fadeIn();
				moveOn("revdelete", $status);
				
			} else {
				
				if (idNumber === 0) {
					hideTopBorderIfNeeded($status);
					$status.fadeIn();
				}
				
				api.postWithToken("csrf", {
					action: "revisiondelete",
					type: "revision",
					ids: revIDs[idNumber].revid,
					hide: isHideSummariesChecked ? "content|comment" : "content",
					reason: cleanerSummary,
					tags: tagExists ? "VandalCleaner" : ""
				}).fail(function() {
					$dialog.find("#vandal-cleaner-revdelete-failed").fadeIn();
					$dialog.find("#vandal-cleaner-total-progress-value").attr("data-failure", "yes");
					failedRevDeletions = true;
				}).always(function() {
					moveOn("revdelete", $status);
				});
				
			}
			
		}
		
		function hideTopBorderIfNeeded($element) {
			
			if (!$element.siblings("p:visible").length) {
				$element.css("border-top-style", "hidden");
			}
			
		}
		
		function moveOn(currentAction, $status) {
			
			switch (currentAction) {
				
				case "block":
					
					updateProgressBar();
					
					setTimeout(function() {
						
						if (isRollbackChecked) {
							massRollback();
						} else if (isDeleteChecked) {
							massDelete();
						} else if (isRevDeleteChecked) {
							massRevDelete();
						} else {
							finalizeTool();
						}
						
					}, 800);
					
					break;
					
				case "rollback":
					
					if (totalEdits > 0) {
						
						updateProgressBar();
						
						rollbackPercent = ++editNumber / totalEdits * 100;
						$dialog.find("#vandal-cleaner-rollback-percent").text(rollbackPercent.toFixed(0));
						
					}
					
					if (editNumber < totalEdits) {
						
						if (currentUserRights.indexOf("noratelimit") < 0 && totalEdits > 10) {
							
							// If we don't have the "noratelimit" right, and the vandal has
							// made more than 10 edits,  then we need to act more slowly;
							// otherwise our actions might be throttled by the system!
							
							if (editNumber >= 1 && editNumber <= Math.floor((totalEdits - 1) / 10) * 10 + 1) {
								
								// Rollback slowly to avoid reaching the rate limit:
								setTimeout(massRollback, 6200);
								
							} else {
								
								// We have less than 10 edits left, so we can now do it more quickly:
								setTimeout(massRollback, 800);
								
							}
							
						} else {
							
							// Either we have the "noratelimit" right and/or the vandal
							// has made <=10 edits, so we can rollback all edits quickly:
							setTimeout(massRollback, 700);
							
						}
						
					} else {
						
						$status
							.removeClass("vandal-cleaner-status-working")
							.addClass("vandal-cleaner-status-complete");
						
						setTimeout(function() {
							
							if (isDeleteChecked) {
								massDelete();
							} else if (isRevDeleteChecked) {
								massRevDelete();
							} else {
								finalizeTool();
							}
							
						}, 800);
						
					}
					
					break;
					
				case "delete":
					
					if (totalPages > 0) {
						
						updateProgressBar();
						
						deletePercent = ++pageNumber / totalPages * 100;
						$dialog.find("#vandal-cleaner-delete-percent").text(deletePercent.toFixed(0));
						
					}
					
					if (pageNumber < totalPages) {
						
						setTimeout(massDelete, 700);
						
					} else {
						
						$status
							.removeClass("vandal-cleaner-status-working")
							.addClass("vandal-cleaner-status-complete");
						
						setTimeout(function() {
							
							if (isRevDeleteChecked) {
								massRevDelete();
							} else {
								finalizeTool();
							}
							
						}, 800);
						
					}
					
					break;
					
				case "revdelete":
					
					if (totalIDs > 0) {
						
						updateProgressBar();
						
						revDeletePercent = ++idNumber / totalIDs * 100;
						$dialog.find("#vandal-cleaner-revdelete-percent").text(revDeletePercent.toFixed(0));
						
					}
					
					if (idNumber < totalIDs) {
						
						setTimeout(massRevDelete, 700);
						
					} else {
						
						$status
							.removeClass("vandal-cleaner-status-working")
							.addClass("vandal-cleaner-status-complete");
						
						setTimeout(finalizeTool, 800);
						
					}
				
			}
			
		}
		
		function updateProgressBar() {
			
			$dialog.find("#vandal-cleaner-total-progress-value")
				.css("width", (++currentTask / totalTasks * 100) + "%");
			
		}
		
		function finalizeTool() {
			
			var $thumbImg = $("<img>", {
			    	id: "vandal-cleaner-thumb-img",
			    	src: "//upload.wikimedia.org/wikipedia/commons/c/ce/Emoji_u1f44d.svg",
			    	alt: i18n("finishedRunning")
			    }),
			    
			    $finishedRunning = $("<h6>", {
			    	id: "vandal-cleaner-finished-running-heading",
			    	text: i18n("finishedRunning")
			    }),
			    
			    $failureWarning = $("<p>", {
			    	"class": "warning",
			    	id: "vandal-cleaner-failure-warning",
			    	html: i18n("failureWarning")
			    		.replace(
			    			/\$LINK/g,
			    			mw.util.getUrl("Special:Contributions", {target: relevantUser, topOnly: "1"})
			    		)
			    }),
			    
			    $para4 = $("<p>", {
			    	text: i18n("para4")
			    }),
			    
			    $para5 = $("<p>", {
			    	html: i18n("para5")
			    		.replace(/\$LINK/g, "//he.wikipedia.org/wiki/שיחת_מדיה_ויקי:סקריפטים/107.js")
			    }),
			    
			    $para6 = $("<p>", {
			    	text: i18n("para6").replace(/\$SITENAME/g, mw.config.get("wgSiteName"))
			    }),
			    
			    $para7 = $("<p>", {
			    	text: i18n("para7")
			    });
			
			$dialog.parent()
				.addClass("vandal-cleaner-dialog-wrapper-disappear")
				.on("transitionend", function() {
					
					$dialog.remove();
					
					$dialog = $("<div>", {
						id: "vandal-cleaner-finished-dialog"
					});
					
					$dialog.append(
						$thumbImg, $finishedRunning, $failureWarning, $para4, $para5, $para6, $para7
					).dialog({
						
						title: i18n("dialogTitle"),
						resizable: false,
						draggable: false,
						open: function() {
							
							$dialog.parent()
								.wrap(
									$("<div>", {
										"class": "vandal-cleaner-dark-cover"
									})
								)
								.addClass("vandal-cleaner-dialog-wrapper vandal-cleaner-dialog-wrapper-reappear")
								.parent().fadeTo("_default", 1)
								.end().find("#vandal-cleaner-close-and-reload-btn").focus();
							
							if (failedBlock || failedRollbacks || failedDeletions || failedRevDeletions) {
								$dialog.find("#vandal-cleaner-failure-warning").show();
							}
							
						},
						close: function() {
							
							$dialog.closest(".vandal-cleaner-dark-cover").remove().end().remove();
							window.location.reload();
							
						},
						buttons: [
							
							{
								id: "vandal-cleaner-close-and-reload-btn",
								text: i18n("closeAndReload"),
								click: function() {
									$dialog.dialog("close");
								}
							}
							
						]
						
					});
					
				});
			
		}
		
	});
	
}