// Скрипт для упрощённой подстановки шаблонов.
// Для установки, добавьте в свой common.js строку importScript('у:Megitsune-chan/subst.js');
// Имеется возможность выбора нескольких шаблонов, а так же выбора места ставки: начало или конец страницы.
// Для предложения добавить шаблоны в скрипт, обращайтесь на страницу обсуждения Участницы Megitsune-chan.
sigWarning = true;
// Добавляем стили
mw.loader.addStyleTag(`
.subst-modal {
background: white;
border: 1px solid #a2a9b1;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
width: 750px;
padding: 20px;
}
.subst-templates {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
max-height: 350px;
overflow-y: auto;
padding: 10px;
border: 1px solid #eaecf0;
border-radius: 4px;
margin: 10px 0;
}
.template-checkbox {
display: flex;
align-items: center;
padding: 5px;
border-radius: 4px;
transition: background 0.2s;
}
.template-checkbox:hover {
background: #f8f9fa;
}
.subst-controls {
display: flex;
gap: 10px;
align-items: center;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eaecf0;
}
.subst-position {
margin: 10px 0;
}
.subst-button {
padding: 8px 16px;
border-radius: 4px;
border: 1px solid #a2a9b1;
cursor: pointer;
transition: background 0.2s;
}
.subst-button.primary {
background: #36c;
color: white;
border-color: #36c;
}
.subst-button:hover {
background: #447ff5;
}
.subst-button.secondary:hover {
background: #f8f9fa;
}
`);
//Список шаблонов
subst = {
$reason: null,
$token: null,
$rev: null,
$reasons: [{
tmpl: 'автобиография',
reason: 'Автобиография',
ns: 0
},
{
tmpl: 'аффилированные источники',
reason: 'Аффилированные источники',
ns: 0
},
{
tmpl: 'взвешенность',
reason: 'Взвешенность',
ns: 0
},
{
tmpl: 'грубый перевод',
reason: 'Грубый перевод',
ns: 0
},
{
tmpl: 'дописать',
reason: 'Дописать',
ns: 0
},
{
tmpl: 'длинное описание сюжета',
reason: 'Длинное описание сюжета',
ns: 0
},
{
tmpl: 'значимость',
reason: 'Значимость',
ns: 0
},
{
tmpl: 'излишние описания компьютерной игры',
reason: 'Излишние описания компьютерной игры',
ns: 0
},
{
tmpl: 'конфликт интересов',
reason: 'Конфликт интересов',
ns: 0
},
{
tmpl: 'много цитат',
reason: 'Много цитат',
ns: 0
},
{
tmpl: 'неавторитетные источники',
reason: 'Неавторитетные источники',
ns: 0
},
{
tmpl: 'недостаточно критики',
reason: 'Недостаточно критики',
ns: 0
},
{
tmpl: 'нет источников',
reason: 'Нет источников',
ns: 0
},
{
tmpl: 'нет карточки',
reason: 'Нет карточки',
ns: 0
},
{
tmpl: 'нет преамбулы',
reason: 'Нет преамбулы',
ns: 0
},
{
tmpl: 'нет сносок',
reason: 'Нет сносок',
ns: 0
},
{
tmpl: 'неэнциклопедично',
reason: 'Неэнциклопедично',
ns: 0
},
{
tmpl: 'обновить',
reason: 'Обновить',
ns: 0
},
{
tmpl: 'орисс',
reason: 'Орисс',
ns: 0
},
{
tmpl: 'отчество',
reason: 'Отчество',
ns: 0
},
{
tmpl: 'пресс-релиз',
reason: 'Пресс-релиз',
ns: 0
},
{
tmpl: 'проверить нейтральность',
reason: 'Проверить нейтральность',
ns: 0
},
{
tmpl: 'редактирую',
reason: 'Редактирую',
ns: 666
},
{
tmpl: 'Резюме',
reason: 'Резюме',
ns: 0
},
{
tmpl: 'реклама',
reason: 'Реклама',
ns: 0
},
{
tmpl: 'стиль статьи',
reason: 'Стиль статьи',
ns: 0
},
{
tmpl: 'чистить',
reason: 'Чистить',
ns: 0
},
{
tmpl: 'Ds-author',
reason: 'К отсроченному удалению автором',
ns: 666
},
{
tmpl: 'помочь',
reason: 'Инкубатор, Прошу помочь',
ns: 102 && 103
},
{
tmpl: 'проверить',
reason: 'Инкубатор, Прошу проверить',
ns: 102 && 103
},
],
//Создание окна
initialise: function() {
var $window = `
<div id="subst-window" class="subst-modal">
<h3>Выберите шаблоны для подстановки</h3>
<form name="subst" id="subst">
<div class="subst-templates">`;
for (var $i in this.$reasons) {
if (this.$reasons[$i].ns == 666 || this.$reasons[$i].ns == mw.config.get('wgNamespaceNumber')) {
$window += `
<label class="template-checkbox">
<input type="checkbox" name="subst" value="${$i}">
<span>${this.$reasons[$i].reason}</span>
</label>`;
}
}
$window += `
</div>
<div class="subst-position">
<label>Место добавления шаблонов:</label>
<select id="subst-position">
<option value="top">В начало страницы</option>
<option value="bottom">В конец страницы</option>
</select>
</div>
<div>
<input type="checkbox" id="subst-forcewrap">
<label for="subst-forcewrap">Обернуть шаблон в тег <noinclude></label>
</div>
<div class="subst-controls">
<button type="button" id="subst-confirm" class="subst-button primary">Отправить</button>
<button type="button" id="subst-cancel" class="subst-button secondary">Отмена</button>
</div>
</form>
</div>`;
$('#bodyContent').append($window);
$('#subst-confirm').on('click', function() {
subst.execute();
});
$('#subst-cancel').on('click', function() {
$('#subst-window').remove();
});
},
execute: function() {
var selectedTemplates = $("input[name='subst']:checked").map(function() {
return $(this).val();
}).get();
if (selectedTemplates.length === 0) {
alert('Выберите хотя бы один шаблон!');
return;
}
this.$selectedTemplates = selectedTemplates;
this.$position = $('#subst-position').val();
$.getJSON(
mw.config.get('wgScriptPath') + '/api.php?format=json&action=query&prop=info&titles=Foobar&meta=tokens&type=csrf',
function($x) {
subst.template($x);
}
);
},
template: function($data) {
if (!$data || !$data['query']) {
return;
}
this.$token = $data['query']['tokens']['csrftoken'];
$.getJSON(mw.config.get('wgScriptPath') + '/api.php?format=json&action=query&prop=revisions&titles=' + subst._ae(mw.config.get('wgPageName')) + '&rvprop=user|content&rvlimit=1', function($data) {
for (var $rev in $data['query']['pages']) {
if (typeof $data['query']['pages'][$rev]['revisions'] == 'undefined') {
alert('Страница удалена или скрипт неправильно обработал её название.');
return false;
}
var $content = $data['query']['pages'][$rev]['revisions'][0]['*'];
var self = subst; // Сохраняем ссылку на контекст
var forcewrap = $('#subst-forcewrap').is(':checked');
var nameSpace = mw.config.get('wgNamespaceNumber');
var additionalParams = $("#subst-neob").val();
// Создаем функцию форматирования шаблона
function formatTemplate(reasonIndex) {
var reason = self.$reasons[reasonIndex];
var wrapStart = (forcewrap || nameSpace == 10) ? '<noinclude>' : '';
var wrapEnd = (forcewrap || nameSpace == 10) ? '</noinclude>' : '';
return wrapStart +
'{{' + (reason.ds ? 'subst:ds' : 'subst') +
((reason.tmpl == '') ? '' : ':' + reason.tmpl) +
(additionalParams ? "|" + additionalParams : "") + '}}' +
wrapEnd + '\n';
}
// Формируем все выбранные шаблоны
var templates = self.$selectedTemplates.map(function(index) {
return formatTemplate(index);
}).join('');
// Определяем итоговый контент в зависимости от выбранной позиции
// Функция для поиска позиции перед категориями
function findCategoryPosition(content) {
// Ищем первое вхождение категории
var categoryMatch = content.match(/\[\[\s*([Кк]атегория|Category)\s*:/);
if (categoryMatch) {
return categoryMatch.index;
}
return content.length;
}
// Определяем итоговый контент в зависимости от выбранной позиции
var newContent;
if (self.$position === 'top') {
newContent = templates + $content;
} else {
// Если выбрано добавление в конец
var categoryPos = findCategoryPosition($content);
if (categoryPos === $content.length) {
// Если категорий нет, добавляем в самый конец
newContent = $content + templates;
} else {
// Если категории есть, вставляем перед ними
newContent = $content.slice(0, categoryPos) + templates + $content.slice(categoryPos);
}
}
// Отправляем изменения
$.post(mw.config.get('wgScriptPath') + '/api.php', {
action: 'edit',
title: mw.config.get('wgPageName'),
summary: 'Подстановка шаблонов с помощью [[user:Megitsune-chan/subst.js|subst.js]]',
token: self.$token,
notminor: '1',
text: newContent,
format: 'json'
}, function($x) {
self.finalf($x)
});
}
});
},
finalf: function($data) {
this.$rev = $data['edit']['newrevid'];
if ($('#subst-warn').is(':checked') && subst.$reasons[subst.$reason].warn) {
$.getJSON(mw.config.get('wgScriptPath') + '/api.php?format=json&action=query&rvdir=newer&prop=revisions&titles=' + subst._ae(mw.config.get('wgPageName')) + '&rvprop=user|content&rvlimit=1', function($x) {
for (var $rev in $x['query']['pages']) {
var $talk = 'Обсуждение_участника:' + $x['query']['pages'][$rev]['revisions'][0]['user'];
$.post(mw.config.get('wgScriptPath') + '/api.php', {
action: 'edit',
appendtext: '\n{{subst:' + subst.$reasons[subst.$reason].warn +
((subst.$reasons[subst.$reason].tmpl == 'copyvio') ? '|1=' + subst._sp(mw.config.get('wgPageName')) + '|2=' + $("#subst-field" + subst.$reason).val() : ((subst.$reasons[subst.$reason].tmpl == 'vand') ? '' : '|1=' + subst._sp(mw.config.get('wgPageName')))) + '}}' +
'\n~~' + '~~',
summary: ' ',
title: $talk,
token: subst.$token,
notminor: '1'
},
function() {
subst.review()
});
}
});
} else {
subst.review()
}
},
review: function($data) {
window.location = mw.config.get('wgServer') + '/wiki/' + encodeURIComponent(mw.config.get('wgPageName'));
},
_ae: function($i) {
return $i.replace(/\+/g, '%2B').replace(/&/g, '%26');
},
_sp: function($i) {
return $i.replace(/\+/g, '%2B').replace(/&/g, '%26').replace(/_/g, ' ');
},
};
// Если мы не на служебной странице, не на странице файла (для него отдельный гаджет), а на существующей странице, которую можно править, добавляет вкладку
$(function() {
if (mw.config.get('wgCanonicalNamespace') != 'Special' && mw.config.get('wgCanonicalNamespace') != 'File' && $("#ca-viewsource").length == 0 && mw.config.get('wgArticleId')) {
mw.loader.using('mediawiki.util').done(function() {
mw.util.addPortletLink("p-cactions", "javascript: subst.initialise()", "Подстановка", "ca-subst", "Подстановка шаблонов");
});
}
});