Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • 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 higimo	// v5.0.4
	Вынесение на КБУ (+ отсроченное), КУ (+ множественное, + оставлено), КУЛ, КПМ.
	Подключение (на своём /common.js):
	var g_user_alert = 0 // указать, чтоб не уведомлять пользователя
	importScript('u:higimo/remove.js');
*/

$(document).ready(function() {
		/*
			Инициализация итератора
		*/
	var i = 0,
		/*
			Инициализация переменной устанавливающей флаг на чекбоксе,
			который влияет на выполнение функции оповещающей создателя статьи
		*/
		setAlert = (typeof g_user_alert == 'undefined') ? 1 : g_user_alert
		/* 
			Список всех доступных причин КБУ
			Коды для админов, текст для остальных.
			Используется максимально короткий шаблон.
			Третий параметр — условие для появления <input>
		*/
		fastRemove = [
			['подст: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 Подстраница инактива'		]
		],
		/*
			Генератор кода для меню на каждой странице
		*/
		menu = 'imp=Обсудить|rnm=К переименованию|tRm=К удалению|mRm=Несколько статей к удалению|ret=Оставить|noRnm=Не переименовано|fRm=К быстрому удалению|merge=Объединить|'.replace(/(.*?)=(.*?)\|/g, '<li><a id=$1>$2</a></li>'),
		/*
			Селектор для темы Вектор, чтобы добавлять собственное выпадающее меню
		*/
		vector = $('#p-views'),
		/*
			Определение неймспейса
		*/
		ns = mw.config.get('wgNamespaceNumber'),
		/*
			Функция запроса к API.
			Запрос бывает двух видов: edit и parse.
			Чтоб каждый раз не передавать параметр, он — булева переменная
			Токен и формат каждый раз дополняется перед запросом.
			Токен и формат обязателен.
			Аргументы:
				1. Передаваемые параметры
				2. Используемый режим
				3. Колбек
			void apiReq(object, bool, function(result, status))
		*/
		apiReq = function(param, mode, callback) {
			param.format = 'json'
			param.token  = mw.user.tokens.get('editToken')
			param.action =
				mode == 0 ? 'edit'	:
				mode == 1 ? 'parse'	: 'query'
			$.post('/w/api.php', param, callback)
		},
		/*
			Функция получения даты.
			Необходимо, наример, для КУ-запросов.
			Есть возможность указать собственную дату для подытоживания номинации.
			['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>'
		},
		/*
			Функция получения текстов статьи и её СО
			getTexts(string, string, function(string, string))
		*/
		getTexts = function(pg, pgtl, clbck) {
			getText = {prop: 'wikitext'},
			getText.page = pg
			apiReq(getText, 1, function(txt1) {
				getText.page = pgtl
				apiReq(getText, 1, function(txt2) {
					clbck(
						txt1.parse ? txt1.parse.wikitext['*'] : '',
						txt2.parse ? txt2.parse.wikitext['*'] : ''
					)
				})
			})
		},
		/*
			Функция отправки уведомления пользователю
			По выполнению вызывает callback
			userAlert(object, string, string, function())
		*/
		userAlert = function(param, pg, msg, clbck) {
			apiReq({
				prop: 'revisions',
				rvprop: 'user',
				rvdir: 'newer',
				titles: pg
			}, 2, function (t) {
				i = t.query.pages
				name = (typeof i['-1'] == 'undefined') ? i[Object.keys(i)[0]].revisions[0].user : ''
				if (!/(\d{1,3}\.){3}\d/.test(name) && setAlert) {
					apiReq({
						title: 'Обсуждение участника:' + name,
						section: 'new',
						sectiontitle: param.sum,
						summary: param.sum,
						text: '~~\~~'
					}, 0, function() {
						clbck()
					})
				} else {
					clbck()
				}
			})
		}
		/*
			Функция работы с текстом статьи.
			Получает адрес страницы обсуждения, получает тексты, определяет
			необходимые шаблоны и устанавливает их на СО или статью.
			changeArticle(object, string, string, function(string))
		*/
		changeArticle = function(param, pg, date, clbck) {
			var so = mw.config.get( 'wgFormattedNamespaces' )[ns + 1] + ':' + pg.replace(/.*?:/, '')
			getTexts(pg, so, function(article, talk) {
				// Дописать остальные итоговые кнопочки и просто добавить. еще и привести в норм. состояние.
				if (/(noRnm)/g.test(param[0])) { //не переименовано
					var tpl = RegExp('(' + param[3].join('|') + ')\\|(\\d{4}-\\d\\d-\\d\\d)\\|?(.*?)}', 'gi').exec(article)
					if (tpl == null)
						location.reload()
					date = getDate(tpl[2])
					apiReq({
						summary: param.sum.replace(/(\[В.*)\/.*?#/g, '$1/' + date[1] + '#'),
						title: so,
						text: '{\{' + param[4] + '|' + date[1] + '|' + pg + '|' + tpl[3] + '}}\n' + talk
					}, 0)
					article = article.replace(RegExp('(<noin.*?>)?{\{(' + param[3].join('|') + ').*?}}?(<\/noin.*?>)?', 'gi'), '')
				}
				if (/(ret)/g.test(param[0])) {//оставлено
					var tpl = RegExp('(' + param[3].join('|') + ')\\|(\\d{4}-\\d\\d-\\d\\d)\\|?(.*?)}', 'gi').exec(article)
					if (tpl == null)
						location.reload()
					date = getDate(tpl[2])
					apiReq({
						summary: param.sum.replace(/(\[В.*)\/.*?#/g, '$1/' + date[1] + '#'),
						title: so,
						text: '{\{' + param[4] + '|' + date[1] + '|' + tpl[3] + '}}\n' + talk
					}, 0)
					article = article.replace(RegExp('(<noin.*?>)?{\{(' + param[3].join('|') + ').*?}}?(<\/noin.*?>)?', 'gi'), '')
				}
				tpl = '<noinclude>{\{' +
					(
						param[0] == 'imp'
						? 'подст:КУЛ'
							: /(tRm|mRm)/g.test(param[0])
							? 'К удалению|' + date[0]
								: param[0] == 'fRm'
								? fastRemove[$('#rmSel').val()][0] + '|' + $('#fiRm').val()
									: param[0] == 'rnm'
									? 'Кпм|' + date[0] + '|' + $('#rmHeader').val() + '|' + param.rmnNom
										: param[0] == 'merge'
										? 'subst:слить|' + $('#rmHeader').val()
											: param[0] == 'split'
											? 'split|' + date[0] + '|' + '[[' + $('#rmHeader').val() + ']] и [[' + $('#rmHeader2').val() + ']]'
												: ''
					) + '}}</noinclude>\n'
				apiReq({
					title: pg,
					text: (tpl.length > 30 ? tpl : '') + article,
					summary: param.sum
				}, 0, function() {
					clbck(pg)
				})
			})
		},
		/*
			Функция установки номинации на соответствующую страницу
			Аргументы:
				1. Параметры
				2. Заголовок номинации
				3. Текст размещаемого сообщения
				4. Колбек
				void setNominate(param, section, msg, callback)
		*/
		setNominate = function(param, section, msg, callback) {
 			if (/(imp|rnm|tRm|mRm|merge|recov|split)/g.test(param[0])) {
				apiReq({
					title: param.place,
					section: 'new',
					sectiontitle:
						'mRm' == param[0]
						? section
						: /(rnm|split|merge)/g.test(param[0])
							? '[[:' + param.rmnNom.replace(/( → |, )/g, ']]$1[[:') + ']]'
							: '[[:' + section + ']]',
					summary: param.sum,
					text: msg
				}, 0, function() {
					callback()
				})
			} else {
				callback()
			}
		},
		/*
			Функция создания и обработки модального окна
			void modalHandler(array)
		*/
		modalHandler = function(param) {
			var 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 (/(imp|tRm|mRm|rnm|merge|recov|split)/g.test(param[0])) {
				content += '<textarea id=rmMsg placeholder="Текст номинации без «~~\~~»." rows=4></textarea>'
			}
			$('#content').prepend(
				'<div id=rmWindow style="padding:2em;margin:1em;border:1px solid #f6fbff; background: #8BCBFF;">' +
				'<h1>' + param[1] + '</h1>' + content +
				'<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>'
			)
			$('#rmSel').change(function() {
				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')
				var date = getDate(),
					msg = $('#rmMsg').val(),
					wind = $('#rmWindow'),
					pg = mw.config.get('wgPageName').replace(/_/g, ' '),
					ttl = $('#rmHeader').val()
					ttl2 = $('#rmHeader2').length ? $('#rmHeader2').val() : ''
				param.place = param[2] ? 'Викисловарь:' + param[2]  : ''
				param.rmnNom = pg + ' → ' + ttl + (ttl2.length ? ', ' + $('#rmHeader2').val() : '')
				param.sum = '[[Участник:Pavliukdanila/удалятор|Удалятор]]: [[' + (param.place ? param.place + '#' : '') + pg + ']] — ' + param[1]
				i = 0
				setAlert = $('[name="rmUAlert"]').is(':checked')
				if ($('#rmArticle0')[0]) {
					i = 4
					msg = '=== По всем ===\n' + msg
				}
				if (/(rnm|split|merge)/g.test(param[0])) {
					param.sum = param.sum.replace(/#.*]]/g, '#' + param.rmnNom + ']]')
				}
				for (; i >= 0; i--) {
					if ($('#rmArticle0')[0]) {
						pg = $('#rmArticle' + i).val()
					}
					if (pg.length) {
						if ($('#rmArticle0')[0]) {
							param.sum = param.sum.replace(/#.*?\]/g, '#' + (ttl ? ttl : pg) + ']')
							msg = '=== [[:' + pg + ']] ===\n' + msg
						}
						changeArticle(param, pg, date, function(pg) {
							wind.append('Исправлена статья «' + pg + '»<br>')
							userAlert(param, pg, msg + ' ~~\~~', function() {
								wind.append('Уведомлен создатель<br>')
								setNominate(param, (ttl ? ttl : pg), msg + ' ~~\~~', function() {
									wind.append('Номинация записана<br>')
									if (/(imp|rnm|tRm|mRm|recov|merge)/g.test(param[0])) {
										wind.append('Открытие номинации<br>')
										window.open(
											'/wiki/' + param.place + '#' + encodeURI(pg.replace(/ /g, '_')).replace(/%/g, '.')
										)
									}
									location.reload()
								})
							})
						})
					}
				}
			})
			/*
				Реализация ctrl+enter события
			*/
			$(window).keydown(function (e) {
				if (e.ctrlKey && e.keyCode == 13)
					$('#rmBtn').click()
			})
		}
	/*
		Добавление выпадающего меню на все страницы
	*/
	if (/\.([0-7]|1([0-1]([01])?|[45])?)\./g.test('.' + ns + '.')) {
		if (vector.length) {
			vector.after('<div class=vectorMenu><h3><span>Заявки</span><a></a></h3><div class=menu><ul>' + menu + '</ul></div></div>')
		} else {
			$('#ca-history').after(menu)
		}
	}
	/*
		Событие по клику на любую кнопку выпадающего меню
	*/
	$('#imp,#tRm,#mRm,#ret,#fRm,#noRnm,#rnm,#merge,#recov,#split').click(function() {
		i = this.id
		modalHandler(
			// [текущее действие, название модального окна и комментарий, название страницы, поддерживаемые шаблоны
			i == 'imp'	? [ i, 'Обсудить содержание страницы',			'Лингвистические и лексикографические вопросы'		] :
			i == 'rnm'	? [ i, 'Номинировать на переименование',			'Лингвистические и лексикографические вопросы'	] :
			i == 'tRm'	? [ i, 'Номинация на удалению',			'К удалению'		] :
			i == 'mRm'	? [ i, 'Номинировать несколько статей к удалению',	'К удалению'		] :
			i == 'ret' 	? [ i, 'Оставлено',				'К удалению',		['ку', 'к удалению'],		'Оставлено' ] :
			i == 'noRnm'? [ i, 'Не переименовано',		'К переименованию',	['кпм', 'к переименованию'],'Не переименовано' ] :
			i == 'merge'? [ i, 'Номинация на объединение',			'К объединению'		] :
			i == 'recov'? [ i, 'Номинация ВУС',			'К восстановлению'	] :
			i == 'split'? [ i, 'Номинация к разделению','К разделению'		] :
			i == 'fRm'	? [ i, 'Номинировать на быстрое удаление'		] : 0
		)
	})
})