MediaWiki:Gadget-speechSynthewiki.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.
/** Speech Synthewiki — гаджет озвучки статей браузером юзера **/
/* Бетаверсия 0.2 — надо много поработать над озвучкой формул */
/* надо решить глюк кнопки в нулевой секции */
/* Рабочей базой служил движок Mediawiki 1.39.1 */
/* Альтернатива [[wikipedia:ru:MediaWiki:Gadget-yandex-tts.js]] */
/* Автор — Урахара из Циклопедии, лицензия — CC BY-SA 4.0 */
/*****************************************************************/
// ИСПОЛЬЗОВАНИЕ
// Возле каждого раздела статьи появляется ссылка «озвучить»
// Нажимаешь → читает весь текст под этим заголовком
// Если хочешь остановить — жмёшь «стоп» (кнопка меняется)
// Когда раздел заканчивается — воспроизведение останавливается
/*****************************************************************/
(function() {
var currentUtterance = null;
var currentButton = null;
var onClick = function(event) {
event.preventDefault();
var a = event.target;
var header = $(a).parent().parent();
// Если нажали на ту же кнопку, которая сейчас воспроизводится - останавливаем
if (currentButton === a && window.speechSynthesis.speaking) {
window.speechSynthesis.cancel();
$(a).text('озвучить');
currentButton = null;
currentUtterance = null;
return;
}
function sanitizeText(text) {
var div = document.createElement('div');
div.textContent = text;
return div.textContent;
}
var text = '';
var next = header.next();
while (next.length > 0) {
if (next.find(".mw-editsection").length !== 0) break;
var cloned = next.clone();
// Удаляем все элементы с файлами и изображениями
cloned.find('.thumb, .thumbinner, .thumbcaption, .magnify, .floatnone, .floatleft, .floatright').remove();
cloned.find('img').remove(); // Удаляем сами изображения
cloned.find('a.image, a.internal').remove(); // Удаляем ссылки на файлы
// Удаляем ссылки на файлы в тексте (Файл:, Image:, File:)
cloned.contents().each(function() {
if (this.nodeType === 3) { // текстовый узел
var node = $(this);
var text = node.text();
// Удаляем строки с "Файл:", "File:", "Image:" и подобными
if (text.match(/(Файл|File|Image|Изображение|Медиа):/i)) {
node.remove();
}
} else if (this.nodeType === 1) { // элемент
var $this = $(this);
// Проверяем ссылки на файлы
if ($this.is('a') && $this.attr('href') && $this.attr('href').match(/\.(jpg|jpeg|png|gif|bmp|svg|webp|ogg|mp3|wav|pdf|djvu)$/i)) {
$this.remove();
}
// Проверяем классы, связанные с файлами
if ($this.hasClass('image') || $this.hasClass('internal') || $this.attr('href') && $this.attr('href').includes('Файл:')) {
$this.remove();
}
}
});
// Удаляем галереи изображений
cloned.find('ul.gallery, div.gallery, li.gallerybox, div.thumb').remove();
// Удаляем блоки с подписями к изображениям
cloned.find('.thumbcaption, .gallerycaption').remove();
cloned.find("sup.reference").remove();
cloned.find(".metadata").remove();
cloned.find(".noprint").remove();
// Воспроизведение формул
cloned.find('math').each(function() {
var formulaText = '';
// Обработка дробей
$(this).find('mfrac').each(function() {
var numerator = $(this).children().first().text().trim();
var denominator = $(this).children().last().text().trim();
formulaText += ' дробь: ' + numerator + ' на ' + denominator + ' ';
$(this).remove();
});
// Обработка верхних индексов (степеней)
$(this).find('msup').each(function() {
var base = $(this).children().first().text().trim();
var exponent = $(this).children().last().text().trim();
if (exponent === '2') formulaText += base + ' в квадрате ';
else if (exponent === '3') formulaText += base + ' в кубе ';
else formulaText += base + ' в степени ' + exponent + ' ';
$(this).remove();
});
// Обработка нижних индексов
$(this).find('msub').each(function() {
var base = $(this).children().first().text().trim();
var subscript = $(this).children().last().text().trim();
formulaText += base + ' с индексом ' + subscript + ' ';
$(this).remove();
});
// Обработка корней
$(this).find('msqrt').each(function() {
var underRoot = $(this).text().trim();
formulaText += ' корень из ' + underRoot + ' ';
$(this).remove();
});
// Замена спецсимволов на читаемые слова
var allText = $(this).text().trim();
allText = allText
.replace(/×/g, ' умножить ')
.replace(/÷/g, ' разделить ')
.replace(/≠/g, ' не равно ')
.replace(/≈/g, ' приблизительно равно ')
.replace(/≤/g, ' меньше или равно ')
.replace(/≥/g, ' больше или равно ')
.replace(/±/g, ' плюс минус ')
.replace(/∞/g, ' бесконечность ')
.replace(/√/g, ' корень из ')
.replace(/π/g, ' пи ')
.replace(/∑/g, ' сумма ')
.replace(/∏/g, ' произведение ')
.replace(/∫/g, ' интеграл ')
.replace(/∂/g, ' частная производная ')
.replace(/∇/g, ' набла ')
.replace(/∈/g, ' принадлежит ')
.replace(/∉/g, ' не принадлежит ')
.replace(/∩/g, ' пересечение ')
.replace(/∪/g, ' объединение ')
.replace(/⊂/g, ' подмножество ')
.replace(/⊃/g, ' надмножество ');
formulaText += allText;
if (formulaText) {
$(this).replaceWith(document.createTextNode(' формула: ' + formulaText + ' '));
}
});
text += sanitizeText(cloned.text());
text += "\n";
next = next.next();
}
if (window.speechSynthesis) {
// Останавливаем предыдущее воспроизведение, если оно было
if (window.speechSynthesis.speaking) {
window.speechSynthesis.cancel();
if (currentButton) {
$(currentButton).text('озвучить');
}
}
currentUtterance = new SpeechSynthesisUtterance(text);
currentUtterance.lang = 'ru-RU';
currentUtterance.rate = 1.0;
currentUtterance.pitch = 1.0;
currentUtterance.volume = 1.0;
var voices = window.speechSynthesis.getVoices();
var russianVoice = voices.find(function(voice) {
return voice.lang.includes('ru');
});
if (russianVoice) {
currentUtterance.voice = russianVoice;
}
currentUtterance.onstart = function() {
$(a).text('стоп');
currentButton = a;
};
// ВАЖНО: когда воспроизведение заканчивается естественным путём
currentUtterance.onend = function() {
// Возвращаем кнопку в исходное состояние
$(a).text('озвучить');
currentButton = null;
currentUtterance = null;
};
// Обработка ошибок - тоже возвращаем кнопку
currentUtterance.onerror = function(event) {
// Не сбрасываем кнопку при прерывании (когда остановили вручную)
if (event.error !== 'interrupted') {
$(a).text('озвучить');
currentButton = null;
currentUtterance = null;
}
};
if (voices.length === 0) {
window.speechSynthesis.onvoiceschanged = function() {
var updatedVoices = window.speechSynthesis.getVoices();
var ruVoice = updatedVoices.find(function(v) {
return v.lang.includes('ru');
});
if (ruVoice) {
currentUtterance.voice = ruVoice;
}
window.speechSynthesis.speak(currentUtterance);
};
} else {
window.speechSynthesis.speak(currentUtterance);
}
} else {
alert('Ваш браузер не поддерживает синтез речи');
}
};
$(document).ready(function() {
$(".mw-editsection").each(function(i, item) {
if (!$(item).prev().hasClass('cyclo-tts-start')) {
var a = $('<span class="mw-editsection cyclo-tts-start">[<a href="#" style="cursor:pointer;">озвучить</a>]</span> ');
$(item).before(a);
a.click(onClick);
}
});
});
})();