User:Phlsph7/WikiNarrator.js

(function(){

const scriptName = 'WikiNarrator';

const intervalDelay = 5000;

let intervalId = '';

const states = {stopped: 0, playing: 1, paused: 2};

let currentState;

let initialized = false;

let selectedText = '';

speechSynthesis.onvoiceschanged = function(){

if(!initialized){

initialized = true;

if(getSettingsFromStorage() === null){

let settings = getDefaultSettings();

saveSettingsToStorage(settings);

}

initializePlayer();

if(getSettingsFromStorage().showPlayer){

showPlayer();

}

}

};

$.when(mw.loader.using('mediawiki.util'), $.ready).then(function(){

const portletLink = mw.util.addPortletLink('p-tb', '#', 'Show/hide ' + scriptName, scriptName + 'Id');

portletLink.onclick = function(e) {

e.preventDefault();

let settings = getSettingsFromStorage();

if(settings.showPlayer){

settings.showPlayer = false;

saveSettingsToStorage(settings);

stop();

hidePlayer();

}

else{

settings.showPlayer = true;

saveSettingsToStorage(settings);

showPlayer();

}

};

});

function initializePlayer(){

const htmlCode = `

`;

const container = document.createElement('div');

document.body.append(container);

container.outerHTML = htmlCode;

if(!('speechSynthesis' in window)){

wikiNarratorControlButtons.innerHTML = '

WikiNarrator does not work in your browser
because it does not support speech synthesis.

';

}

else{

adjustState(states.stopped);

wikiNarratorVoice.onchange = saveSettingsFromInput;

wikiNarratorVolume.onchange = saveSettingsFromInput;

wikiNarratorSpeed.onchange = saveSettingsFromInput;

wikiNarratorPitch.onchange = saveSettingsFromInput;

wikiNarratorSettings.onclick = showHideSettings;

wikiNarratorPlay.onmousedown = function(){

selectedText = getSelection().toString();

};

wikiNarratorPlay.onclick = play;

wikiNarratorPause.onclick = pause;

wikiNarratorStop.onclick = stop;

wikiNarratorDefault.onclick = setDefaultSettings;

wikiNarratorClose.onclick = showHideSettings;

initializeInput();

}

}

function showPlayer(){

wikiNarratorContainer.style.display = '';

}

function hidePlayer(){

wikiNarratorContainer.style.display = "none";

}

function play(){

if(currentState == states.paused){

adjustState(states.playing);

speechSynthesis.resume();

setSpeechInterval();

}

else{

adjustState(states.playing);

let settings = getSettingsFromStorage();

hideRefs();

let text = selectedText;

if(text == ''){

let articleContainer;

if(document.getElementById('wikiPreview')){

articleContainer = document.getElementById('wikiPreview').querySelector('.mw-parser-output');

}

else{

articleContainer = document.getElementById('mw-content-text').querySelector('.mw-parser-output');

}

let readableElements = Array.from(articleContainer.querySelectorAll(':scope > p, :scope > blockquote, :scope > ul, :scope > ol, :scope > dl, :scope > .mw-heading'));

for(let element of readableElements){

text += element.innerText + '\n';

}

}

showRefs();

text = text.split('\r').join('\n')

.split('\n\n').join('\n')

.split('\n.').join('\n');

let textChunks = [''];

const chunkSize = 2000;

let sentences = text.split('. ');

let currentIndex = 0;

for(let sentence of sentences){

if(sentence.trim().length > 2){

if(textChunks[currentIndex].length + sentence.length < chunkSize){

textChunks[currentIndex] += sentence + '. ';

}

else{

currentIndex++;

textChunks[currentIndex] = sentence + '. ';

}

}

}

for(let i = 0; i < textChunks.length; i++){

const target = "\n. ";

if (textChunks[i].endsWith(target)) {

textChunks[i] = textChunks[i].slice(0, -target.length);

}

textChunks[i] = textChunks[i].trim();

}

let utterances = [];

for(let i = 0; i < textChunks.length; i++){

let utterance = new SpeechSynthesisUtterance();

utterance.text = textChunks[i].trim();

utterance.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == settings.voiceName; })[0];

utterance.volume = settings.volume;

utterance.rate = settings.speed;

utterance.pitch = settings.pitch;

utterances.push(utterance);

}

for(let i = 0; i < utterances.length - 1; i++){

let currentUtterance = utterances[i];

let nextUtterance = utterances[i+1];

currentUtterance.onend = function(){

if(currentState == states.playing){

clearInterval(intervalId);

speechSynthesis.cancel();

speechSynthesis.speak(nextUtterance);

setSpeechInterval();

}

else{

adjustState(states.stopped);

clearInterval(intervalId);

}

};

}

utterances[utterances.length-1].onend = function() {

adjustState(states.stopped);

clearInterval(intervalId);

};

speechSynthesis.cancel();

speechSynthesis.speak(utterances[0]);

setSpeechInterval();

}

}

function pause(){

adjustState(states.paused);

clearInterval(intervalId);

speechSynthesis.pause();

}

function stop(){

adjustState(states.stopped);

clearInterval(intervalId);

speechSynthesis.cancel();

}

function adjustState(newState){

currentState = newState;

switch(currentState){

case states.stopped:

wikiNarratorPlay.disabled = false;

wikiNarratorPause.disabled = true;

wikiNarratorStop.disabled = true;

break;

case states.playing:

wikiNarratorPlay.disabled = true;

wikiNarratorPause.disabled = false;

wikiNarratorStop.disabled = false;

break;

case states.paused:

wikiNarratorPlay.disabled = false;

wikiNarratorPause.disabled = true;

wikiNarratorStop.disabled = false;

break;

}

}

function setSpeechInterval(){

intervalId = setInterval(function(){

speechSynthesis.pause();

speechSynthesis.resume();

}, intervalDelay);

}

function showHideSettings(){

if(wikiNarratorSettingsContainer.style.display != "none"){

wikiNarratorSettingsContainer.style.display = "none";

}

else{

wikiNarratorSettingsContainer.style.display = '';

}

}

function hideRefs(){

let refs = document.body.querySelectorAll('.reference, .Inline-Template, .mw-editsection');

for(let ref of refs){

ref.style.display = 'none';

}

}

function showRefs(){

let refs = document.body.querySelectorAll('.reference, .Inline-Template, .mw-editsection');

for(let ref of refs){

ref.style.display = '';

}

}

function getSettingsFromStorage(){

return JSON.parse(localStorage.getItem('wikiNarratorSettings'));

}

function getSettingsFromInput(){

let settings = {

voiceName: wikiNarratorVoice.value,

volume: wikiNarratorVolume.value,

speed: wikiNarratorSpeed.value,

pitch: wikiNarratorPitch.value,

showPlayer: true,

};

return settings;

}

function saveSettingsToStorage(settings){

localStorage.setItem('wikiNarratorSettings', JSON.stringify(settings));

}

function initializeInput(){

let settings = getSettingsFromStorage();

wikiNarratorVolume.value = settings.volume;

wikiNarratorSpeed.value = settings.speed;

wikiNarratorPitch.value = settings.pitch;

wikiNarratorVoice.innerHTML = '';

let voices = speechSynthesis.getVoices();

for(let voice of voices){

const option = document.createElement('option');

option.value = voice.name;

option.text = voice.name;

if(voice.name == settings.voiceName){

option.selected = true;

}

wikiNarratorVoice.add(option);

}

}

function saveSettingsFromInput(){

let settings = getSettingsFromInput();

saveSettingsToStorage(settings);

}

function setDefaultSettings(){

let settings = getDefaultSettings();

saveSettingsToStorage(settings);

initializeInput();

}

function getDefaultSettings(){

let voices = speechSynthesis.getVoices();

let filteredVoices = [];

for(let voice of voices){

if(voice.lang.includes('en-')){

filteredVoices.push(voice);

}

}

if(filteredVoices.length == 0){

filteredVoices = voices;

}

let defaultVoiceName = '';

for(let voice of filteredVoices){

if(voice.default == true){

defaultVoiceName = voice.name;

break;

}

}

if(defaultVoiceName == ''){

defaultVoiceName = filteredVoices[0].name;

}

return {

voiceName: defaultVoiceName,

volume: 1,

speed: 1,

pitch: 1,

showPlayer: true,

};

}

})();