Участник:Луффи/import.js: различия между версиями

Материал из Posmotrelisu
Перейти к навигации Перейти к поиску
(Новая страница: «var talk = $("#ca-talk"); if (mw.config.get('wgNamespaceNumber') == 0 && talk.length != 0 && !(talk.hasClass("new") || talk.hasClass("selected"))) { var a = $("a", talk); var name = "Обсуждение:" + $("#firstHeading").text(); $.get("//ru.wikipedia.org/w/index.php", { title: name, action: "render" }, function (data) { var html = $($(data).children()); var isEmpty = true; var isWebCiteArchiver = true; for (var i = 0; i < html....»)
 
 
Строка 1: Строка 1:
var talk = $("#ca-talk");
/*
if (mw.config.get('wgNamespaceNumber') == 0 && talk.length != 0 && !(talk.hasClass("new") || talk.hasClass("selected")))
CC-BY-SA 4.0 Основано на u:higimo/remove.js?oldid=107716412 // v5.0.4
{
Вынесение на КБУ (+ отсроченное), КУ (+ множественное, + оставлено), КУЛ, КПМ.
  var a = $("a", talk);
Подключение (на своём /common.js):
  var name = "Обсуждение:" + $("#firstHeading").text();
var g_user_alert = 0 // указать, чтоб не уведомлять автора
  $.get("//ru.wikipedia.org/w/index.php", { title: name, action: "render" }, function (data)  
importScript('у:Землеройкин/remove.js');
  {
*/
    var html = $($(data).children());
$(document).ready(function() {
    var isEmpty = true;
var config = mw.config.get(['skin', 'wgNamespaceIds', 'wgFormattedNamespaces', 'wgNamespaceNumber', 'wgPageName', 'wgPageContentModel', 'wgIsRedirect', 'wgUserName']),
    var isWebCiteArchiver = true;
/*
    for (var i = 0; i < html.length; i++)  
Пространства имён, в которых будет включён удалятор
    {
*/
      var item = html[i];
rmNamespaces = (typeof g_rm_namespaces == 'undefined') ? [0, 2, 4, 6, 10, 14, 100, 104, 828] : g_rm_namespaces,
      var nodeName = item.nodeName.toLowerCase();
/*
      if (item.nodeType == 1 && (nodeName == "p" || nodeName == "ul" || nodeName == "ol" || nodeName == "dl") && item.textContent != "")
Все кнопки меню
      {
*/
          isEmpty = false;
menu_raw = {imp:'КУЛ', rnm:'КПМ', tRm:'КУ', mRm:'Много КУ', fRm:'КБУ', merge:'КОБ', split:'КРАЗД', recov:'ВУС', ret:'Оставить', noRnm:'Не переименовано'},
          if (item.textContent.indexOf("WebCite Archiver") == -1)
/*
          {
Доступные кнопки
            isWebCiteArchiver = false;
*/
            break;
rmActions = (typeof g_rm_actions == 'undefined') ? Object.keys(menu_raw) : g_rm_actions,
          }
/*
      }
Переменная, управляющая функцией оповещения создателя статьи
    }
*/
    if (isEmpty)
setAlert = (typeof g_user_alert == 'undefined') ? 1 : g_user_alert,
    {
/*
      a.attr("title", "Обсуждение содержания страницы (нет реплик) [alt-t]");
Индикатор ошибки api
      a.css("color", "#643916");
*/
    }
isError,
    else if (isWebCiteArchiver)
/*
    {
Здесь хранятся параметры номинации
      a.attr("title", "Обсуждение содержания страницы (только ботореплики) [alt-t]");
*/
      a.css("color", "#977b98");
param,
    }
/*
  }, "html");
Возможные разделы КБУ в зависимости от пространства имён
}
*/
fastRemovePrefix = (config.wgIsRedirect ? 'ОП' : 'О') + ({0:'Сd', 2:'У', 3:'У', 6:'Ф', 14:'К'}[config.wgNamespaceNumber] || ''),
/*
Список всех доступных причин КБУ
Коды для админов, текст для остальных.
Используется максимально короткий шаблон.
Третий параметр — условие для появления <input>
*/
fastRemove = [
['подст: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(function(arr) {
return fastRemovePrefix.indexOf(arr[1].charAt(0)) >= 0
}),
/*
Функция запроса к API.
Токен и формат каждый раз дополняется перед запросом.
Аргументы:
1. Передаваемые параметры
2. Режим запроса (query, edit или parse)
3. Колбек
void apiReq(object, string, function(result, status))
*/
apiReq = function(par, mode, clbck) {
par.format = 'json'
par.token  = mw.user.tokens.get('csrfToken')
par.action = mode
$.post('/w/api.php', par, clbck)
},
/*
Функция получения даты.
Необходимо, например, для КУ-запросов.
Есть возможность указать собственную дату для подытоживания номинации.
['1051-32-33', '20 апреля 2014'] getDate(string)
*/
getDate = function(s) {
var d = (!!s) ? new Date(s) : new Date()
return [d.toISOString().substr(0, 10), d.getUTCDate() + ' ' +
'января,февраля,марта,апреля,мая,июня,июля,августа,сентября,октября,ноября,декабря'.split(',')[d.getUTCMonth()] +
' ' + d.getUTCFullYear()]
},
/*
Функция получения <input>
* Атрибут «h» используется в КБУ, указывая type
* Атрибут placeholder используется везде, для улучшения UI
* Атрибут id используется везде для получения информации из поля
string getInput(string, string, bool)
*/
getInput = function(id, p, h) {
return '<input id=' + id + ' type=' + (h ? 'hidden' : 'text') + ' placeholder="' + p + '" class=messagebox>'
},
/*
Определяет СО страницы
*/
getSO = function(pg) {
var tmp = /([^:]*:)?(.*)/.exec(pg)
if (tmp[1]) {
var ns = config.wgNamespaceIds[tmp[1].slice(0, -1).toLowerCase().replace(/ /g, '_')]
if (ns != undefined)
return config.wgFormattedNamespaces[ns | 1] + ':' + tmp[2]
}
return 'Обсуждение:' + pg
},
/*
Функция получения текста страницы, null если ошибка
getText(string, function(string))
*/
getText = function(pg, clbck) {
apiReq({
prop: 'wikitext',
page: pg
}, 'parse', function(txt) {
clbck(txt.parse ? txt.parse.wikitext['*'] : null)
})
},
/*
Функция отправки уведомления пользователю
По выполнению вызывает callback
userAlert(string, function(error))
*/
userAlert = function(pg, clbck) {
apiReq({
prop: 'revisions',
rvprop: 'user',
rvdir: 'newer',
titles: pg
}, 'query', function (t) {
var i = t.query.pages
if (!('-1' in i)) {
var rvdata = i[Object.keys(i)[0]].revisions[0]
if (!('anon' in rvdata) && !rvdata.userhidden && (rvdata.user && rvdata.user !== config.wgUserName)) {
apiReq({
title: 'оу:' + rvdata.user,
section: 'new',
sectiontitle: 'Удалятор: [[:' + pg + ']]',
summary: param.sum,
text: 'Страница [[:' + pg + ']], созданная вами, ' + (param[3] ? '' : 'предложена ') + param[1] + '. ' +
(param.place ? 'Обсуждение — на странице [[' + param.place + '#' + param.sectionNW + ']]. ' : '') +
'~~\~~<br><small>Это автоматическое уведомление, сгенерированное [[у:Землеройкин/remove.js|скриптом «Удалятор»]].</small>'
}, 'edit', function(t) {
clbck(t.error)
})
}
else clbck('не нужно')
}
else clbck('страница удалена')
})
},
/*
Функция работы с текстом статьи.
Определяет необходимые шаблоны и устанавливает их на СО или статью.
changeArticle(string, function(object))
*/
changeArticle = function(pg, clbck) {
if (/(noRnm|ret)/g.test(param[0]))
changeArticleDenom(pg, clbck)
else
changeArticleNom(pg, clbck)
},
// Закрытие номинации (снять в статье, установить на СО)
changeArticleDenom = function(pg, clbck) {
getText(pg, function(article) {
var tpl = RegExp('{\{(' + param[3] + ')\\|(\\d{4}-\\d\\d-\\d\\d)\\|?(.*?)}}', 'gi').exec(article)
if (tpl == null) {
clbck({
code: 'ошибка',
info: 'Невозможно снять шаблон «' + param[3] + '».'
})
return
}
param.date = getDate(tpl[2])
param.place = 'ВП:' + param[3].replace(/\|.*/, '') + '/' + param.date[1]
if (param[0] == 'noRnm') { //не переименовано
param.sectionNW = pg + ' → ' + tpl[3]
param.tplpar = pg + '|' + tpl[3]
}
if (param[0] == 'ret') { //оставлено
param.sectionNW = tpl[3].length ? tpl[3] : pg
param.tplpar = 'l1=' + param.sectionNW
}
param.sum = '[[у:Землеройкин/remove.js|Удалятор]]: номинация [[' + (param.place ? param.place + '#' : '') + param.sectionNW + ']] — ' + param[2]
 
apiReq({
summary: param.sum,
title: getSO(pg),
prependtext: '{{' + param[2] + '|' + param.date[1] + '|' + param.tplpar + '}}\n'
}, 'edit')
 
article = article.replace(RegExp('(<noin.*?>)?{\{(' + param[3] + ')\\|.*?}}\n?(<\/noin.*?>)?\n?', 'gi'), '')
apiReq({
title: pg,
text: article,
summary: param.sum
}, 'edit', function(t) {
clbck(t.error)
})
})
},
// Открытие номинации (установка шаблона в статье)
changeArticleNom = function(pg, clbck) {
getText(pg, function(article) {
if (article == null) {
clbck({
code: 'ошибка',
info: 'Страница «' + pg + '» не существует.'
})
return
}
var tpl = ''
if (param[0] == 'fRm') {
tpl = fastRemove[$('#rmSel').val()][0] + '|1=' + $('#fiRm').val()
}
else {
tpl = param.tplpar
if (param[0] == 'merge') {
tpl = ('|' + tpl + '|').replace('|' + pg + '|', '|').slice(1, -1)
}
tpl = param[2] + '|' + param.date[0] + (tpl.length ? '|' + tpl : '')
}
apiReq({
title: pg,
text: (tpl.length ? '<noinclude>{{' + tpl + '}}\n</noinclude>' : '') + article,
summary: param.sum
}, 'edit', function(t) {
clbck(t.error)
})
})
},
/*
Функция установки номинации на соответствующую страницу
void setNominate(callback)
*/
setNominate = function(clbck) {
apiReq({
title: param.place,
createonly: '1',
text: '{{' + param[4] + '-Навигация}}\n',
summary: '[[у:Землеройкин/remove.js|Удалятор]]: автоматическая шапка',
}, 'edit', function(t) {
apiReq({
title: param.place,
section: 'new',
sectiontitle: param.section,
summary: param.sum,
text: param.msg + ' ~~\~~'
}, 'edit', function(t) {
clbck(t.error)
})
})
},
/*
Заполнение параметров номинации (переменная param).
.date — дата номинации
.msg — текст номинации
.place — место обсуждения (ВП:к_чему-то-там... или пусто, если КБУ)
.section — заголовок раздела номинации
.sectionNW — то же, без викификации
.tplpar — параметры шаблона, для установки в статье
.sum — описание правок скрипта
Возвращает массив страниц для обработки (до 5 шт. для МногоКУ, 2 для КОБ, 0 для ВУС, иначе 1)
*/
setParam = function() {
var i,
pg = config.wgPageName.replace(/_/g, ' '),
ttl = $('#rmHeader').val(),
ttl2 = $('#rmHeader2').length ? $('#rmHeader2').val() : '',
msg = $('#rmMsg').val(),
pgs = [pg]
 
if (/(noRnm|ret)/g.test(param[0]))
return pgs
 
param.date = getDate()
param.msg = msg ? msg.trim() : ''
param.place = param[0] != 'fRm' ? 'ВП:' + param[2] + '/' + param.date[1] : ''
 
param.section = param[0] == 'mRm' ? ttl : '[[:' + pg + ']]'
param.tplpar = ''
if (param[0] == 'rnm') {
param.tplpar = ttl
param.section += ' → [[:' + ttl + ']]'
}
else if (param[0] == 'merge') {
param.tplpar = pg + '|' + ttl
param.section += ' и [[:' + ttl + ']]'
}
else if (param[0] == 'split') {
param.tplpar = '[[:' + ttl + ']]' + (ttl2 ? ' и [[:' + ttl2 + ']]' : '')
param.section += ' → ' + param.tplpar
}
 
param.sectionNW = param.section.replace(/\[\[:/g, '').replace(/]]/g, '')
param.sum = '[[у:Землеройкин/remove.js|Удалятор]]: номинация [[' + (param.place ? param.place + '#' : '') + param.sectionNW + ']]' + (param[0] == 'fRm' ? ' ' + param[2] : '')
 
if (param[0] == 'mRm') {
pgs = []
param.msg = '=== По всем ===\n' + param.msg
for (i = 4; i >= 0; i--) {
pg = $('#rmArticle' + i).val()
if (pg.length) {
pgs.push(pg)
param.msg = '=== [[:' + pg + ']] ===\n' + param.msg
}
}
}
else if (param[0] == 'merge') {
pgs.push(ttl)
}
else if (param[0] == 'recov') {
pgs = []
}
return pgs
},
/*
Вывод сообщений об ошибках
*/
logError = function(s, err) {
if (err && err.code) isError = 1
$('#rmWindow').append(
'<br>' + s + ' — ' + (
err ? (err.code ? '<span class="error"><small>' + err.code + ': ' + err.info + '</small></span>' : err)
: 'OK'
)
)
},
/*
Обработка массива страниц
*/
processPages = function(pgs) {
if (pgs.length) {
var pg = pgs.pop()
changeArticle(pg, function(err) {
logError('Правка статьи «' + pg + '»', err)
if (isError) {
finalizeWindow()
return
}
if (setAlert) {
userAlert(pg, function(err) {
logError('Уведомление создателя', err)
processPages(pgs)
})
}
else processPages(pgs)
})
}
else {
if (param[4]) {
setNominate(function(err) {
logError('Запись номинации', err)
// window.open('/wiki/' + param.place + '#' + encodeURI(param.sectionNW.replace(/ /g, '_')))
finalizeWindow()
})
}
else finalizeWindow()
}
},
/*
Вызывается, когда всё сделано
*/
finalizeWindow = function() {
if (isError) {
$('.mw-small-spinner').remove()
$('#rmWindow')
.append('<p class="error">При выполнении скрипта случились ошибки. Поправьте всё, что надо, вручную.')
.children().prop('disabled', false)
$('#rmBtn').attr('disabled', 1)
$('#rmClose').text('Закрыть')
}
else location.reload()
},
/*
Функция создания модального окна
*/
modalHandler = function() {
var i,
content = ''
 
if (param[0] == 'mRm') {
content += getInput('rmHeader', 'Заголовок номинации')
for (i = 0; i < 5; i++) {
content += getInput('rmArticle' + i, 'Статья' + (i + 1))
}
}
if (param[0] == 'fRm') {
content += '<select id=rmSel class=messagebox>'
for (i = 0; i < fastRemove.length; i++) {
content += '<option value=' + i + '>' + fastRemove[i][1] + '</option>'
}
content += '</select>' + getInput('fiRm', '', 1)
}
if (param[0] == 'rnm') {
content += getInput('rmHeader', 'Новое название')
}
if (param[0] == 'merge') {
content += getInput('rmHeader', 'Объединить с…')
}
if (param[0] == 'split') {
content += getInput('rmHeader', 'Разделить на эту')
content += getInput('rmHeader2', 'И на эту')
}
if (param[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>Удалятор: ' + param[2] + '</h1>' + content +
'<br><label><input name="rmUAlert" type=checkbox ' + ((setAlert) ? 'checked' : '') + '>Оповестить автора</label><br>' +
'<button id=rmBtn class=mw-ui-button>Отправить</button><button id=rmClose class=mw-ui-button>Отмена</button>'
)
if (param[0] == 'mRm') $('#rmArticle0').val(config.wgPageName.replace(/_/g, ' '))
 
$('#rmSel').change(function() {
var i = fastRemove[this.value][2]
$('#fiRm').attr({type: (i ? 'text' : 'hidden'), placeholder: 'Укажите ' + i})
})
$('#rmClose').click(function() {
$('#rmWindow').remove()
})
 
$('#rmBtn').click(function() {
$('#rmWindow')
.append('<b class=mw-small-spinner></b>')
.children().attr('disabled', '1')
setAlert = $('[name="rmUAlert"]').is(':checked')
var pgs = setParam()
processPages(pgs)
})
/*
Реализация ctrl+enter события
*/
$(window).keydown(function (e) {
if (e.ctrlKey && e.keyCode == 13)
$('#rmBtn').click()
})
};
/*
Добавление выпадающего меню на все страницы
*/
if ((rmNamespaces.indexOf(config.wgNamespaceNumber & ~1) >= 0) && (config.wgPageContentModel == 'wikitext')) {
var menuLocation = 'p-cactions';
var nextNode = '#ca-move';
if ( config.skin === 'timeless' || config.skin === 'minerva' ) {
menuLocation = 'p-tb';
nextNode = null;
}
if ( config.skin === 'vector' || config.skin === 'vector-2022' ) {
nextNode = null;
var $menuClone = $( '#p-variants' ).eq( 0 ).clone();
$menuClone.attr( 'id', 'p-remove-js' )
.attr( 'aria-labelledby', 'p-remove-js-label' )
.removeClass( 'emptyPortlet' );
$menuClone.find( 'ul' ).empty();
$menuClone.find( 'input' ).attr( 'aria-labelledby', 'p-remove-js-label' );
$menuClone.find( 'label' ).attr( 'id', 'p-remove-js-label' ).find( 'span' ).text( 'Удалятор' );
$( '#p-views' ).after( $menuClone );
menuLocation = 'p-remove-js';
}
for (var a of rmActions) {
var portletLink = mw.util.addPortletLink( menuLocation, '#', menu_raw[ a ], 'ca-remove-' + a, null, null, nextNode );
$( portletLink ).find( 'a' ).click(function( e ) {
e.preventDefault();
var i = $( this ).parent().attr( 'id' ).replace( 'ca-remove-', '' );
param = (
// [текущее действие, комментарий, шаблон/место обсуждения, поддерживаемые шаблоны
i == 'imp' ? [ i, 'к срочному улучшению', 'к улучшению', '', 'КУЛ'] :
i == 'rnm' ? [ i, 'к переименованию', 'к переименованию', '', 'КПМ'] :
i == 'tRm' ? [ i, 'к удалению', 'к удалению', '', 'КУ'] :
i == 'mRm' ? [ i, 'к удалению', 'к удалению', '', 'КУ'] :
i == 'merge'? [ i, 'к объединению с другой', 'к объединению', '', 'КОБ'] :
i == 'split'? [ i, 'к разделению', 'к разделению', '', 'КР'] :
i == 'fRm' ? [ i, 'к [[ВП:КБУ|быстрому удалению]]', 'к быстрому удалению'] :
i == 'recov'? [ i, '', 'к восстановлению', '', 'ВУС'] :
i == 'ret' ? [ i, 'оставлена', 'оставлено', 'к удалению|ку'] :
i == 'noRnm'? [ i, 'не переименована', 'не переименовано', 'к переименованию|кпм|rename'] :
0
)
isError = 0
modalHandler()
});
}
}
})

Текущая версия на 23:44, 2 июля 2023

/*
	CC-BY-SA 4.0 Основано на u:higimo/remove.js?oldid=107716412 // v5.0.4
	Вынесение на КБУ (+ отсроченное), КУ (+ множественное, + оставлено), КУЛ, КПМ.
	Подключение (на своём /common.js):
	var g_user_alert = 0 // указать, чтоб не уведомлять автора
	importScript('у:Землеройкин/remove.js');
*/
$(document).ready(function() {
	var config = mw.config.get(['skin', 'wgNamespaceIds', 'wgFormattedNamespaces', 'wgNamespaceNumber', 'wgPageName', 'wgPageContentModel', 'wgIsRedirect', 'wgUserName']),
		/*
			Пространства имён, в которых будет включён удалятор
		*/
		rmNamespaces = (typeof g_rm_namespaces == 'undefined') ? [0, 2, 4, 6, 10, 14, 100, 104, 828] : g_rm_namespaces,
		/*
			Все кнопки меню
		*/
		menu_raw = {imp:'КУЛ', rnm:'КПМ', tRm:'КУ', mRm:'Много КУ', fRm:'КБУ', merge:'КОБ', split:'КРАЗД', recov:'ВУС', ret:'Оставить', noRnm:'Не переименовано'},
		/*
			Доступные кнопки
		*/
		rmActions = (typeof g_rm_actions == 'undefined') ? Object.keys(menu_raw) : g_rm_actions,
		/*
			Переменная, управляющая функцией оповещения создателя статьи
		*/
		setAlert = (typeof g_user_alert == 'undefined') ? 1 : g_user_alert,
		/*
			Индикатор ошибки api
		*/
		isError,
		/*
			Здесь хранятся параметры номинации
		*/
		param,
		/*
			Возможные разделы КБУ в зависимости от пространства имён
		*/
		fastRemovePrefix = (config.wgIsRedirect ? 'ОП' : 'О') + ({0:'Сd', 2:'У', 3:'У', 6:'Ф', 14:'К'}[config.wgNamespaceNumber] || ''),
		/*
			Список всех доступных причин КБУ
			Коды для админов, текст для остальных.
			Используется максимально короткий шаблон.
			Третий параметр — условие для появления <input>
		*/
		fastRemove = [
			['подст: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(function(arr) {
			return fastRemovePrefix.indexOf(arr[1].charAt(0)) >= 0
		}),
		/*
			Функция запроса к API.
			Токен и формат каждый раз дополняется перед запросом.
			Аргументы:
				1. Передаваемые параметры
				2. Режим запроса (query, edit или parse)
				3. Колбек
			void apiReq(object, string, function(result, status))
		*/
		apiReq = function(par, mode, clbck) {
			par.format = 'json'
			par.token  = mw.user.tokens.get('csrfToken')
			par.action = mode
			$.post('/w/api.php', par, clbck)
		},
		/*
			Функция получения даты.
			Необходимо, например, для КУ-запросов.
			Есть возможность указать собственную дату для подытоживания номинации.
			['1051-32-33', '20 апреля 2014'] getDate(string)
		*/
		getDate = function(s) {
			var d = (!!s) ? new Date(s) : new Date()
			return [d.toISOString().substr(0, 10), d.getUTCDate() + ' ' +
				'января,февраля,марта,апреля,мая,июня,июля,августа,сентября,октября,ноября,декабря'.split(',')[d.getUTCMonth()] +
				' ' + d.getUTCFullYear()]
		},
		/*
			Функция получения <input>
			* Атрибут «h» используется в КБУ, указывая type
			* Атрибут placeholder используется везде, для улучшения UI
			* Атрибут id используется везде для получения информации из поля
			string getInput(string, string, bool)
		*/
		getInput = function(id, p, h) {
			return '<input id=' + id + ' type=' + (h ? 'hidden' : 'text') + ' placeholder="' + p + '" class=messagebox>'
		},
		/*
			Определяет СО страницы
		*/
		getSO = function(pg) {
			var tmp = /([^:]*:)?(.*)/.exec(pg)
			if (tmp[1]) {
				var ns = config.wgNamespaceIds[tmp[1].slice(0, -1).toLowerCase().replace(/ /g, '_')]
				if (ns != undefined)
					return config.wgFormattedNamespaces[ns | 1] + ':' + tmp[2]
			}
			return 'Обсуждение:' + pg
		},
		/*
			Функция получения текста страницы, null если ошибка
			getText(string, function(string))
		*/
		getText = function(pg, clbck) {
			apiReq({
				prop: 'wikitext',
				page: pg
			}, 'parse', function(txt) {
				clbck(txt.parse ? txt.parse.wikitext['*'] : null)
			})
		},
		/*
			Функция отправки уведомления пользователю
			По выполнению вызывает callback
			userAlert(string, function(error))
		*/
		userAlert = function(pg, clbck) {
			apiReq({
				prop: 'revisions',
				rvprop: 'user',
				rvdir: 'newer',
				titles: pg
			}, 'query', function (t) {
				var i = t.query.pages
				if (!('-1' in i)) {
					var rvdata = i[Object.keys(i)[0]].revisions[0]
					if (!('anon' in rvdata) && !rvdata.userhidden && (rvdata.user && rvdata.user !== config.wgUserName)) {
						apiReq({
							title: 'оу:' + rvdata.user,
							section: 'new',
							sectiontitle: 'Удалятор: [[:' + pg + ']]',
							summary: param.sum,
							text: 'Страница [[:' + pg + ']], созданная вами, ' + (param[3] ? '' : 'предложена ') + param[1] + '. ' +
								(param.place ? 'Обсуждение — на странице [[' + param.place + '#' + param.sectionNW + ']]. ' : '') +
								'~~\~~<br><small>Это автоматическое уведомление, сгенерированное [[у:Землеройкин/remove.js|скриптом «Удалятор»]].</small>'
						}, 'edit', function(t) {
							clbck(t.error)
						})
					}
					else clbck('не нужно')
				}
				else clbck('страница удалена')
			})
		},
		/*
			Функция работы с текстом статьи.
			Определяет необходимые шаблоны и устанавливает их на СО или статью.
			changeArticle(string, function(object))
		*/
		changeArticle = function(pg, clbck) {
			if (/(noRnm|ret)/g.test(param[0]))
				changeArticleDenom(pg, clbck)
			else
				changeArticleNom(pg, clbck)
		},
		// Закрытие номинации (снять в статье, установить на СО)
		changeArticleDenom = function(pg, clbck) {
			getText(pg, function(article) {
				var tpl = RegExp('{\{(' + param[3] + ')\\|(\\d{4}-\\d\\d-\\d\\d)\\|?(.*?)}}', 'gi').exec(article)
				if (tpl == null) {
					clbck({					
						code: 'ошибка',
						info: 'Невозможно снять шаблон «' + param[3] + '».'
					})
					return
				}
				param.date = getDate(tpl[2])
				param.place = 'ВП:' + param[3].replace(/\|.*/, '') + '/' + param.date[1]
				if (param[0] == 'noRnm') { //не переименовано
					param.sectionNW = pg + ' → ' + tpl[3]
					param.tplpar = pg + '|' + tpl[3]
				}
				if (param[0] == 'ret') { //оставлено
					param.sectionNW = tpl[3].length ? tpl[3] : pg
					param.tplpar = 'l1=' + param.sectionNW
				}
				param.sum = '[[у:Землеройкин/remove.js|Удалятор]]: номинация [[' + (param.place ? param.place + '#' : '') + param.sectionNW + ']] — ' + param[2]

				apiReq({
					summary: param.sum,
					title: getSO(pg),
					prependtext: '{{' + param[2] + '|' + param.date[1] + '|' + param.tplpar + '}}\n'
				}, 'edit')

				article = article.replace(RegExp('(<noin.*?>)?{\{(' + param[3] + ')\\|.*?}}\n?(<\/noin.*?>)?\n?', 'gi'), '')
				apiReq({
					title: pg,
					text: article,
					summary: param.sum
				}, 'edit', function(t) {
					clbck(t.error)
				})
			})
		},
		// Открытие номинации (установка шаблона в статье)
		changeArticleNom = function(pg, clbck) {
			getText(pg, function(article) {
				if (article == null) {
					clbck({
						code: 'ошибка',
						info: 'Страница «' + pg + '» не существует.'
					})
					return
				}
				var tpl = ''
				if (param[0] == 'fRm') {
					tpl = fastRemove[$('#rmSel').val()][0] + '|1=' + $('#fiRm').val()
				}
				else {
					tpl = param.tplpar
					if (param[0] == 'merge') {
						tpl = ('|' + tpl + '|').replace('|' + pg + '|', '|').slice(1, -1)
					}
					tpl = param[2] + '|' + param.date[0] + (tpl.length ? '|' + tpl : '')
				}
				apiReq({
					title: pg,
					text: (tpl.length ? '<noinclude>{{' + tpl + '}}\n</noinclude>' : '') + article,
					summary: param.sum
				}, 'edit', function(t) {
					clbck(t.error)
				})
			})
		},
		/*
			Функция установки номинации на соответствующую страницу
			void setNominate(callback)
		*/
		setNominate = function(clbck) {
			apiReq({
				title: param.place,
				createonly: '1',
				text: '{{' + param[4] + '-Навигация}}\n',
				summary: '[[у:Землеройкин/remove.js|Удалятор]]: автоматическая шапка',
			}, 'edit', function(t) {
				apiReq({
					title: param.place,
					section: 'new',
					sectiontitle: param.section,
					summary: param.sum,
					text: param.msg + ' ~~\~~'
				}, 'edit', function(t) {
					clbck(t.error)
				})
			})
		},
		/*
			Заполнение параметров номинации (переменная param).
				.date — дата номинации
				.msg — текст номинации
				.place — место обсуждения (ВП:к_чему-то-там... или пусто, если КБУ)
				.section — заголовок раздела номинации
				.sectionNW — то же, без викификации
				.tplpar — параметры шаблона, для установки в статье
				.sum — описание правок скрипта
			Возвращает массив страниц для обработки (до 5 шт. для МногоКУ, 2 для КОБ, 0 для ВУС, иначе 1)
		*/
		setParam = function() {
			var	i,
				pg = config.wgPageName.replace(/_/g, ' '),
				ttl = $('#rmHeader').val(),
				ttl2 = $('#rmHeader2').length ? $('#rmHeader2').val() : '',
				msg = $('#rmMsg').val(),
				pgs = [pg]

			if (/(noRnm|ret)/g.test(param[0]))
				return pgs

			param.date = getDate()
			param.msg = msg ? msg.trim() : ''
			param.place = param[0] != 'fRm' ? 'ВП:' + param[2] + '/' + param.date[1] : ''

			param.section =	param[0] == 'mRm' ? ttl	: '[[:' + pg + ']]'
			param.tplpar = ''
			if (param[0] == 'rnm') {
				param.tplpar = ttl
				param.section += ' → [[:' + ttl + ']]'
			}
			else if (param[0] == 'merge') {
				param.tplpar = pg + '|' + ttl
				param.section += ' и [[:' + ttl + ']]'
			}
			else if (param[0] == 'split') {
				param.tplpar = '[[:' + ttl + ']]' + (ttl2 ? ' и [[:' + ttl2 + ']]' : '')
				param.section += ' → ' + param.tplpar
			}

			param.sectionNW = param.section.replace(/\[\[:/g, '').replace(/]]/g, '')
			param.sum = '[[у:Землеройкин/remove.js|Удалятор]]: номинация [[' + (param.place ? param.place + '#' : '') + param.sectionNW + ']]' + (param[0] == 'fRm' ? ' ' + param[2] : '')

			if (param[0] == 'mRm') {
				pgs = []
				param.msg = '=== По всем ===\n' + param.msg
				for (i = 4; i >= 0; i--) {
					pg = $('#rmArticle' + i).val()
					if (pg.length) {
						pgs.push(pg)
						param.msg = '=== [[:' + pg + ']] ===\n' + param.msg
					}
				}
			}
			else if (param[0] == 'merge') {
				pgs.push(ttl)
			}
			else if (param[0] == 'recov') {
				pgs = []
			}
			return pgs
		},
		/*
			Вывод сообщений об ошибках
		*/
		logError = function(s, err) {
			if (err && err.code) isError = 1
			$('#rmWindow').append(
				'<br>' + s + ' — ' + (
					err ? (err.code ? '<span class="error"><small>' + err.code + ': ' + err.info + '</small></span>' : err)
						: 'OK'
				)
			)
		},
		/*
			Обработка массива страниц
		*/
		processPages = function(pgs) {
			if (pgs.length) {
				var pg = pgs.pop()
				changeArticle(pg, function(err) {
					logError('Правка статьи «' + pg + '»', err)
					if (isError) {
						finalizeWindow()
						return
					}
					if (setAlert) {
						userAlert(pg, function(err) {
							logError('Уведомление создателя', err)
							processPages(pgs)
						})
					}
					else processPages(pgs)
				})
			}
			else {
				if (param[4]) {
					setNominate(function(err) {
						logError('Запись номинации', err)
//						window.open('/wiki/' + param.place + '#' + encodeURI(param.sectionNW.replace(/ /g, '_')))
						finalizeWindow()
					})
				}
				else finalizeWindow()	
			}
		},
		/*
			Вызывается, когда всё сделано
		*/
		finalizeWindow = function() {
			if (isError) {
				$('.mw-small-spinner').remove()
				$('#rmWindow')
					.append('<p class="error">При выполнении скрипта случились ошибки. Поправьте всё, что надо, вручную.')
					.children().prop('disabled', false)
				$('#rmBtn').attr('disabled', 1)
				$('#rmClose').text('Закрыть')
			}
			else location.reload()
		},
		/*
			Функция создания модального окна
		*/
		modalHandler = function() {
			var i,
				content = ''

			if (param[0] == 'mRm') {
				content += getInput('rmHeader', 'Заголовок номинации')
				for (i = 0; i < 5; i++) {
					content += getInput('rmArticle' + i, 'Статья' + (i + 1))
				}		
			}
			if (param[0] == 'fRm') {
				content += '<select id=rmSel class=messagebox>'
				for (i = 0; i < fastRemove.length; i++) {
					content += '<option value=' + i + '>' + fastRemove[i][1] + '</option>'
				}
				content += '</select>' + getInput('fiRm', '', 1)
			}
			if (param[0] == 'rnm') {
				content += getInput('rmHeader', 'Новое название')
			}
			if (param[0] == 'merge') {
				content += getInput('rmHeader', 'Объединить с…')
			}
			if (param[0] == 'split') {
				content += getInput('rmHeader', 'Разделить на эту')
				content += getInput('rmHeader2', 'И на эту')
			}
			if (param[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>Удалятор: ' + param[2] + '</h1>' + content +
				'<br><label><input name="rmUAlert" type=checkbox ' + ((setAlert) ? 'checked' : '') + '>Оповестить автора</label><br>' +
				'<button id=rmBtn class=mw-ui-button>Отправить</button><button id=rmClose class=mw-ui-button>Отмена</button>'
			)
			if (param[0] == 'mRm') $('#rmArticle0').val(config.wgPageName.replace(/_/g, ' '))

			$('#rmSel').change(function() {
				var i = fastRemove[this.value][2]
				$('#fiRm').attr({type: (i ? 'text' : 'hidden'), placeholder: 'Укажите ' + i})
			})
			$('#rmClose').click(function() {
				$('#rmWindow').remove()
			})

			$('#rmBtn').click(function() {
				$('#rmWindow')
					.append('<b class=mw-small-spinner></b>')
					.children().attr('disabled', '1')
				setAlert = $('[name="rmUAlert"]').is(':checked')
				var pgs = setParam()
				processPages(pgs)
			})
			/*
				Реализация ctrl+enter события
			*/
			$(window).keydown(function (e) {
				if (e.ctrlKey && e.keyCode == 13)
					$('#rmBtn').click()
			})
		};
	/*
		Добавление выпадающего меню на все страницы
	*/
	if ((rmNamespaces.indexOf(config.wgNamespaceNumber & ~1) >= 0) && (config.wgPageContentModel == 'wikitext')) {
		var menuLocation = 'p-cactions';
		var nextNode = '#ca-move';
		if ( config.skin === 'timeless' || config.skin === 'minerva' ) {
			menuLocation = 'p-tb';
			nextNode = null;
		}
		if ( config.skin === 'vector' || config.skin === 'vector-2022' ) {
			nextNode = null;
			var $menuClone = $( '#p-variants' ).eq( 0 ).clone();
			
			$menuClone.attr( 'id', 'p-remove-js' )
				.attr( 'aria-labelledby', 'p-remove-js-label' )
				.removeClass( 'emptyPortlet' );
			
			$menuClone.find( 'ul' ).empty();
			$menuClone.find( 'input' ).attr( 'aria-labelledby', 'p-remove-js-label' );
			$menuClone.find( 'label' ).attr( 'id', 'p-remove-js-label' ).find( 'span' ).text( 'Удалятор' );
			$( '#p-views' ).after( $menuClone );
			menuLocation = 'p-remove-js';
		}
		
		for (var a of rmActions) {
			var portletLink = mw.util.addPortletLink( menuLocation, '#', menu_raw[ a ], 'ca-remove-' + a, null, null, nextNode );
			$( portletLink ).find( 'a' ).click(function( e ) {
				e.preventDefault();
				var i = $( this ).parent().attr( 'id' ).replace( 'ca-remove-', '' );
				param = (
					// [текущее действие, комментарий, шаблон/место обсуждения, поддерживаемые шаблоны
					i == 'imp'	? [ i, 'к срочному улучшению',		'к улучшению',		'',	'КУЛ'] :
					i == 'rnm'	? [ i, 'к переименованию',			'к переименованию',	'',	'КПМ'] :
					i == 'tRm'	? [ i, 'к удалению',				'к удалению',		'',	'КУ'] :
					i == 'mRm'	? [ i, 'к удалению',				'к удалению',		'',	'КУ'] :
					i == 'merge'? [ i, 'к объединению с другой',	'к объединению',	'',	'КОБ'] :
					i == 'split'? [ i, 'к разделению',				'к разделению',		'',	'КР'] :
					i == 'fRm'	? [ i, 'к [[ВП:КБУ|быстрому удалению]]',	'к быстрому удалению'] :
					i == 'recov'? [ i, '',							'к восстановлению',	'',	'ВУС'] :
					i == 'ret'	? [ i, 'оставлена',					'оставлено',		'к удалению|ку'] :
					i == 'noRnm'? [ i, 'не переименована',			'не переименовано',	'к переименованию|кпм|rename'] :
					0
				)
				isError = 0
				modalHandler()
			});
		}
	}
})