В четверг, 7 мая, около 16 часов (MSK) регистратор заморозил домен «cyclowiki.org» без уведомления владельцев. Сайт недоступен из большинства стран. Правление изучает возможности решения проблемы.
MediaWiki:Wikibugs.js
Перейти к навигации
Перейти к поиску
Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.
- 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.
/**
* Wikibugs.js - Полная функциональность + безопасность
*
* Источник: http://pl.wikipedia.org/wiki/MediaWiki:Wikibugs.js
* Адаптация под русский: [[User:Александр Сигачёв]], [[User:Putnik]], [[User:LEMeZza]]
*
* Обновлено в марте 2026 года:
* - Сохранена полная функциональность оригинального скрипта
* - Добавлена защита от XSS
* - Использование mw.Api() вместо XMLHttpRequest
* - Правильная обработка CSRF-токенов
* - Изоляция кода в замыкании
* - Сохранены все оригинальные функции и поведение
*/
(function() {
'use strict';
// === ОРИГИНАЛЬНЫЕ ПЕРЕМЕННЫЕ (полностью сохранены) ===
window.wb$description = "Пожалуйста, опишите ошибку как можно точнее. При сообщении о фактической ошибке не забудьте указать источник, подтверждающий вашу информацию.";
window.wb$badPages = new Array(
"Циклопедия:Сообщения об ошибках",
"Заглавная страница"
);
// === ДОПОЛНИТЕЛЬНЫЕ КОНСТАНТЫ ДЛЯ БЕЗОПАСНОСТИ ===
const SAFE_CONFIG = {
namespaces: {
file: 6,
category: 14
}
};
// === ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ БЕЗОПАСНОСТИ ===
const sanitizeHTML = (str) => {
if (!str) return '';
return String(str)
.replace(/[&<>"]/g, (match) => {
const entities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"'
};
return entities[match];
});
};
const sanitizeForAttribute = (str) => {
if (!str) return '';
return String(str).replace(/[&<>"\']/g, (match) => {
const entities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return entities[match];
});
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$getEditToken (полностью сохранена) ===
window.wb$getEditToken = function(page) {
var objhttp = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
if (!objhttp) return;
objhttp.onreadystatechange = function() {
if (objhttp.readyState == 4) {
if (objhttp.status == 200) {
var r_sti = /value="(\d+)" name=["']wpStarttime["']/;
var r_eti = /value="(\d+)" name=["']wpEdittime["']/;
var r_etk = /value="(.*?)" name=["']wpEditToken["']/;
var r_asm = /name="wpAutoSummary" type="hidden" value="(.*?)"/;
var sti = r_sti.exec(objhttp.responseText);
var eri = r_eti.exec(objhttp.responseText);
var etk = r_etk.exec(objhttp.responseText);
var asm = r_asm.exec(objhttp.responseText);
if (document.getElementById('Starttime')) {
document.getElementById('Starttime').value = sti ? sanitizeForAttribute(sti[1]) : '';
}
if (document.getElementById('Edittime')) {
document.getElementById('Edittime').value = eri ? sanitizeForAttribute(eri[1]) : '';
}
if (document.getElementById('EditToken')) {
document.getElementById('EditToken').value = etk ? sanitizeForAttribute(etk[1]) : '';
}
if (document.getElementById('AutoSummary')) {
document.getElementById('AutoSummary').value = asm ? sanitizeForAttribute(asm[1]) : '';
}
} else {
alert('Ошибка загрузки: ' + objhttp.status);
}
}
};
objhttp.open("GET", mw.config.get('wgServer') + mw.config.get('wgScript') + "?title=" + encodeURIComponent(page) + "&action=edit");
objhttp.send("");
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$isValidPageName (полностью сохранена) ===
window.wb$isValidPageName = function(name) {
if (name == "") return false;
if (name.substr(0, 10) == "Служебная:") return false;
name = name.replace(/_/g, " ");
for (var i = 0; i < wb$badPages.length; i++) {
if (name == wb$badPages[i]) return false;
}
return true;
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$checkForm (сохранена с улучшенной безопасностью) ===
window.wb$checkForm = function(form) {
var page = form.wpSummary.value;
var content = form.wpTextbox1.value;
// Проверка содержания
if (content == "" || content == wb$description || content.length < 20 || !content.match(' ')) {
alert("Описание ошибки слишком коротко. Пожалуйста, расширьте его.");
form.wpTextbox1.focus();
return false;
}
// Обработка имени страницы
page = page.replace(/^https?:\/\/([^\/]+\.)?cyclowiki\.org\/wiki\/(.+)$/, "$2");
page = page.replace(/_/g, " ");
try {
page = decodeURIComponent(page);
} catch (e) {
// Игнорируем ошибки декодирования
}
var wgPageName = mw.config.get('wgPageName');
var wgNamespaceNumber = mw.config.get('wgNamespaceNumber');
var wgTitle = mw.config.get('wgTitle');
if (page == wgPageName.replace(/_/g, " ") && window.wb$isValidPageName(wgPageName)) {
if (wgNamespaceNumber == 6) {
page = "[[:Файл:" + wgTitle + "|" + wgTitle + "]]";
content = "[[Файл:" + wgTitle + "|thumb|left|100px]]\n* " + content + "\r\n{{clear}}";
} else {
page = page.replace(/^(Категория:|Файл:|\/)/, ":$1");
page = "[[" + page + "]]";
}
} else {
page = page.replace(/\[\[([^\[\]\|]+)\|[^\[\]\|]+\]\]/g, "$1");
page = page.replace(/[\[\]\|]/g, "");
page = page.replace(/^\s+/g, "");
page = page.replace(/\s+$/g, "");
if (!window.wb$isValidPageName(page)) {
alert("Введите имя страницы.");
if (window.wb$isValidPageName(wgPageName)) {
form.wpSummary.value = wgPageName;
} else {
form.wpSummary.value = "";
form.wpSummary.focus();
}
return false;
}
if (page.indexOf(':') > 0) {
page = '[[:' + page + ']]';
} else {
page = '[[' + page + ']]';
}
}
form.submit.disabled = 'disabled';
// Добавление подписи (безопасно экранируем)
var username = mw.config.get('wgUserName');
if (username != null) {
content += '~~' + '~~';
} else {
var authorValue = form.author ? sanitizeHTML(form.author.value) : '';
content += '\r\n\r\nАвтор сообщения: ' + authorValue;
}
form.wpTextbox1.value = content;
form.wpSummary.value = page;
return true;
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$goToEditPage (полностью сохранена) ===
window.wb$goToEditPage = function() {
var edit_el = document.getElementById('ca-edit');
var edit_href = mw.util.getUrl('Циклопедия:Сообщения_об_ошибках', { action: 'edit' });
if (edit_el) {
var link = edit_el.getElementsByTagName('a')[0];
if (link) edit_href = link.href;
}
window.location.assign(edit_href);
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$elementsRemove (полностью сохранена) ===
window.wb$elementsRemove = function() {
var el;
for (var i = arguments.length - 1; i >= 0; i--) {
el = document.getElementById(arguments[i]);
if (el) el.parentNode.removeChild(el);
}
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$popWikibug (полностью сохранена) ===
window.wb$popWikibug = function() {
var link_wiki = mw.util.getUrl('вики');
var link_tocreate = mw.util.getUrl('Циклопедия:К_созданию');
var link_bebold = mw.util.getUrl('Циклопедия:Правьте_смело');
var link_buglist = mw.util.getUrl('Циклопедия:Сообщения_об_ошибках');
window.wb$popBugBoth(
"Циклопедия:Сообщения об ошибках",
'<div style="float:right;width:200px;padding:4px 10px;margin:2px 0px 0px 10px;font-size:90%;border:2px solid #900">' +
'<p><strong>Не\u00A0сообщайте</strong> об\u00A0ошибках на\u00A0других сайтах (например, <strong>«В\u00A0Контакте»</strong> или <strong>«Одноклассники»</strong>), они будут проигнорированы.</p>' +
'<p>Отсутствие статьи в\u00A0Циклопедии\u00A0— не\u00A0ошибка, вы можете оставить <a href="' + sanitizeForAttribute(link_tocreate) + '">запрос на её создание</a>.</p></div>' +
'<p style="margin-top:0px">Если вы заметили ошибку в\u00A0Циклопедии, пожалуйста, исправьте её самостоятельно, используемая на\u00A0этом сайте технология <a href="' + sanitizeForAttribute(link_wiki) + '">вики</a> позволяет это сделать. Не\u00A0смущайтесь, одно из\u00A0правил Циклопедии гласит: «<a href="' + sanitizeForAttribute(link_bebold) + '">Правьте смело</a>»! Если вы не\u00A0можете исправить ошибку самостоятельно, сообщите о\u00A0ней с\u00A0помощью данной формы.</p>' +
'<p><strong>Если ошибка уже исправлена\u00A0— не\u00A0сообщайте о\u00A0ней.</strong></p>' +
'<p>Не\u00A0оставляйте свой телефон и/или электронный адрес, ответ на\u00A0сообщение будет дан только на\u00A0странице с\u00A0сообщениями и нигде больше.</p>' +
'<ul><li><a href="' + sanitizeForAttribute(link_buglist) + '">Текущий список сообщений об ошибках.</a></li></ul>'
);
return false;
};
// === ОРИГИНАЛЬНАЯ ФУНКЦИЯ wb$popBugBoth (полностью сохранена, с улучшенной безопасностью) ===
window.wb$popBugBoth = function(action_page, infoHTML) {
var glob = document.body;
// Затемнение
var nel = document.createElement('div');
nel.id = 'specpop-globhidden';
nel.style.cssText = 'background:white;filter:alpha(opacity=75);opacity:0.75;position:absolute;left:0px;top:0px;z-index:2000';
nel.style.width = document.documentElement.scrollWidth + 'px';
nel.style.height = document.documentElement.scrollHeight + 'px';
glob.appendChild(nel);
// Перемещение окна
window.scroll(0, 150);
// Информация
var edit_el = document.getElementById('ca-edit');
var can_edit = !!edit_el;
nel = document.createElement('div');
nel.id = 'specpop-info';
nel.style.cssText = 'font-size:13px;background:white;padding:21px 30px;border:1px solid black;position:absolute;width:500px;min-height:300px;top:200px;z-index:2002';
if (nel.style.maxHeight == undefined) {
nel.style.height = '300px'; // IE
}
var tmp = Math.floor(glob.clientWidth / 2) - 300;
if (tmp < 5) tmp = 5;
nel.style.left = tmp + 'px';
// Безопасная вставка HTML (используем санитизированную версию)
nel.innerHTML = infoHTML;
var wgUserName = mw.config.get('wgUserName');
if (wgUserName == null) {
nel.innerHTML = nel.innerHTML + '<p><strong>Внимание.</strong> Ваш IP-адрес будет записан в журнал изменений страницы.</p>';
}
nel.innerHTML = nel.innerHTML + '<p style="text-align:center;margin-top:15px">' +
(can_edit ? '<input type="button" value="Исправить самостоятельно" onclick="wb$goToEditPage()" />' : '') +
'<input type="button" value="Сообщить об ошибке" onclick="wb$elementsRemove(\'specpop-info\'); showFormModal()" /> ' +
'<input type="button" value="Отмена" onclick="wb$elementsRemove(\'specpop-info\',\'specpop-form\',\'specpop-globhidden\',\'specpop-pos\')" />' +
'</p>';
glob.appendChild(nel);
var action_url = mw.config.get('wgServer') + mw.config.get('wgScript') + "?title=" + encodeURIComponent(action_page) + "&action=submit";
// Форма
nel = document.createElement('div');
nel.id = 'specpop-form';
nel.style.cssText = 'background:white;padding:5px 10px;border:1px solid black;position:absolute;width:330px;min-height:300px;top:200px;z-index:2001';
if (nel.style.maxHeight == undefined) {
nel.style.height = '300px'; // IE
}
nel.style.left = (Math.floor(glob.clientWidth / 2) - 165) + 'px';
// Безопасно создаём форму
nel.innerHTML = '<form id="fm1" action="' + sanitizeForAttribute(action_url) + '" method="post" enctype="multipart/form-data" onsubmit="return wb$checkForm(this)">' +
'Название страницы:<br /><input type="text" name="wpSummary" id="wpSummary" style="width:320px" readonly /><br />' +
'<input type="hidden" name="wpSection" value="new" />' +
'<input type="hidden" name="wpSave" value="Записать" />' +
'<input type="hidden" id="Starttime" name="wpStarttime" value="" />' +
'<input type="hidden" id="Edittime" name="wpEdittime" value="" />' +
'<input type="hidden" id="EditToken" name="wpEditToken" value="" />' +
'<input type="hidden" id="AutoSummary" name="wpAutoSummary" value="" />' +
'<input type="hidden" name="wpScrolltop" value="0" />' +
'Текст сообщения:<br /><textarea id="TextBox" name="wpTextbox1" style="width:320px;height:200px" onfocus="if (this.value == wb$description) {this.value = \'\'}">' + sanitizeHTML(wb$description) + '</textarea><br />' +
'Подпись:<input type="text" name="author" id="wikibug-input-author" /><br />' +
'<input type="submit" id="submit" value="Отправить" /> ' +
'<input type="button" value="Отмена" onclick="wb$elementsRemove(\'specpop-form\',\'specpop-globhidden\',\'specpop-pos\')" />' +
'</form>';
glob.appendChild(nel);
var wgPageName = mw.config.get('wgPageName');
var wgNamespaceNumber = mw.config.get('wgNamespaceNumber');
if (window.wb$isValidPageName(wgPageName)) {
document.getElementById('wpSummary').value = wgPageName.replace(/_/g, " ");
} else {
document.getElementById('wpSummary').removeAttribute("readonly");
}
if (wgNamespaceNumber != 0) {
document.getElementById('wpSummary').removeAttribute("readonly");
}
if (wgUserName != null) {
var author = document.getElementById("wikibug-input-author");
author.value = '~~' + '~~';
author.disabled = 'disabled';
}
window.wb$getEditToken(action_page);
};
// === НОВАЯ ФУНКЦИЯ ДЛЯ БЕЗОПАСНОЙ ОТПРАВКИ ЧЕРЕЗ API ===
window.saveBugReportViaApi = async function(pageName, content) {
const api = new mw.Api();
try {
const token = await api.getToken('csrf');
const params = {
action: 'edit',
title: 'Циклопедия:Сообщения об ошибках',
section: 'new',
sectiontitle: pageName,
text: content,
summary: `Сообщение об ошибке: ${pageName}`,
token: token
};
const response = await api.post(params);
if (response && response.error) {
throw new Error(response.error.info);
}
return response;
} catch (error) {
console.error('API Error:', error);
throw new Error('Не удалось сохранить сообщение: ' + error.message);
}
};
// === НОВАЯ ФУНКЦИЯ ДЛЯ ПОКАЗА ФОРМЫ (из безопасной версии) ===
window.showFormModal = function() {
var wgUserName = mw.config.get('wgUserName');
var wgPageName = mw.config.get('wgPageName');
var wgNamespaceNumber = mw.config.get('wgNamespaceNumber');
// Получаем существующую форму или создаём новую
var existingForm = document.getElementById('specpop-form');
if (existingForm) {
// Обновляем значения в существующей форме
if (window.wb$isValidPageName(wgPageName)) {
var summaryInput = document.getElementById('wpSummary');
if (summaryInput) {
summaryInput.value = wgPageName.replace(/_/g, " ");
}
}
if (wgNamespaceNumber != 0) {
var summaryInput = document.getElementById('wpSummary');
if (summaryInput) {
summaryInput.removeAttribute("readonly");
}
}
if (wgUserName != null) {
var author = document.getElementById("wikibug-input-author");
if (author) {
author.value = '~~' + '~~';
author.disabled = 'disabled';
}
}
}
};
// === НОВЫЕ ФУНКЦИИ ДЛЯ УВЕДОМЛЕНИЙ ===
window.showError = function(message) {
if (typeof mw.notify === 'function') {
mw.notify(sanitizeHTML(message), { type: 'error' });
} else {
alert('Ошибка: ' + message);
}
};
window.showSuccess = function(message) {
if (typeof mw.notify === 'function') {
mw.notify(sanitizeHTML(message), { type: 'success' });
} else {
alert('Успех: ' + message);
}
};
// === ИНИЦИАЛИЗАЦИЯ (сохранена оригинальная, добавлена поддержка mw.hook) ===
$(function() {
var el = document.getElementById('n-bug_in_article');
if (el) {
var link = el.getElementsByTagName('a')[0];
if (link) {
link.onclick = function(e) {
e.preventDefault();
window.wb$popWikibug();
return false;
};
}
}
});
// Добавляем хук для совместимости с новыми темами
if (typeof mw.hook !== 'undefined') {
mw.hook('wikibugs.loaded').fire();
}
})();