// <nowiki>
/*
* Вынесение на КБУ (+ отсроченное), КУ (+ множественное, + оставлено), КУЛ, КПМ.
* Подключение (на своём /common.js):
* const userAlertEnabled = 0 // указать, чтоб не уведомлять автора
*/
$(document).ready(function() {
const config = mw.config.get(['skin', 'wgNamespaceIds', 'wgFormattedNamespaces', 'wgNamespaceNumber', 'wgPageName', 'wgPageContentModel', 'wgIsRedirect', 'wgUserName']);
// Пространства имён, в которых будет включён удалятор
const namespacesForRemoval = (typeof g_rm_namespaces === 'undefined') ? [0, 2, 4, 6, 10, 14, 100, 104, 828] : g_rm_namespaces;
// Все кнопки меню
const menuButtons = {
imp: 'КУЛ',
rnm: 'КПМ',
tRm: 'КУ',
mRm: 'Много КУ',
fRm: 'КБУ',
merge: 'КОБ',
split: 'КРАЗД',
recov: 'ВУС',
ret: 'Оставить',
noRnm: 'Не переименовано'
};
// Доступные кнопки
const availableActions = (typeof g_rm_actions === 'undefined') ? Object.keys(menuButtons) : g_rm_actions;
// Переменная, управляющая функцией оповещения создателя статьи
let userAlertEnabled = (typeof g_user_alert === 'undefined') ? 0 : g_user_alert;
// Индикатор ошибки API
let isError;
// Здесь хранятся параметры номинации
let nominationParams;
// Возможные разделы КБУ в зависимости от пространства имён
const fastRemovePrefix = (config.wgIsRedirect ? 'ОП' : 'О') + ({
0: 'Сd',
2: 'У',
3: 'У',
6: 'Ф',
14: 'К'
}[config.wgNamespaceNumber] || '');
// Список всех доступных причин КБУ
const fastRemoveReasons = [
['подст:ds', 'ds Отсроченное'],
['уд-бессвязно', 'О1 Бессвязный текст'],
['уд-тест', 'О2 Тестовая страница'],
['уд-ванд', 'О3 Вандальная страница'],
['уд-повторно', 'О4 Уже удалялось'],
['уд-автор', 'О5 По просьбе автора'],
['уд-обс', 'О6 Ненужная подстраница'],
['уд-переим', 'О7 Для переименования', 'страницу'],
['уд-дубль', 'О8 Дубликат', 'страницу'],
['уд-реклама', 'О9 Реклама или спам'],
['db-badtalk', 'О10 Нецелевая СО'],
['уд-копивио', 'О11 Нарушение АП', 'ссылку'],
['уд-пусто', 'С1 Пусто или коротко'],
['уд-иностр', 'С2 Не на русском'],
['уд-ссылки', 'С3 Лишь ссылки'],
['уд-нз', 'С5 Явно незначимо'],
['уд-в никуда', 'П1 Перенапр. в никуда'],
['db-redirspace', 'П2 Межпростр. перенапр.'],
['уд-опечатка', 'П3 Перенапр. с опечаткой'],
['уд-падеж', 'П4 Не именительный падеж'],
['уд-смысл', 'П5 Неверное перенапр.'],
['db-redirtalk', 'П6 Перенапр. на СО'],
['db-duplicate', 'Ф1 Копия файла', 'файл'],
['db-badimage', 'Ф2 Повреждённый файл'],
['подст:nld', 'Ф3 Нет данных о лицензии'],
['подст:nsd', 'Ф3 Нет данных о источнике'],
['подст:nad', 'Ф3 Нет данных о авторе'],
['подст:dd', 'Ф3 Сомнительные данные файла'],
['подст:ofud', 'Ф4 Неиспользуемый КДИ'],
['подст:dfud', 'Ф5 Нет КДИ'],
['db-badfairuse', 'Ф6 Неоправданное КДИ'],
['NCT', 'Ф8 Есть на Складе', 'файл'],
['подст:Nothost', 'Ф9 Файл — ВП:НЕХОСТИНГ'],
['уд-пусткат', 'К1 Пустая категория'],
['уд-перекат', 'К2 Переименованная кат.', 'категорию'],
['уд-владелец', 'У1 По желанию владельца'],
['уд-анон', 'У2 Устаревшая СО анонима'],
['уд-несущ', 'У3 Несуществующий участник'],
['уд-нецелевое', 'У4 Нецелевое использ. ЛП'],
['уд-неактив', 'У5 Подстраница неактивного'],
['db', 'Особый случай', 'причину']
].filter(arr => fastRemovePrefix.indexOf(arr[1].charAt(0)) >= 0);
// Функция запроса к API
const makeApiRequest = (params, mode, callback) => {
params.format = 'json';
params.token = mw.user.tokens.get('csrfToken');
params.action = mode;
$.post('/w/api.php', params, callback);
};
// Функция получения даты
const getCurrentDate = (customDate) => {
const date = customDate ? new Date(customDate) : new Date();
return [date.toISOString().substr(0, 10), `${date.getUTCDate()} ${
'января,февраля,марта,апреля,мая,июня,июля,августа,сентября,октября,ноября,декабря'.split(',')[date.getUTCMonth()]
} ${date.getUTCFullYear()}`];
};
// Функция получения <input>
const createInputField = (id, placeholder, isHidden) => {
return `<input id="${id}" type="${isHidden ? 'hidden' : 'text'}" placeholder="${placeholder}" class="messagebox">`;
};
// Определяет СО страницы
const getDiscussionPage = (pageName) => {
const match = /([^:]*:)?(.*)/.exec(pageName);
if (match[1]) {
const namespace = config.wgNamespaceIds[match[1].slice(0, -1).toLowerCase().replace(/ /g, '_')];
if (namespace !== undefined) {
return `${config.wgFormattedNamespaces[namespace | 1]}:${match[2]}`;
}
}
return `Обсуждение:${pageName}`;
};
// Функция получения текста страницы
const fetchPageText = (pageName, callback) => {
makeApiRequest({
prop: 'wikitext',
page: pageName
}, 'parse', (response) => {
callback(response.parse ? response.parse.wikitext['*'] : null);
});
};
// Функция отправки уведомления пользователю
const notifyUser = (pageName, callback) => {
makeApiRequest({
prop: 'revisions',
rvprop: 'user',
rvdir: 'newer',
titles: pageName
}, 'query', (response) => {
const pages = response.query.pages;
if (!('-1' in pages)) {
const revision = pages[Object.keys(pages)[0]].revisions[0];
if (!('anon' in revision) && !revision.userhidden && (revision.user && revision.user !== config.wgUserName)) {
makeApiRequest({
title: `оу:${revision.user}`,
section: 'new',
sectiontitle: `Удалятор: [[:${pageName}]]`,
summary: nominationParams.summary,
text: `Страница [[:${pageName}]], созданная вами, ${nominationParams[3] ? '' : 'предложена '}${nominationParams[1]}. ${
nominationParams.place ? `Обсуждение — на странице [[${nominationParams.place}#${nominationParams.sectionNW}]]. ` : ''
}~~\~~<br><small>Это автоматическое уведомление, сгенерированное скриптом «Удалятор».</small>`
}, 'edit', (response) => {
callback(response.error);
});
} else {
callback('не нужно');
}
} else {
callback('страница удалена');
}
});
};
// Функция работы с текстом статьи
const modifyArticleContent = (pageName, callback) => {
if (/(noRnm|ret)/g.test(nominationParams[0])) {
closeNomination(pageName, callback);
} else {
openNomination(pageName, callback);
}
};
// Закрытие номинации (снять в статье, установить на СО)
const closeNomination = (pageName, callback) => {
fetchPageText(pageName, (articleText) => {
const templateMatch = RegExp(`\{\{(${nominationParams[3]})\\|(\\d{4}-\\d\\d-\\d\\d)\\|?(.*?)}}`, 'gi').exec(articleText);
if (templateMatch === null) {
callback({
code: 'ошибка',
info: `Невозможно снять шаблон «${nominationParams[3]}».`
});
return;
}
nominationParams.date = getCurrentDate(templateMatch[2]);
nominationParams.place = `ВП:${nominationParams[3].replace(/\|.*/, '')}/${nominationParams.date[1]}`;
if (nominationParams[0] === 'noRnm') {
nominationParams.sectionNW = `${pageName} → ${templateMatch[3]}`;
nominationParams.templateParams = `${pageName}|${templateMatch[3]}`;
}
if (nominationParams[0] === 'ret') {
nominationParams.sectionNW = templateMatch[3].length ? templateMatch[3] : pageName;
nominationParams.templateParams = `l1=${nominationParams.sectionNW}`;
}
nominationParams.summary = `Удалятор: номинация [[${nominationParams.place ? `${nominationParams.place}#` : ''}${nominationParams.sectionNW}]] — ${nominationParams[2]}`;
makeApiRequest({
summary: nominationParams.summary,
title: getDiscussionPage(pageName),
prependtext: `{{${nominationParams[2]}|${nominationParams.date[1]}|${nominationParams.templateParams}}}\n`
}, 'edit');
articleText = articleText.replace(RegExp(`(<noin.*?>)?\{\{(${nominationParams[3]})\\|.*?}}\n?(<\/noin.*?>)?\n?`, 'gi'), '');
makeApiRequest({
title: pageName,
text: articleText,
summary: nominationParams.summary
}, 'edit', (response) => {
callback(response.error);
});
});
};
// Открытие номинации (установка шаблона в статье)
const openNomination = (pageName, callback) => {
fetchPageText(pageName, (articleText) => {
if (articleText === null) {
callback({
code: 'ошибка',
info: `Страница «${pageName}» не существует.`
});
return;
}
let template = '';
if (nominationParams[0] === 'fRm') {
template = `${fastRemoveReasons[$('#rmSel').val()][0]}|1=${$('#fiRm').val()}`;
} else {
template = nominationParams.templateParams;
if (nominationParams[0] === 'merge') {
template = (`|${template}|`).replace(`|${pageName}|`, '|').slice(1, -1);
}
template = `${nominationParams[2]}|${nominationParams.date[0]}${template.length ? `|${template}` : ''}`;
}
makeApiRequest({
title: pageName,
text: (template.length ? `<noinclude>{{${template}}}\n</noinclude>` : '') + articleText,
summary: nominationParams.summary
}, 'edit', (response) => {
callback(response.error);
});
});
};
// Функция установки номинации на соответствующую страницу
const setNomination = (callback) => {
makeApiRequest({
title: nominationParams.place,
createonly: '1',
text: `{{${nominationParams[4]}-Навигация}}\n`,
summary: 'Удалятор: автоматическая шапка',
}, 'edit', (response) => {
makeApiRequest({
title: nominationParams.place,
section: 'new',
sectiontitle: nominationParams.section,
summary: nominationParams.summary,
text: `${nominationParams.msg} ~~\~~`
}, 'edit', (response) => {
callback(response.error);
});
});
};
// Заполнение параметров номинации
const setNominationParams = () => {
const pageName = config.wgPageName.replace(/_/g, ' '),
title = $('#rmHeader').val(),
title2 = $('#rmHeader2').length ? $('#rmHeader2').val() : '',
message = $('#rmMsg').val(),
pages = [pageName];
if (/(noRnm|ret)/g.test(nominationParams[0])) {
return pages;
}
nominationParams.date = getCurrentDate();
nominationParams.msg = message ? message.trim() : '';
nominationParams.place = nominationParams[0] !== 'fRm' ? `ВП:${nominationParams[2]}/${nominationParams.date[1]}` : '';
nominationParams.section = nominationParams[0] === 'mRm' ? title : `[[:${pageName}]]`;
nominationParams.templateParams = '';
if (nominationParams[0] === 'rnm') {
nominationParams.templateParams = title;
nominationParams.section += ` → [[:${title}]]`;
} else if (nominationParams[0] === 'merge') {
nominationParams.templateParams = `${pageName}|${title}`;
nominationParams.section += ` и [[:${title}]]`;
} else if (nominationParams[0] === 'split') {
nominationParams.templateParams = `[[:${title}]]${title2 ? ` и [[:${title2}]]` : ''}`;
nominationParams.section += ` → ${nominationParams.templateParams}`;
}
nominationParams.sectionNW = nominationParams.section.replace(/\[\[:/g, '').replace(/]]/g, '');
nominationParams.summary = `Удалятор: номинация [[${nominationParams.place ? `${nominationParams.place}#` : ''}${nominationParams.sectionNW}]]${nominationParams[0] === 'fRm' ? ` ${nominationParams[2]}` : ''}`;
if (nominationParams[0] === 'mRm') {
pages.length = 0;
nominationParams.msg = `=== По всем ===\n${nominationParams.msg}`;
for (let i = 4; i >= 0; i--) {
const page = $(`#rmArticle${i}`).val();
if (page.length) {
pages.push(page);
nominationParams.msg = `=== [[:${page}]] ===\n${nominationParams.msg}`;
}
}
} else if (nominationParams[0] === 'merge') {
pages.push(title);
} else if (nominationParams[0] === 'recov') {
pages.length = 0;
}
return pages;
};
// Вывод сообщений об ошибках
const logError = (message, error) => {
if (error && error.code) isError = 1;
$('#rmWindow').append(
`<br>${message} — ${error ? (error.code ? `<span class="error"><small>${error.code}: ${error.info}</small></span>` : error) : 'OK'}`
);
};
// Обработка массива страниц
const processPages = (pages) => {
if (pages.length) {
const page = pages.pop();
modifyArticleContent(page, (error) => {
logError(`Правка статьи «${page}»`, error);
if (isError) {
finalizeWindow();
return;
}
if (userAlertEnabled) {
notifyUser(page, (error) => {
logError('Уведомление создателя', error);
processPages(pages);
});
} else {
processPages(pages);
}
});
} else {
if (nominationParams[4]) {
setNomination((error) => {
logError('Запись номинации', error);
finalizeWindow();
});
} else {
finalizeWindow();
}
}
};
// Вызывается, когда всё сделано
const finalizeWindow = () => {
if (isError) {
$('.mw-small-spinner').remove();
$('#rmWindow')
.append('<p class="error">При выполнении скрипта случились ошибки. Поправьте всё, что надо, вручную.</p>')
.children().prop('disabled', false);
$('#rmBtn').attr('disabled', 1);
$('#rmClose').text('Закрыть');
} else {
location.reload();
}
};
// Функция создания модального окна
const showModal = () => {
let content = '';
if (nominationParams[0] === 'mRm') {
content += createInputField('rmHeader', 'Заголовок номинации');
for (let i = 0; i < 5; i++) {
content += createInputField(`rmArticle${i}`, `Статья${i + 1}`);
}
}
if (nominationParams[0] === 'fRm') {
content += '<select id="rmSel" class="messagebox">';
fastRemoveReasons.forEach((reason, index) => {
content += `<option value="${index}">${reason[1]}</option>`;
});
content += '</select>' + createInputField('fiRm', '', 1);
}
if (nominationParams[0] === 'rnm') {
content += createInputField('rmHeader', 'Новое название');
}
if (nominationParams[0] === 'merge') {
content += createInputField('rmHeader', 'Объединить с…');
}
if (nominationParams[0] === 'split') {
content += createInputField('rmHeader', 'Разделить на эту');
content += createInputField('rmHeader2', 'И на эту');
}
if (nominationParams[4]) {
content += '<textarea id="rmMsg" placeholder="Текст номинации без «~~\~~»." rows="4"></textarea>';
}
$('#content').prepend(
`<div id="rmWindow" style="padding:2em;margin:1em;border:1px solid #985; background: #fec;">
<h1>Удалятор: ${nominationParams[2]}</h1>${content}
<br><label><input name="rmUAlert" type="checkbox" ${userAlertEnabled ? 'checked' : ''}>Оповестить автора</label><br>
<button id="rmBtn" class="mw-ui-button">Отправить</button><button id="rmClose" class="mw-ui-button">Отмена</button>
</div>`
);
if (nominationParams[0] === 'mRm') $('#rmArticle0').val(config.wgPageName.replace(/_/g, ' '));
$('#rmSel').change(function() {
const reason = fastRemoveReasons[this.value][2];
$('#fiRm').attr({
type: reason ? 'text' : 'hidden',
placeholder: `Укажите ${reason}`
});
});
$('#rmClose').click(() => {
$('#rmWindow').remove();
});
$('#rmBtn').click(() => {
$('#rmWindow')
.append('<b class="mw-small-spinner"></b>')
.children().attr('disabled', '1');
userAlertEnabled = $('[name="rmUAlert"]').is(':checked');
const pages = setNominationParams();
processPages(pages);
});
// Реализация ctrl+enter события
$(window).keydown((e) => {
if (e.ctrlKey && e.keyCode === 13) {
$('#rmBtn').click();
}
});
};
// Добавление выпадающего меню в существующий список
if ((namespacesForRemoval.indexOf(config.wgNamespaceNumber & ~1) >= 0) && (config.wgPageContentModel === 'wikitext')) {
// Создаем основной пункт меню
const newLi = $('<li>')
.attr('style', 'position:relative;')
.attr('id', 'mm-page-deletor')
.addClass('mm-submenu-wrapper mw-list-item')
.html('<a style="font-weight: bold"><span>Удалятор…</span></a>');
// Создаем подменю
const submenuUl = $('<ul>')
.addClass('menu mm-submenu vector-menu-content-list')
.css({
position: 'absolute',
left: '170.578px',
display: 'none'
});
// Заполняем подменю пунктами
availableActions.forEach(action => {
const menuItem = $('<li>')
.addClass('mm-item mw-list-item')
.html(`<a href="#"><span>${menuButtons[action]}</span></a>`)
.click(function(e) {
e.preventDefault();
const actionType = action;
nominationParams = (
actionType === 'imp' ? [actionType, 'к срочному улучшению', 'к улучшению', '', 'КУЛ'] :
actionType === 'rnm' ? [actionType, 'к переименованию', 'к переименованию', '', 'КПМ'] :
actionType === 'tRm' ? [actionType, 'к удалению', 'к удалению', '', 'КУ'] :
actionType === 'mRm' ? [actionType, 'к удалению', 'к удалению', '', 'КУ'] :
actionType === 'merge' ? [actionType, 'к объединению с другой', 'к объединению', '', 'КОБ'] :
actionType === 'split' ? [actionType, 'к разделению', 'к разделению', '', 'КР'] :
actionType === 'fRm' ? [actionType, 'к [[ВП:КБУ|быстрому удалению]]', 'к быстрому удалению'] :
actionType === 'recov' ? [actionType, '', 'к восстановлению', '', 'ВУС'] :
actionType === 'ret' ? [actionType, 'оставлена', 'оставлено', 'к удалению|ку'] :
actionType === 'noRnm' ? [actionType, 'не переименована', 'не переименовано', 'к переименованию|кпм|rename'] :
0
);
isError = 0;
showModal();
});
submenuUl.append(menuItem);
});
// Добавляем подменю к основному пункту
newLi.append(submenuUl);
// Обработчики для показа/скрытия подменю
newLi.hover(
function() { $(this).find('ul').show(); },
function() { $(this).find('ul').hide(); }
);
// Вставляем новый пункт в начало списка
$('.vector-menu-content-list.mm-menu').prepend(newLi);
}
});
// </nowiki>