Content deleted Content added
English date [Factotum] |
cleanup, bugfixes: adding nomination to page, filtering users to notify [Factotum] |
||
Line 1:
// <nowiki>
// urgent bugs:
// toggle bug shows bad elements
// undefined everywhere - needs logging
// todo: make counter inline, remove progresss and progressElement from editPAge(), more dynamic reatelimit wait.
// counter semi inline; adjust align in createProgressBar()
Line 16 ⟶ 21:
"pageDemoText": "{{subst:Cfd|Category:Bishops}}",
"discussionLinkRegex": /^Wikipedia:Categories for discussion\/Log\/\d\d\d\d \w+ \d\d?#(.+)$/,
"nominationReplacement": [/==== ?NEW NOMINATIONS ?====\s*(?:<!-- ?Please add the newest nominations below this line ?-->)?/, '$&\n\n${nominationText}'],
"userNotificationTemplate": 'Cfd mass notice',
"baseDiscussionPage": 'Wikipedia:Categories for discussion/Log/',
"normaliseFunction": (title) => { return 'Category:' + capitalise(title.replace(/^ *[Cc]ategory:/, '').trim()); },
"actions":
"Delete": {
'prepend': '{{subst:Cfd|${sectionName}}}',
Line 46 ⟶ 52:
},
"displayTemplates": [{
},
Line 67 ⟶ 73:
"pageDemoText": "",
"discussionLinkRegex": /^Wikipedia:Redirects for discussion\/Log\/\d\d\d\d \w+ \d\d?#(.+)$/,
"nominationReplacement": [/<!-- ?Add new entries directly below this line\. ?-->/, '$&\n${nominationText}\n'],
"userNotificationTemplate": 'Rfd mass notice',
"baseDiscussionPage": 'Wikipedia:Redirects for discussion/Log/',
"normaliseFunction": (title) => { return capitalise(title.trim()) },
"actions":
{
'prepend': '{{subst:rfd|${sectionName}|content=\n${pageText}\n}}'
},
"displayTemplate": "{{subst:rfd2|multi=yes|redirect=${pageName}|target={{subst:target of|${pageName}}}}}"
}
const match = /Special:Mass(\w+)/.exec(mw.config.get('wgPageName'))
const XFD = match ? match[1].toUpperCase() : false
const config = XFDconfig[XFD]
function wipePageContent() {
var bodyContent = $('#bodyContent');
if (bodyContent) {
bodyContent.empty();
}
var header = $('#firstHeading');
Line 91 ⟶ 98:
}
$('title').text(`${config.title} - Wikipedia`);
label,
align: 'inline',
classes: ['newnomonly'],
var container = new OO.ui.PanelLayout({
expanded: false
});
var titleLabel = new OO.ui.LabelWidget({
label: $(`<span>${title}</span>`)
});
var infoPopup = makeInfoPopup(info);
var inputField = new OO.ui.MultilineTextInputWidget({
placeholder,
indicator: 'required',
rows: 10,
autosize: true
});
return {
titleLabel,
inputField,
container,
infoPopup,
};
var container = new OO.ui.PanelLayout({
expanded: false
});
var titleLabel = new OO.ui.LabelWidget({
label: title
});
var inputField = new OO.ui.TextInputWidget({
placeholder,
indicator: 'required'
});
container.$element.append(titleLabel.$element, inputField.$element);
return {
titleLabel,
inputField,
container
};
]
return
}
function createNominationToggle() {
var newNomToggle = new OO.ui.ButtonOptionWidget({
data: 'new',
label: 'New nomination',
selected: true
});
var oldNomToggle = new OO.ui.ButtonOptionWidget({
data: 'old',
label: 'Old nomination',
});
var
items: [
newNomToggle,
oldNomToggle
]
}
return {
toggle,
newNomToggle,
oldNomToggle,
}
label: "Foo",
data: "foo"
label,
align: 'inline',
selected: true
var items = Object.keys(config.actions)
.map(action => [action, action]) // [label, data]
.map(action => createMenuOptionWidget(...
var dropdown = new OO.ui.DropdownWidget(
label: 'Mass action',
menu: {
items
}
}
return { dropdown };
]
return
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
});
});
return hrefs;
}
function handlepaste(widget, e) {
var types, pastedData, parsedData;
// Browsers that support the 'text/html' type in the Clipboard API (Chrome, Firefox 22+)
if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) {
// Check for 'text/html' in types list
types = e.clipboardData.types;
if (((types instanceof DOMStringList) && types.contains("text/html")) ||
($.inArray && $.inArray('text/html', types) !== -1)) {
// Extract data and pass it to callback
pastedData = e.clipboardData.getData('text/html');
parsedData = parseHTML(pastedData);
// Check if it's an empty array
if (!parsedData || parsedData.length === 0) {
// Allow the paste event to propagate for plain text or empty array
return true;
}
let confirmed = confirm('You have pasted formatted text. Do you want this to be converted into wikitext?');
if (!confirmed) return true;
processPaste(widget, pastedData);
// Stop the data from actually being pasted
e.stopPropagation();
e.preventDefault();
return false;
}
}
// Allow the paste event to propagate for plain text
return true;
}
function waitForPastedData(widget, savedContent) {
// If data has been processed by the browser, process it
if (widget.getValue() !== savedContent) {
// Retrieve pasted content via widget's getValue()
var pastedData = widget.getValue();
// Restore saved content
widget.setValue(savedContent);
// Call callback
processPaste(widget, pastedData);
}
// Else wait 20ms and try again
else {
setTimeout(function () {
waitForPastedData(widget, savedContent);
}, 20);
}
}
function processPaste(widget, pastedData) {
// Parse the HTML
var parsedArray = parseHTML(pastedData);
let stringOutput = '';
for (const pages of parsedArray) {
stringOutput += pages.join('|') + '\n';
}
widget.insertContent(stringOutput);
}
function getWikitext(pageTitle) {
var api = new mw.Api();
var requestData = {
"action": "query",
"format": "json",
"prop": "revisions",
"titles": pageTitle,
"formatversion": "2",
"rvprop": "content",
"rvlimit": "1",
};
return api.get(requestData).then(function (data) {
var pages = data.query.pages;
return pages[0].revisions[0].content; // Return the wikitext
}).catch(function (error) {
console.error('Error fetching wikitext:', error);
});
}
// function to revert edits - this is hacky, and potentially unreliable
function revertEdits() {
var revertAllCount = 0;
var revertElements = $('.massxfdundo');
if (!revertElements.length) {
$('#massxfdrevertlink').replaceWith('Reverts done.');
} else {
$('#massxfdrevertlink').replaceWith('<span><span id="revertall-text">Reverting...</span> (<span id="revertall-done">0</span> / <span id="revertall-total">' + revertElements.length + '</span> done)</span>');
revertElements.each(function (index, element) {
element = $(element); // jQuery-ify
var title = element.attr('data-title');
var revid = element.attr('data-revid');
revertEdit(title, revid)
.then(function () {
element.text('. Reverted.');
revertAllCount++;
$('#revertall-done').text(revertAllCount);
}).catch(function () {
element.html('. Revert failed. <a href="/wiki/Special:Diff/' + revid + '">Click here</a> to view the diff.');
});
}).promise().done(function () {
$('#revertall-text').text('Reverts done.');
});
}
}
function revertEdit(title, revid, retry = false) {
var api = new mw.Api();
if (retry) {
sleep(1000);
}
var requestData = {
action: 'edit',
title,
undo: revid,
format: 'json'
};
return new Promise(function (resolve, reject) {
api.postWithEditToken(requestData).then(function (data) {
if (data.edit && data.edit.result === 'Success') {
resolve(true);
} else {
console.error('Error occurred while undoing edit:', data);
reject();
}
}).catch(function (error) {
console.error('Error occurred while undoing edit:', error); // handle: editconflict, ratelimit (retry)
if (error == 'editconflict') {
resolve(revertEdit(title, revid, retry = true));
} else if (error == 'ratelimited') {
setTimeout(function () { // wait a minute
resolve(revertEdit(title, revid, retry = true));
}, 60000);
} else {
reject();
}
});
});
}
function getUserData(titles) {
var api = new mw.Api();
return api.get({
action: 'query',
list: 'users',
ususers: titles,
usprop: 'blockinfo|groups', // blockinfo - check if indeffed, groups - check if bot
format: 'json'
}).then(function (data) {
return data.query.users;
}).catch(function (error) {
console.error('Error occurred while fetching page author:', error);
return false;
});
var api = new mw.Api();
return api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'user',
rvdir: 'newer', // Sort the revisions in ascending order (oldest first)
rvlimit: 1,
format: 'json'
}).then(function (data) {
var pages = data.query.pages;
var pageId = Object.keys(pages)[0];
var revisions = pages[pageId].revisions;
if (revisions && revisions.length > 0) {
return revisions[0].user;
} else {
return false;
}
}).catch(function (error) {
console.error('Error occurred while fetching page author:', error);
return false;
});
console.log(titles)
var authorList = [];
var promises = titles.map(function (title) {
return getPageAuthor(title);
});
try {
var api = new mw.Api();
var messageElement = createMessageElement();
messageElement.setLabel((retry) ? $('<span>').text('Retrying ').append($(makeLink(title))) : $('<span>').text('Editing ').append($(makeLink(title)))
progressElement.$element.append(messageElement.$element);
var container = $('.sticky-container');
Line 655 ⟶ 665:
sleep(1000);
}
action: 'edit',
title,
summary,
format: 'json'
};
if (type === 'prepend') { //
requestData.nocreate = 1; // don't create new
// parse title
var targets = titlesDict[title];
for (let i = 0; i < targets.length; i++) {
// we add 1 to i in the replace function because placeholders start from $1 not $0
let placeholder = '$' + (i + 1);
text = text.replace(placeholder, targets[i]);
}
text = text.replace(/\$\d/g, ''); // remove unmatched |$x
requestData.prependtext = text.trim() + '\n\n';
} else if (type === 'append') { // user
requestData.appendtext = '\n\n' + text.trim();
Line 682 ⟶ 692:
requestData.text = text;
}
return new Promise(function (resolve, reject) {
if (window.abortEdits) {
// hide message and return
Line 689 ⟶ 699:
return;
}
api.postWithEditToken(requestData).then(function (data) {
if (data.edit && data.edit.result === 'Success') {
messageElement.setType('success');
messageElement.setLabel(
resolve();
} else {
messageElement.setType('error');
messageElement.setLabel(
console.error('Error occurred while prepending text to page:', data);
reject();
}
}).catch(function (error) {
messageElement.setType('error');
messageElement.setLabel(
console.error('Error occurred while prepending text to page:', error); // handle: editconflict, ratelimit (retry)
if (error == 'editconflict') {
editPage(title, text, summary, progressElement, ratelimitMessage, progress, type, titlesDict, retry = true).then(function () {
resolve();
});
} else if (error == 'ratelimited') {
progress.setDisabled(true);
handleRateLimitError(ratelimitMessage).then(function () {
progress.setDisabled(false);
editPage(title, text, summary, progressElement, ratelimitMessage, progress, type, titlesDict, retry = true).then(function () {
resolve();
});
});
}
else {
reject();
}
});
});
var modify = !(ratelimitMessage.isVisible()); // only do something if the element hasn't already been shown
if (massXFDratelimitPromise !== null) {
return massXFDratelimitPromise;
}
massXFDratelimitPromise =
var remainingSeconds = 60;
var secondsToWait = remainingSeconds * 1000;
console.log('Rate limit reached. Waiting for ' + remainingSeconds + ' seconds...');
ratelimitMessage.setType('warning');
ratelimitMessage.setLabel('Rate limit reached. Waiting for ' + remainingSeconds + ' seconds...');
ratelimitMessage.toggle(true);
var countdownInterval = setInterval(function () {
remainingSeconds--;
if (modify) {
ratelimitMessage.setLabel('Rate limit reached. Waiting for ' + remainingSeconds + ' second' + ((remainingSeconds === 1) ? '' : 's') + '...');
}
if (remainingSeconds <= 0 || window.abortEdits) {
clearInterval(countdownInterval);
massXFDratelimitPromise = null; // reset
ratelimitMessage.toggle(false);
resolve();
}
}, 1000);
// Use setTimeout to ensure the promise is resolved even if the countdown is not reached
setTimeout(function () {
clearInterval(countdownInterval);
ratelimitMessage.toggle(false);
massXFDratelimitPromise = null; // reset
resolve();
}, secondsToWait);
});
return massXFDratelimitPromise;
var progressBar = new OO.ui.ProgressBarWidget();
progressBar.setProgress(0);
var fieldlayout = new OO.ui.FieldLayout(
return {
progressBar, };
}
//
async function runMassXFD() {
Object.keys(XFDconfig).forEach(function (XfD) {
mw.util.addPortletLink
}
if (XFD) {
// Load the required modules
mw.loader.using('oojs-ui').done(function () {
wipePageContent();
onbeforeunload = function() {
return "Closing this tab will cause you to lose all progress.";
};
elementsToDisable = [];
var bodyContent = $('#bodyContent');
mw.util.addCSS(`.sticky-container {
bottom: 0;
width: 100%;
Line 809 ⟶ 821:
overflow-y: auto;
}`);
var nominationToggleObj = createNominationToggle();
var nominationToggle = nominationToggleObj.toggle;
bodyContent.append(nominationToggle.$element);
elementsToDisable.push(nominationToggle);
var rationaleObj = createTitleAndInputField('Rationale:', config.placeholderRationale);
var rationaleContainer = rationaleObj.container;
var rationaleInputField = rationaleObj.inputField;
elementsToDisable.push(rationaleInputField);
var nominationToggleOld = nominationToggleObj.oldNomToggle;
var nominationToggleNew = nominationToggleObj.newNomToggle;
var discussionLinkObj = createTitleAndSingleInputField('Discussion link', config.placeholderDiscussionLink);
var discussionLinkContainer = discussionLinkObj.container;
Line 848 ⟶ 860:
}
}
displayElements();
nominationToggle.on('select', displayElements);
function createActionNomination(actionsContainer, first = false) {
var count
let actionNominationTitle = XFD === 'CFD' ? 'Action batch #' + count : ''
var container = createFieldset(actionNominationTitle);
actionsContainer.append(container.$element);
var actionDropdownObj = createActionDropdown();
var dropdown = actionDropdownObj.dropdown;
elementsToDisable.push(dropdown);
dropdown.$element.css('max-width', 'fit-content');
let demoText = config.pageDemoText
var prependTextObj = createTitleAndInputField('Text to tag the nominated pages with:', demoText, info = 'A dollar sign <code>$</code> followed by a number, such as <code>$1</code>, will be replaced with a target specified in the title field, or if not target is specified, will be removed.');
var prependTextLabel = prependTextObj.titleLabel;
var prependTextInfoPopup = prependTextObj.infoPopup;
var prependTextInputField = prependTextObj.inputField;
elementsToDisable.push(prependTextInputField);
var prependTextContainer = new OO.ui.PanelLayout({
expanded: false
});
var actionObj = createTitleAndInputFieldWithLabel('Action', 'renaming', classes = ['newnomonly']);
var actionContainer = actionObj.container;
var actionInputField = actionObj.inputField;
elementsToDisable.push(actionInputField);
actionInputField.$element.css('max-width', 'fit-content');
if (
prependTextContainer.$element.append(prependTextLabel.$element, prependTextInfoPopup.$element, dropdown.$element, actionContainer.$element, prependTextInputField.$element);
nominationToggle.on('select', function () {
if (nominationToggleOld.isSelected()) {
$('.newnomonly').hide();
if
}
else if (nominationToggleNew.isSelected()
if (
if (
}
});
if (nominationToggleOld.isSelected()) {
if (discussionLinkInputField.getValue().match(config.discussionLinkRegex)) {
sectionName = discussionLinkInputField.getValue().trim();
}
}
else if (nominationToggleNew.isSelected()) {
sectionName = newNomHeaderInputField.getValue().trim();
}
// helper function, makes more accurate.
function replaceOccurence(str, find, replace) {
if (XFD === 'CFD') {
// last occurence
let index = str.lastIndexOf(find);
if (index >= 0) {
return str.substring(0, index) + replace + str.substring(index + find.length);
} else {
return str;
}
} else {
str = str.replace(/\{\{subst:rfd\|/i, '')
return '{{subst:rfd|' + str.replace(find, replace)
} else {
return str.replace(find, replace) // first occurence
}
}
}
var oldSectionName = sectionName;
prependTextInputField.setValue(config.actions.prepend.replace('${sectionName}', sectionName))
}
if (discussionLinkInputField.getValue().match(config.discussionLinkRegex)) {
oldSectionName = sectionName;
sectionName = discussionLinkInputField.getValue().replace(config.discussionLinkRegex, '$1').trim();
var text = prependTextInputField.getValue();
text = replaceOccurence(text, oldSectionName, sectionName);
prependTextInputField.setValue(text);
}
});
newNomHeaderInputField.on('change', function () {
dropdown.on('labelChange', function () {
let actionData = config.actions[dropdown.getLabel()];
prependTextInputField.setValue(actionData.prepend.replace('${sectionName}', sectionName));
actionInputField.setValue(actionData.action);
var titleListObj = createTitleAndInputField(`List of titles (one per line${XFD === 'CFD' ? ', <code>Category:</code> prefix is optional' : ''})`, 'Title1\nTitle2\nTitle3', info = 'You can specify targets by adding a pipe <code>|</code> and then the target, e.g. <code>Example|Target1|Target2</code>. These targets can be used in the tagging step.');
var titleListInputField = titleListObj.inputField;
let handler = handlepaste.bind(this,
let textInputElement =
// Modern browsers. Note: 3rd argument is required for Firefox <= 6
if (textInputElement.addEventListener) {
textInputElement.addEventListener('paste', handler, false);
}
// IE <= 8
else {
textInputElement.attachEvent('onpaste', handler);
}
titleListObj.inputField.$element.on('paste', handlepaste);
if (XFD !== 'CFD') {
// most XfDs don't need multiple actions, they're just delete. so hide unnecessary elements'
actionContainer.$element.hide();
prependTextInfoPopup.$element.hide() // both popups give info about targets which aren't relevant here
}
if (!first && XFD !== 'CFD') {
var removeButton = createRemoveBatchButton();
elementsToDisable.push(removeButton);
removeButton.on('click', function
});
for (i = 0; i < actions.length; i++) {
}
} else {
container.addItems([prependTextContainer,
return
label: 'Action batch #' + count,
};
var actionsContainer = $('<div />');
var actions =
actions.push(createActionNomination(actionsContainer, first = true));
var checkboxObj = createCheckboxWithLabel('Notify
var notifyCheckbox =
var checkboxFieldlayout = checkboxObj.fieldlayout;
bodyContent.append(checkboxFieldlayout.$element);
var multiOptionButton =
multiOptionButton.$element.css('margin-bottom', '10px');
bodyContent.append('<br
multiOptionButton.on('click', () => {
actions.push(createActionNomination(actionsContainer));
if (XFD !== 'CFD')
} else
var categoryTemplateDropdownObj =
categoryTemplateDropdown =
categoryTemplateDropdown.$element.css(
{
'display':
'max-width':
}
);
elementsToDisable.push(categoryTemplateDropdown);
if (nominationToggleOld.isSelected()) categoryTemplateDropdownContainer.$element.hide();
bodyContent.append(categoryTemplateDropdownContainer.$element);
}
var startButton = createStartButton();
elementsToDisable.push(startButton);
bodyContent.append(startButton.$element);
startButton.on('click', function () {
var isOld = nominationToggleOld.isSelected();
var isNew = nominationToggleNew.isSelected();
// First check elements
var error = false;
var regex = config.discussionLinkRegex;
if (isOld) {
if (!(discussionLinkInputField.getValue().trim()) || !regex.test(discussionLinkInputField.getValue().trim())) {
discussionLinkInputField.setValidityFlag(false);
error = true;
} else {
discussionLinkInputField.setValidityFlag(true);
}
} else if (isNew) {
if (!(newNomHeaderInputField.getValue().trim())) {
newNomHeaderInputField.setValidityFlag(false);
error = true;
} else {
newNomHeaderInputField.setValidityFlag(true);
}
if (!(rationaleInputField.getValue().trim())) {
rationaleInputField.setValidityFlag(false);
error = true;
} else {
rationaleInputField.setValidityFlag(true);
}
}
batches = actions.map(function ({ titleListInputField, prependTextInputField, label, actionInputField }) {
if (!(prependTextInputField.getValue().trim()) || !prependTextInputField.getValue().includes('${pageText}')) {
prependTextInputField.setValidityFlag(false);
error = true;
} else {
prependTextInputField.setValidityFlag(true);
}
if (isNew && XFD === 'CFD') {
if (!(actionInputField.getValue().trim())) {
actionInputField.setValidityFlag(false);
error = true;
} else {
actionInputField.setValidityFlag(true);
}
}
if (!(titleListInputField.getValue().trim())) {
titleListInputField.setValidityFlag(false);
error = true;
} else {
titleListInputField.setValidityFlag(true);
}
// Retreive titles, handle dups
var titles = {};
var titleList = titleListInputField.getValue().split('\n');
function normalise(title) {
return config.normaliseFunction(title)
}
titleList.forEach(function (title) {
if (title) {
var targets = title.split('|');
var newTitle = targets.shift();
newTitle = normalise(newTitle);
if (!Object.keys(titles).includes(newTitle)) {
titles[newTitle] = targets.map(normalise);
}
}
});
if (!(Object.keys(titles).length)) {
titleListInputField.setValidityFlag(false);
error = true;
} else {
titleListInputField.setValidityFlag(true);
}
return {
titles,
prependText: prependTextInputField.getValue().trim(),
label,
actionInputField
};
});
if (error) {
return;
}
for (let element of elementsToDisable) {
element.setDisabled(true);
}
$('.remove-batch-button').remove();
var abortButton = createAbortButton();
bodyContent.append(abortButton.$element);
window.abortEdits = false; // initialise
abortButton.on('click', function () {
// Set abortEdits flag to true
if (confirm('Are you sure you want to abort?')) {
abortButton.setDisabled(true);
window.abortEdits = true;
}
});
var allTitles = batches.reduce((allTitles, obj) => {
return allTitles.concat(Object.keys(obj.titles));
}, []);
createAuthorList(allTitles).then(function (authors) {
function processContent(content, titles, textToModify, summary, type, doneMessage, headingLabel) {
if (!Array.isArray(titles)) {
var titlesDict = titles;
titles = Object.keys(titles);
}
var fieldset = createFieldset(headingLabel);
content.append(fieldset.$element);
var progressElement = createProgressElement();
fieldset.addItems([progressElement]);
var ratelimitMessage = createRatelimitMessage();
ratelimitMessage.toggle(false);
fieldset.addItems([ratelimitMessage]);
var progressObj = createProgressBar(`(0 / ${titles.length}, 0 errors)`); // with label
var progress = progressObj.progressBar;
var progressContainer = progressObj.fieldlayout;
// Add margin or padding to the progress bar widget
progress.$element.css('margin-top', '5px');
progress.pushPending();
fieldset.addItems([progressContainer]);
let resolvedCount = 0;
let rejectedCount = 0;
function updateCounter() {
progressContainer.setLabel(`(${resolvedCount} / ${titles.length}, ${rejectedCount} errors)`);
}
function updateProgress() {
var percentage = (resolvedCount + rejectedCount) / titles.length * 100;
progress.setProgress(percentage);
}
function trackPromise(promise) {
return new Promise((resolve, reject) => {
promise
.then(value => {
resolvedCount++;
updateCounter();
updateProgress();
resolve(value);
})
.catch(error => {
rejectedCount++;
updateCounter();
updateProgress();
resolve(error);
});
});
}
return new Promise(async function (resolve) {
var promises = [];
for (const title of titles) {
// RfD needs special handling here, because it wraps around the whole page content
if (XFD === 'RFD' && type === 'prepend') { // prepend implicitly means page tagging, not actually prepend in this case
text = await getWikitext(title);
textToModify = textToModify.replace('${pageText}', text);
type = 'text';
}
var promise = editPage(title, textToModify, summary, progressElement, ratelimitMessage, progress, type, titlesDict);
promises.push(trackPromise(promise));
await sleep(100); // space out calls
await massXFDratelimitPromise; // stop if ratelimit reached (global variable)
}
Promise.allSettled(promises)
.then(function () {
progress.toggle(false);
if (window.abortEdits) {
var abortMessage = createAbortMessage();
abortMessage.setLabel(
content.append(abortMessage.$element);
} else {
var completedElement = createCompletedElement();
completedElement.setLabel(doneMessage);
completedElement.$element.css('margin-bottom', '16px');
content.append(completedElement.$element);
}
resolve();
})
.catch(function (error) {
console.error("Error occurred during title processing:", error);
resolve();
});
});
}
const date = new Date();
const year = date.getUTCFullYear();
const month = date.toLocaleString('en', { month: 'long', timeZone: 'UTC' });
const day = date.getUTCDate();
var summaryDiscussionLink;
var discussionPage = `${config.baseDiscussionPage}${year} ${month} ${day}`;
if (isOld) summaryDiscussionLink = discussionLinkInputField.getValue().trim();
else if (isNew) summaryDiscussionLink = `${discussionPage}#${newNomHeaderInputField.getValue().trim()}`;
const advSummary = ' ([[User:Qwerfjkl/scripts/massXFD.js|via
// WIP, not finished
const categorySummary = 'Tagging page for [[' + summaryDiscussionLink + ']]' + advSummary;
const userSummary = 'Notifying user about [[' + summaryDiscussionLink + ']]' + advSummary;
const userNotification = `{{ subst: ${config.userNotificationTemplate} | ${summaryDiscussionLink} }} ~~~~`;
const nominationSummary =
var batchesToProcess = [];
var newNomPromise = new Promise(function (resolve) {
if (isNew) {
nominationText = `==== ${newNomHeaderInputField.getValue().trim()} ====\n`;
for (const batch of batches) {
var action = batch.actionInputField.getValue().trim();
for (const page of Object.keys(batch.titles)) {
if (XFD == 'CFD'
var targets = batch.titles[page].slice(); // copy array
var targetText = '';
if (targets.length) {
if (targets.length === 2) {
targetText = ` to [[:${targets[0]}]] and [[:${targets[1]}]]`;
}
else if (targets.length > 2) {
var lastTarget = targets.pop();
targetText = ' to [[:' + targets.join(']], [[:') + ']], and [[:' + lastTarget + ']]';
} else { // 1 target
targetText = ' to [[:' + targets[0] + ']]';
}
}
nominationText += `:* '''Propose ${action}''' {{${categoryTemplateDropdown.getValue()}|${page}}}${targetText}\n`;
} else {
nominationText += config.displayTemplate.replaceAll('${pageName}', page) + '\n';
}
}
}
var rationale = rationaleInputField.getValue().trim().replace(/\n/, '<br />');
nominationText += `${XFD === 'CFD' ? ":'''Nominator's rationale:''' " : ''}${rationale} ~~~~`;
var newText;
getWikitext(discussionPage).then(function (wikitext) {
if (!wikitext.match(config.nominationReplacement[0])) {
var nominationErrorMessage = createNominationErrorMessage();
bodyContent.append(nominationErrorMessage.$element);
} else {
batchesToProcess.push({
content: bodyContent,
titles: [discussionPage],
textToModify: newText,
summary: nominationSummary,
type: 'text',
doneMessage: 'Nomination added',
headingLabel: 'Creating nomination'
});
resolve();
}
}).catch(function (error)
} else
});
textToModify:
summary:
doneMessage: 'All pages
headingLabel: 'Editing nominated pages' + ((batches.length > 1) ? ' — ' + batch.label
if (notifyCheckbox.isSelected())
content:
titles:
}
for (const batch of batchesToProcess
await
promise.then(() =>
//
console.error('Error occurred:',
});
});
}
|