User:Terasail/Edit Request Tool.js

/* 	Edit Request Tool Created by: Terasail var dataERT; var editRequestBoxes = $('.editrequest'); var editRequests = []; for (let i = 0; i < editRequestBoxes.length; i++) { if (typeof(editRequestBoxes[i].attributes['data-origlevel']) != 'undefined') { if (editRequestBoxes[i].id == "") { $(editRequestBoxes[i].children[0].children[0].children[0]).append(' '); } else { $(editRequestBoxes[i].children[0]).append('  '); }		editRequests.push(editRequestBoxes[i]); } }

if (editRequests.length > 0) { mw.loader.using(["oojs-ui-core", "oojs-ui-widgets", "oojs-ui-windows"]).done(function {		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"]);		loadERTool;		$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Terasail/Edit_Request_Tool.json&action=raw&ctype=text/json", function (newData) { dataERT = newData; });	}); }

async function loadERTool { // Get page watchers, visitors and user watch status. let watchStatus = []; let watchQuery = await ApiGetERT({		action: "query",		prop: "info",		pageids: mw.config.get("wgArticleId"),		inprop: "watchers|visitingwatchers|watched",		format: "json"	}); let watchData = watchQuery.query.pages[mw.config.get("wgArticleId")]; let watched = watchData.watched; let expiry = watchData.watchlistexpiry; if (expiry) { watched = Math.ceil((new Date(expiry).getTime - Date.now) / 1000 / 60 / 60 / 24) + " days"; }	watchStatus.push(watchData.watchers || "less than 30", watchData.visitingwatchers || "<30", watched); //Increment through all edit requests & add respond button for (let i = 0; i < editRequests.length; i++) { let responseCell = $('.response-cell-ert')[i]; let smallButton = false; if (responseCell.tagName == "DIV") { smallButton = true; }		let respondButton = new OO.ui.ButtonWidget({			icon: "edit",			label: "Respond",			flags: "progressive",			title: "Open the response menu for this request",			invisibleLabel: smallButton,		}).on("click", function {			loadERTResponse(editRequests[i], respondButton, watchStatus);			respondButton.setDisabled(true);		}); respondButton.$element[0].style = "margin:5px"; $(responseCell).append(respondButton.$element); } }

function loadERTResponse(editRequest, respondButton, watchStatus) { let boxType = editRequest.dataset.origlevel; boxType = boxType.replace("full", "fully"); $(' ').insertAfter(editRequest); let responseBox = editRequest.nextElementSibling; let responseQuick = $(responseBox).find('.response-quick')[0]; let responseCustom = $(responseBox).find('.response-custom')[0]; let responsePreview = $(responseBox).find('.response-preview')[0]; let responseControls = $(responseBox).find('.response-controls')[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"; $(responseCustom).append(typeChange.$element); //Create target page list let boxLinks = editRequest.getElementsByClassName("mbox-text")[0].getElementsByClassName("external text"); let pageTargets = []; 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") { pageTargets[pageTargets.length] = boxLinks[c1].innerHTML; }		}	} else {//Closed request boxLinks = editRequest.getElementsByClassName("mbox-text")[0].getElementsByTagName("A"); for (let c2 = 1; c2 < boxLinks.length; c2++) { pageTargets[pageTargets.length] = boxLinks[c2].title; }		if (pageTargets.length == 0) { pageTargets = mw.config.get("wgPageName").replace(/(_talk|Talk:)/,"").replaceAll("_", " "); }	}	let targetPages = new OO.ui.TagMultiselectWidget({		placeholder: 'Target Pages',		allowArbitrary: true,		selected: pageTargets	}); targetPages.on("change", function {		submitB.setDisabled(false);	}); targetPages.$element[0].style = "text-align:left; margin:5px auto"; $(responseCustom).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"; $(responseCustom).append(dropMenu.$element); dropMenu.on("labelChange", function {		submitB.setDisabled(false);		previewERT(inputText, responses[dropMenu.getLabel], responsePreview, 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"; $(responseCustom).append(inputText.$element); inputText.on("change", function (newText) {		previewERT(inputText, responses[dropMenu.getLabel], responsePreview, 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'})]); $(responseQuick).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 {		$(responseBox).find("tr").each(function(_, row) { if ($(row).find('.response-quick').length == 0) { row.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 { saveResponseERT([editRequest, responseQuick, responsePreview, responseControls], nonResponses.Remove, "", null, typeChange.defaultValue, targetPages.getValue, "nochange", ""); });		hzLayoutT.addItems([remSec]);		hzLayoutT.addItems([cancelB]);	}); hzLayoutT.addItems([remove]); //Open & Close button if (editRequest.attributes[2].localName != "data-origlevel") { let closeB = new OO.ui.ButtonWidget({			icon: "unFlag",			invisibleLabel: true,			title: "Mark as answered"		}); closeB.on("click", function {			saveResponseERT([editRequest, responseQuick, responsePreview, responseControls], 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 {			saveResponseERT([editRequest, responseQuick, responsePreview, responseControls], 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 {			saveResponseERT([editRequest, responseQuick, responsePreview, responseControls], 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'})]); $(responseControls).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 {		respondButton.setDisabled(false);		responseBox.remove;	}); hzLayoutB.addItems([cancelB]); //Watchlist dropdown let watchOptions = [{data: "infinite", 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 watchValue = "infinite"; if (!!watchStatus[2]) { watchOptions.unshift({data: "nochange", label: watchStatus[2]}); watchValue = "nochange"; }	let watchlistLayout = new OO.ui.HorizontalLayout; let watchlistDropdown = new OO.ui.DropdownInputWidget({		value: watchValue,		options: watchOptions,		disabled: (watchStatus[2] == undefined)	}); watchlistLayout.addItems([watchlistDropdown]); //Watchlist checkbox & label let watchlistCheckbox = new OO.ui.CheckboxInputWidget({		selected: (watchStatus[2] != undefined)	}).on("change", function (newStatus) {		watchlistDropdown.setDisabled(!newStatus);	}); let watchlistLabel = new OO.ui.LabelWidget({label: "Watch this page"}).on("change", function (newStatus) {

});	hzLayoutB.addItems([watchlistCheckbox, watchlistLabel, watchlistLayout]);	//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; if (typeof(newResponse) == "undefined") { newText = ""; newResponse = nonResponses.ChangeLevel; //Assume that it is a template change let newPageTargets = []; targetPages.items.forEach(function(item) {				newPageTargets.push(item.label);			}); if (pageTargets.toString != newPageTargets.toString) { //Check if the page targets were changed instead newResponse = nonResponses.ChangeTarget; }		}		saveResponseERT([editRequest, responseQuick, responsePreview, responseControls], newResponse, newText, toggleAns.selected, typeChange.value, targetPages.getValue, watchlistCheckbox.selected, watchlistDropdown.value); });	hzLayoutB.addItems([submitB]); }

function previewERT(inputText, replyOption, tableRow, template) { var restTransform = "https://en.wikipedia.org/api/rest_v1/transform/wikitext/to/html/" + encodeURIComponent(mw.config.get('wgPageName')); let preview = ""; let newText = inputText.value; if (typeof (inputText) == 'string') { newText = inputText; }	template = dataERT.protections[template][1]; if (typeof (replyOption) != "undefined") { preview += " "; }	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 = " " + 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;"; } }

async function saveResponseERT(requestBox, responseOption, responseText, answered, requestType, targets, watchPage, watchValue) { await new Promise(function(resolve) {		OO.ui.confirm("Confirm in order to reply to this edit request.").done(function(confirmed) { if (confirmed) { resolve; } else { return; }});	});	//Create label box & remove action buttons requestBox[1].innerHTML = ""; requestBox[3].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"; $(requestBox[1]).append(infoBox.$element); //Create loading bar let progressBar = new OO.ui.ProgressBarWidget({		progress: false	}); $(requestBox[1]).append(progressBar.$element); //Set preview for output if (responseOption[0] != "") {//Don't preview a non-response previewERT(responseText, responseOption, requestBox[2], requestType); }	//Find header let header = ""; let sectionIndex = 0; let tempElement = requestBox[0]; let sectionQuery = await ApiGetERT({		action: "parse",		page: mw.config.get("wgPageName"),		prop: "sections"	}); let sections = sectionQuery.parse.sections; do { tempElement = tempElement.previousElementSibling; if (tempElement.classList.contains("mw-heading")) { if (tempElement.parentElement.tagName == "SECTION") { //Need to support both while new parser is being implemented header = $(tempElement).find("h1,h2,h3,h4,h5,h6")[0].id; sectionIndex = parseInt(tempElement.parentElement.dataset.mwSectionId); } else { if (tempElement.getElementsByClassName("mw-headline").length > 0) { //Vector 2022 header = tempElement.getElementsByClassName("mw-headline")[0].id; } else { //Vector Legacy header = $(tempElement).find("h1,h2,h3,h4,h5,h6")[0].id; }				for (let i = 0; i < sections.length; i++) { if (sections[i].anchor == header) { sectionIndex = parseInt(sections[i].index); }				}			}		}	}	while (header == ""); infoBox.setLabel("Processing request — Making changes to the edit request"); let editSummary = "/* " + header.replaceAll("_", " ") + " */ " + responseOption[2] + " (Edit Request Tool)"; let wikitextQuery = await ApiGetERT({		action: "parse",		page: mw.config.get("wgPageName"),		section: sectionIndex,		prop: "wikitext|revid"	}); let wikitext = wikitextQuery.parse.wikitext["*"]; let latestRevision = wikitextQuery.parse.revid; if (responseOption[1] != "Remove") { let editTemplate = "{{Edit " + requestType + "-protected"; for (let c3 = 0; c3 < targets.length; c3++) { editTemplate += "|" + targets[c3]; }		if (answered) { editTemplate += "|answered=yes"; } else { editTemplate += "|answered=no"; }		wikitext = wikitext.replace(/{{ *([SETFI]PER|Edit([ -]?[A-Z]+[ -]?|[- ])Protected)\s*[^}}]*/i, editTemplate); if (responseOption[1] != "Close") { wikitext = wikitext.trim + "\n:"; if (responseOption[0] != "") { wikitext += "{{subst:" + dataERT.protections[requestType][1] + responseOption[0] + "}} "; }			if (responseText != "") { wikitext += responseText.replaceAll(/\s*~\s*/g, "") + " "; }			wikitext += "~"; }	} else { wikitext = ""; editSummary = editSummary.replace(/[^]+\*\/ /, ""); }	infoBox.setType("success"); infoBox.setLabel("Processing request — Saving changes to the talk page."); if (latestRevision != mw.config.values.wgRevisionId) { await new Promise(function(resolve) {			OO.ui.confirm("There has been a new revision to the page, do you wish to continue?").done(function(confirmed) { if (confirmed) { resolve; } else { return; }});		});	}	if (watchPage) { if (watchPage != "nochange") { watchPage = "watch"; }	} else { watchPage = "unwatch"; }	let apiParams = { action: 'edit', title: mw.config.get("wgPageName"), text: wikitext, section: sectionIndex, summary: editSummary, watchlist: watchPage };	if (watchPage == "watch") { apiParams.watchlistexpiry = watchValue; }	let reloadURL = "/w/index.php?title=" + encodeURI(mw.config.get("wgPageName")) + "&type=revision&diff=cur&oldid=prev"; new mw.Api.postWithEditToken(apiParams).done(function {		window.location = reloadURL;	}); }

function ApiGetERT(params) { return new Promise(function(resolve) {		new mw.Api.get(params)		.done(function (data) {resolve(data);})		.fail(function (data) {console.error(data);});	}); } //