Jump to content

User:Terasail/Edit Request Tool.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Terasail (talk | contribs) at 20:22, 13 November 2022 (Probably should do it like this). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
//<nowiki>
/*jshint esversion: 6*/
var editRequestBoxes = document.getElementsByClassName("plainlinks tmbox tmbox-notice editrequest");
var pageWatchers = "There are currently ";
var watchStatus;
var dataERT;
var api = new mw.Api();
var pageNameERT = mw.config.values.wgPageName;
var encodePageName = encodeURIComponent(pageNameERT);
pageNameERT = pageNameERT.replaceAll("_", " ");

function postEdit(wikitext, editSummary, secIndx, changeWatch, watchlist) {
	let apiParams = {
		action: 'edit',
		title: pageNameERT,
		text: wikitext,
		section: secIndx,
		summary: editSummary,
		watchlist: changeWatch
	};
	let reloadURL = "/w/index.php?title=" + encodePageName + "&type=revision&diff=cur&oldid=prev";
	if (changeWatch == "watch") {
		apiParams.watchlistexpiry = watchlist;
	}
	api.postWithEditToken(apiParams).done(function () {
		window.location = reloadURL;
	});
}

function execute(responseTable, replyOption, inputText, answered, boxType, targets, changeWatch, watchlist) {
	OO.ui.confirm("Confirm in order to reply to this edit request.").done(function (confirmed) {
		if (confirmed) {
			//Create label box & remove action buttons
			let firstRow = responseTable.children[1].children[0];
			firstRow.innerHTML = "";
			responseTable.children[5].children[0].remove();
			let infoBox = new OO.ui.MessageWidget({
				icon: 'pageSettings',
				type: 'notice',
				label: 'Processing request — Edit request starting, getting section data to edit.'
			});
			infoBox.$element[0].style = "margin:5px 0; max-width:50em";
			$(firstRow).append(infoBox.$element);
			//Create loading bar
			let progressBar = new OO.ui.ProgressBarWidget({
				progress: false
			});
			$(firstRow).append(progressBar.$element);
			//Set preview for output
			if (replyOption[0] != "") {//Don't preview a non-response
				showOutput(inputText, replyOption, responseTable.children[4].children[0], boxType);
			}
			//Find header
			let header = "";
			let curElement = responseTable.parentNode;
			do {
				curElement = curElement.previousElementSibling;
				if (curElement.getElementsByClassName("mw-headline").length == 1) {
					header = curElement.getElementsByClassName("mw-headline")[0].id;
				}
			}
			while (header == "");
			api.get({
				action: "parse",
				page: pageNameERT,
				prop: "sections"
			}).done(function (data) {
				infoBox.setLabel("Processing request — Making changes to the edit request");
				let editTemplate = "{{Edit " + boxType + "-protected";
				if (answered) {
					answered = "yes";
				} else {
					answered = "no";
				}
				for (let c3 = 0; c3 < targets.length; c3++) {
					editTemplate += "|" + targets[c3];
				}
				editTemplate += "|answered=" + answered;
				let sections = data.parse.sections;
				let secIndx = sections[0];
				for (let j = 0; j < sections.length; j++) {
					if (sections[j].anchor == header) {
						secIndx = parseInt(sections[j].index);
					}
				}
				api.get({
					action: "parse",
					page: pageNameERT,
					section: secIndx,
					prop: "wikitext|revid"
				}).done(function (data) {
					let wikitext = data.parse.wikitext["*"];
					let newRev = data.parse.revid;
					wikitext = wikitext.replace(/{{ *([SETFI]PER|Edit([ -]?[A-Z]+[ -]?|[- ])Protected)\s*[^}}]*/i, editTemplate);
					let editSummary = "/* " + header.replaceAll("_", " ") + " */ " + replyOption[2] + " ([[User:Terasail/Edit_Request_Tool|Edit Request Tool]])";
					if (replyOption[1] != "Close") {
						wikitext = wikitext.trim() + "\n:";
						if (replyOption[0] != "") {
							wikitext += "{{subst:" + dataERT.protections[boxType][1] + replyOption[0] + "}} ";
						}
						if (inputText != "") {
							wikitext += inputText.replaceAll(/\s*~~~~\s*/g, "") + " ";
						}
						wikitext += "~~~~";
						if (replyOption[1] == "Remove") {
							wikitext = "";
							if (inputText == "EmptyNotice") {
								let commentERT = document.getElementById(header).dataset.mwComment;
								if (typeof (commentERT) != 'undefined' && commentERT.search(/-(?=[0-9]{14}","type)/) != -1) {
									let commenter = commentERT.slice(commentERT.indexOf('"name"') + 10, commentERT.search(/-(?=[0-9]{14}","type)/));
									let talkParams = dataERT.emptyNotice;
									talkParams.title = "User talk:" + commenter;
									talkParams.sectiontitle += pageNameERT;
									talkParams.text = talkParams.text.replace("```", pageNameERT);
									talkParams.summary = talkParams.summary.replace("```", pageNameERT);
									console.log(talkParams);
									api.postWithEditToken(talkParams).done(function (result) {
										//Do something here?
									});
								}
							}
							editSummary = editSummary.replace(/[^]+\*\/ /, "");
						}
					}
					infoBox.setType("success");
					infoBox.setLabel("Processing request — Saving changes to the talk page.");
					if (newRev == mw.config.values.wgRevisionId) {
						postEdit(wikitext, editSummary, secIndx, changeWatch, watchlist);
					} else {
						OO.ui.confirm("There has been a new revision to the page, do you wish to continue?").done(function (revCon) {
							if (revCon) {
								postEdit(wikitext, editSummary, secIndx, changeWatch, watchlist);
							}
						});
					}
				});
			});
		}
	});
}

function addButtons(currentBox, replyButton) {
	let boxType = currentBox.parentElement.dataset.origlevel;
	boxType = boxType.replace("full", "fully");
	let tableElem = currentBox.parentElement;
	$('<table style="border:1px solid #A2A9B1; border-radius:2px; padding:10px 16px 0; margin:auto; max-width:55em; width:100%; clear:both;"><tr><td style="color:#808080"><div style="font-style:italic; margin-left:1em;">' + pageWatchers + '</div><div>Quick options:</div></td></tr><tr style="display: flex; justify-content: center;"><td></td></tr><tr><td style="color:#808080">Custom response:</td></tr><tr style="text-align:center;"><td></td></tr><tr style="background:#F6F6F6;"><td style="display:none;"><div style="color:#808080">Preview:</div><div></div></td></tr><tr style="display: flex; justify-content: right;"><td></td></tr></table>').insertAfter(tableElem);
	let responseTable = tableElem.nextElementSibling.children[0];
	let firstRow = responseTable.children[1].children[0];
	let secondRow = responseTable.children[3].children[0];
	let thirdRow = responseTable.children[4].children[0];
	let fourthRow = responseTable.children[5].children[0];
	let protections = Object.entries(dataERT.protections);
	let responses = Object.entries(dataERT.response);
	let quickResponses = Object.entries(dataERT.quickResponse);
	let nonResponses = dataERT.nonResponse;
	//Create type change dropdown
	let tcOptions = [];
	for (let i = 0; i < protections.length; i++) {
		tcOptions.push({data: protections[i][0], label: protections[i][1][0]});
	}
	let typeChange = new OO.ui.DropdownInputWidget({
		value: boxType,
		options: tcOptions
	});
	typeChange.on("change", function () {
		submitB.setDisabled(false);
	});
	typeChange.$element[0].style = "text-align:left; margin:auto";
	$(secondRow).append(typeChange.$element);
	//Create target page list
	let boxLinks = currentBox.getElementsByClassName("mbox-text")[0].getElementsByClassName("external text");
	let pageLinks = [];
	if (boxLinks.length > 0) {//Open request
		for (let c1 = 0; c1 < boxLinks.length; c1++) {
			if (boxLinks[c1].parentElement.tagName == "LI" || boxLinks[c1].parentElement.tagName == "B") {
				pageLinks[pageLinks.length] = boxLinks[c1].innerHTML;
			}
		}
	} else {//Closed request
		boxLinks = currentBox.getElementsByClassName("mbox-text")[0].getElementsByTagName("A");
		for (let c2 = 1; c2 < boxLinks.length; c2++) {
			pageLinks[pageLinks.length] = boxLinks[c2].title;
		}
		if (pageLinks.length == 0) {
			pageLinks = pageNameERT.replace(/(_talk|Talk:)/, "");
		}
	}
	let targetPages = new OO.ui.TagMultiselectWidget({
		placeholder: 'Target Pages',
		allowArbitrary: true,
		selected: pageLinks
	});
	targetPages.on("change", function () {
		submitB.setDisabled(false);
	});
	targetPages.$element[0].style = "text-align:left; margin:5px auto";
	$(secondRow).append(targetPages.$element);
	//Create dropdown menu
	let dropMenu = new OO.ui.DropdownWidget({
		label: "Select reply option - Add additional text below",
		menu: {items: []}
	});
	for (let count = 0; count < responses.length; count++) {
		let newOption = new OO.ui.MenuOptionWidget({
			label: responses[count][0],
			icon: responses[count][1][1]
		});
		dropMenu.menu.addItems([newOption]);
	}
	responses = dataERT.response;
	dropMenu.$element[0].style = "text-align:left; margin:0px";
	$(secondRow).append(dropMenu.$element);
	dropMenu.on("labelChange", function () {
		submitB.setDisabled(false);
		showOutput(inputText, responses[dropMenu.getLabel()], thirdRow, typeChange.value);
	});
	//Create input box
	let inputText = new OO.ui.MultilineTextInputWidget({autosize: true, rows: 4, label: "Additional text"});
	inputText.$element[0].style = "margin:5px auto";
	$(secondRow).append(inputText.$element);
	inputText.on("change", function (newText) {
		showOutput(inputText, responses[dropMenu.getLabel()], thirdRow, typeChange.value);
	});
	//Create top horizontal layout
	let hzLayoutT = new OO.ui.HorizontalLayout();
	//Create firstrow fieldset
	let fieldsetT = new OO.ui.FieldsetLayout();
	fieldsetT.addItems([new OO.ui.FieldLayout(new OO.ui.Widget({content: [hzLayoutT]}), {align: 'top'})]);
	$(firstRow).append(fieldsetT.$element);
	//Remove button
	let remove = new OO.ui.ButtonWidget({
		icon: "trash",
		flags: ["primary", "destructive"],
		invisibleLabel: true,
		title: "Remove the section!"
	});
	remove.on("click", function () {
		secondRow.style = "display:none";
		thirdRow.style = "display:none";
		fourthRow.style = "display:none";
		responseTable.children[2].children[0].style = "display:none";
		submitB.$element.remove();
		hzLayoutT.clearItems();
		//Create deletion options
		let remSec = new OO.ui.ButtonWidget({//RemoveSection
			icon: "trash",
			flags: ["primary", "destructive"],
			label: "Remove section",
			title: "Remove the entire section!"
		});
		remSec.on("click", function () {
			if (toggleESN.selected) {
				execute(responseTable, nonResponses.Remove, "EmptyNotice", null, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
			} else {
				execute(responseTable, nonResponses.Remove, "", null, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
			}
		});
		hzLayoutT.addItems([remSec]);
		let toggleESN = new OO.ui.CheckboxInputWidget({selected: false});
		hzLayoutT.addItems([toggleESN, new OO.ui.LabelWidget({label: "Give the user an empty section notice"})]);
		hzLayoutT.addItems([cancelB]);
	});
	hzLayoutT.addItems([remove]);
	//Open & Close button
	if (currentBox.parentElement.attributes[2].localName != "data-origlevel") {
		let closeB = new OO.ui.ButtonWidget({
			icon: "unFlag",
			invisibleLabel: true,
			title: "Mark as answered"
		});
		closeB.on("click", function () {
			execute(responseTable, nonResponses.Close, "", true, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
		});
		hzLayoutT.addItems([closeB]);
	} else {
		let openB = new OO.ui.ButtonWidget({
			icon: "flag",
			invisibleLabel: true,
			title: "Mark as unanswered"
		});
		openB.on("click", function () {
			execute(responseTable, nonResponses.Open, "", false, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
		});
		hzLayoutT.addItems([openB]);
	}
	//Create quick response buttons
	for (let i = 0; i < quickResponses.length; i++) {
		let newButton = new OO.ui.ButtonWidget({
			label: quickResponses[i][1][0],
			flags: quickResponses[i][1][1],
			title: quickResponses[i][1][2]
		});
		newButton.on("click", function () {
			execute(responseTable, responses[quickResponses[i][0]], "", toggleAns.selected, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
		});
		hzLayoutT.addItems([newButton]);
	}
	//Toggle answer button
	let toggleAns = new OO.ui.CheckboxInputWidget({selected: true});
	hzLayoutT.addItems([toggleAns, new OO.ui.LabelWidget({label: "Answered"})]);
	//Create lastrow horizontal layout
	let hzLayoutB = new OO.ui.HorizontalLayout();
	//Create lastrow fieldset
	let fieldsetB = new OO.ui.FieldsetLayout();
	fieldsetB.addItems([new OO.ui.FieldLayout(new OO.ui.Widget({content: [hzLayoutB]}), {align: 'top'})]);
	$(fourthRow).append(fieldsetB.$element);
	//Cancel response button
	let cancelB = new OO.ui.ButtonWidget({
		icon: "cancel",
		flags: ["destructive"],
		label: "Cancel",
		framed: false,
		title: "Cancel the response & close menu"
	});
	cancelB.on("click", function () {
		replyButton.setDisabled(false);
		responseTable.parentElement.remove();
	});
	hzLayoutB.addItems([cancelB]);
	let isWatched = (typeof (watchStatus[0]) != "undefined");
	//Watch list//Toggle answer button
	let toggleWL = new OO.ui.CheckboxInputWidget({selected: isWatched});
	let toggleWLLable = new OO.ui.LabelWidget({label: "Watch this page"});
	toggleWL.on("change", function (newStatus) {
		watchDropdown.setDisabled(!newStatus);
	});
	toggleWLLable.$element[0].style = "white-space: nowrap;";
	hzLayoutB.addItems([toggleWL, toggleWLLable]);
	let watchValue = "never";
	let watchOptions = [{data: "never", label: "Permanent"}, {data: "1 day", label: "1 day"}, {
		data: "3 days",
		label: "3 days"
	}, {data: "1 week", label: "1 week"}, {data: "1 month", label: "1 month"}];
	let wlExpiry = watchStatus[1];
	if (typeof (wlExpiry) != "undefined") {
		let daysDif = Math.ceil((new Date(wlExpiry).getTime() - Date.now()) / 1000 / 60 / 60 / 24);
		watchOptions.unshift({data: "nochange", label: daysDif + " days"});
		watchValue = wlExpiry;
	}
	//Create WLDropdown horizontal layout
	let hzLayoutWLD = new OO.ui.HorizontalLayout();
	hzLayoutB.addItems([hzLayoutWLD]);
	//Watchlist dropdown
	let watchDropdown = new OO.ui.DropdownInputWidget({
		value: watchValue,
		options: watchOptions,
		disabled: !isWatched
	});
	hzLayoutWLD.addItems([watchDropdown]);
	//Submit response button
	let submitB = new OO.ui.ButtonWidget({
		icon: "checkAll",
		flags: ["primary", "progressive"],
		label: "Submit",
		title: "Submit the response",
		disabled: true
	});
	submitB.on("click", function () {
		let newResponse = responses[dropMenu.getLabel()];
		let newText = inputText.value;
		let isAns = toggleAns.selected;
		let newType = typeChange.value;
		let newTargets = targetPages.getValue();
		let targets = targetPages.items;
		let targetChange = false;
		let wlChange = toggleWL.selected;
		let wlVals = watchDropdown.value;
		if (wlChange) {
			wlChange = "watch";
			if (wlVals == "nochange") {
				wlChange = "nochange";
			}
		} else {
			wlChange = "unwatch";
		}
		if (typeof (newResponse) != "undefined") {
			execute(responseTable, newResponse, newText, isAns, newType, newTargets, wlChange, wlVals);
		} else if (typeChange.value != typeChange.defaultValue) {
			execute(responseTable, nonResponses.ChangeLevel, "", isAns, newType, newTargets, wlChange, wlVals);
		}
		if (targets.length == pageLinks.length) {
			for (let item = 0; item < targets.length; item++) {
				if (targets[item].data != pageLinks[item]) {
					targetChange = true;
				}
			}
		} else {
			targetChange = true;
		}
		if (targetChange) {
			execute(responseTable, nonResponses.ChangeTarget, "", isAns, newType, newTargets, wlChange, wlVals);
		}
	});
	hzLayoutB.addItems([submitB]);
}

function showOutput(inputText, replyOption, tableRow, template) {
	var restTransform = "https://en.wikipedia.org/api/rest_v1/transform/wikitext/to/html/" + encodePageName;
	let preview = "";
	let newText = inputText.value;
	if (typeof (inputText) == 'string') {
		newText = inputText;
	}
	template = dataERT.protections[template][1];
	if (typeof (replyOption) != "undefined") {
		preview += "{{" + template + replyOption[0] + "}} ";
	}
	if (newText != "" && typeof (newText) != "undefined") {
		preview += newText + " ";
	}
	if (preview != "") {
		let nickname = " " + mw.user.options.values.nickname;
		if (nickname == " ") {//Create default signature if no nickname
			nickname = mw.user.getName();
			nickname = " [[User:" + nickname + "|" + nickname + "]] ([[User talk:" + nickname + "|talk]])";
		}
		let dateObj = new Date();
		let dateNow = dateObj.toLocaleDateString('en-GB', {
			timeZone: 'UTC',
			year: 'numeric',
			month: 'long',
			day: 'numeric'
		});
		let timeNow = dateObj.toLocaleTimeString('en-GB', {timeZone: 'UTC', hour: '2-digit', minute: '2-digit'});
		preview += nickname + " " + timeNow + ", " + dateNow + " (UTC)";
		preview = preview.replaceAll(/{{subst:/gi, "{{");
		$.post(restTransform, 'wikitext=' + encodeURIComponent(preview) + '&body_only=true',
			function (html) {
				if (inputText.value != "" || typeof (replyOption) != "undefined") {//Stops preview appearing with empty input box
					tableRow.style = "padding:8px 1em 2px;";
					tableRow.children[1].innerHTML = html;
				}
			}
		);
	} else {
		tableRow.style = "display:none;";
	}
}

if (editRequestBoxes.length != 0) {
	let pageID = mw.config.values.wgArticleId;
	api.get({
		action: "query",
		prop: "info",
		pageids: pageID,
		inprop: "watchers|visitingwatchers|watched",
		format: "json"
	}).done(function (data) {
		data = data.query.pages[pageID];
		let watchers = data.watchers;
		let visiting = data.visitingwatchers;
		watchStatus = [data.watched, data.watchlistexpiry];
		if (typeof (watchers) == "undefined") {
			pageWatchers += "less than 30";
		} else {
			pageWatchers += watchers;
		}
		pageWatchers += " users watching this page (";
		if (typeof (visiting) == "undefined") {
			pageWatchers += "0";
		} else {
			pageWatchers += visiting;
		}
		pageWatchers += " have viewed recent edits).";
	});
	var jsonERTURL = "https://en.wikipedia.org/w/index.php?title=User:Terasail/Edit_Request_Tool.json&action=raw&ctype=text/json";
	$.getJSON(jsonERTURL, function (newData) {
		dataERT = newData;
		mw.loader.load(["oojs-ui-core", "oojs-ui-widgets", "oojs-ui-windows"]);
		mw.loader.load(["oojs-ui.styles.icons-interactions", "oojs-ui.styles.icons-moderation", "oojs-ui.styles.icons-user", "oojs-ui.styles.icons-content", "oojs-ui.styles.icons-editing-core", "oojs-ui.styles.icons-editing-advanced"]);
	});
	for (let i = 0; i < editRequestBoxes.length; i++) {
		let currentBox = editRequestBoxes[i].children[0]; //The tbody tag for the box
		if (typeof (currentBox.parentElement.dataset.origlevel) != "undefined") {
			let isSmall = false;
			if (editRequestBoxes[i].id == "") {
				isSmall = true;
			}
			let replyButton = new OO.ui.ButtonWidget({
				icon: "edit",
				flags: ["progressive"],
				label: "Respond",
				invisibleLabel: isSmall,
				title: "Respond to the edit request."
			});
			replyButton.on("click", function () {
				addButtons(currentBox, replyButton);
				replyButton.setDisabled(true);
			});
			replyButton.$element[0].style = "margin:2px 0";
			if (isSmall) {
				$(currentBox.children[0].children[0]).append(replyButton.$element);
			} else {
				$(currentBox).append('<tr><td colspan=2><div style="display: flex; justify-content: center;"></div></td></tr>');
				$(currentBox.children[1].children[0].children[0]).append(replyButton.$element);
			}
		}
	}
}
//</nowiki>[[Category:Wikipedia scripts]]