Участник:Луффи/import.js
< Участник:Луффи
Перейти к навигации
Перейти к поиску
Версия от 23:44, 2 июля 2023; Луффи (обсуждение | вклад)
Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.
- Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
- Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
- Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
- Opera: Нажмите Ctrl+F5.
/* 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() }); } } })