Skip to content
Snippets Groups Projects
talk.js 15.43 KiB
// jshint esversion: 6
	const machine = document.location.search.indexOf('machine=')>-1? document.location.search.split('machine=')[1].split('&')[0]: 'fermi';
	initSearch('../' + machine + '_lattice.json');
	window.openData = [];
	const t0 = + new Date();
	let mode = '';
	const launcherSrc = 'https://puma-01.elettra.eu/knob/launcher.php';
	const lang = document.location.search.indexOf('lang=')>-1 && document.location.search.split('lang=')[1].split('&')[0]=='en'? 'en-US': 'it-IT'; // BCP 47 language
	const host = document.location.search.indexOf('talk=')>-1? document.location.search.split('talk=')[1].split('&')[0].replace('ee', 'pcl-elettra-cre-0').replace('ef','pcl-elettra-crf-0'): '';
	function switchLocale(newlang) {document.location = './talk.php?lang='+newlang+(host.length>0? '&talk='+host: '');}
	let locale = {};
	function myload() {
		fetch('./talk_locale.json').then((response) => {return response.json();}).then((rlocale) => {
			locale = rlocale;
			$('#title').html(rlocale[lang].talkto + host);
			$('#micna').attr('title', rlocale[lang].micna);
			$('#miclabel').html(rlocale[lang].miclabel + '   ');
			if (document.location.search.indexOf('background=')>-1) {
				const a = document.location.search.split('background=')[1].split('&')[0];
				console.log(a);
				// $('body').css('background-color', +a > 0? '#'+a: a);
				$('#search').hide(); $('#open').hide();
			}
		});
	}
	// https://stackoverflow.com/questions/64405532/why-speechsynthesisutterance-is-not-working-on-chrome
	// It's because in Chrome speech synthasis requires user interaction before it speaks e.g. a button click.
	// https://stackoverflow.com/questions/50490304/how-to-make-audio-autoplay-on-chrome
	const synth = window.speechSynthesis;
	const voices = synth.getVoices();
	const voiceLocale = {'it-IT': false, 'en-US': false};
	for (let i=voices.length-1; i>=0; i--) {
		if (voices[i].lang=='it-IT') voiceLocale['it-IT'] = voices[i];
		if (voices[i].lang=='en-US') voiceLocale['en-US'] = voices[i];
	}
	let oldText = '';
	let oldTime = 0;
	function speakGenerated(textValue) {
		if (textValue !== '') {
			stopRec();
			if (synth.speaking) {console.error(locale.lang.speaking);showLog(locale.lang.speaking2);return;}
			const utterThis = new SpeechSynthesisUtterance(textValue);
			utterThis.onend = function (event) {startRec(); console.log('SpeechSynthesisUtterance.onend');showLog('..');};
			utterThis.onerror = function (event) {console.error('SpeechSynthesisUtterance.onerror', event.error, event);showLog(locale[lang].onerror+JSON.stringify(event.error));};
			utterThis.voice = voiceLocale[lang]===false? voices[0]: voiceLocale[lang];
			utterThis.pitch = 0.1;
			utterThis.rate = 1;
			utterThis.lang = lang;
			showLog(lang);
			synth.speak(utterThis);
		}
	}
	let keepAlive = false;
	const names = [];
	let aliveTimer = -1;
	function startRec() {
		keepAlive = true;
		aliveTimer = -1;
		showLog('start');
		recognition.start();
		showLog('started');
		document.getElementById('micstart').style.display = 'inline';
		document.getElementById('micstop').style.display = 'none';
		$('#search').hide();
		$('#open').hide();
		console.log($('#search'));
	}
	function naClick() {
		$("#micstart").show();
		$("#micna").hide();
		$("#miclabel").hide();
		if (window.parent && window.parent.document.getElementById("talk")) {
			window.parent.document.getElementById("talk").style.height = "250px";
			window.parent.document.getElementById("talk").style.position = "absolute";
			window.parent.document.getElementById("talk").style.right = ((window.parent.innerWidth-250)/2)+"px";
			window.parent.document.getElementById("talk").style.top = ((window.parent.innerHeight-250)/2)+"px";
		}
	}
	function stopRec() {
		if (window.parent && window.parent.document.getElementById("talk")) {
			window.parent.document.getElementById("talk").style.height = "30px";
			window.parent.document.getElementById("talk").style.position = "unset";
		}
		if (typeof window.SpeechRecognition == 'undefined') {$("#micstart").hide();$("#micna").show();}
		keepAlive = false;
		recognition.stop();
	}
	function commandlistinfo() {
		alert(locale[lang].commandlistinfo);
	}
	if (document.location.search.indexOf('debug')==-1) $('#log').hide();
	let speechStat = '';
	let firstRun = true;
	function showLog(currentToken, p) {
		if (typeof p != 'undefined') console.log(p);
		console.trace(currentToken);
		// Show log in console and UI.
		$('#log').html(currentToken);
		// const logElement = document.querySelector('#log');
		// logElement.textContent = logElement.textContent+'\n'+JSON.stringify(currentToken);
	}
	let finalTranscript = '';
	const SpeechRecognition = window.speechRecognition || window.webkitSpeechRecognition;
	const recognition = new SpeechRecognition();
	recognition.maxAlternatives = 10;
	recognition.continuous = true;
	recognition.interimResults = true;
	recognition.lang = lang;
	recognition.maxAlternatives = 3;
	showLog('starting...'+'\n');
	recognition.onerror = function(event) {
		showLog(locale[lang].errorname + event.error+', '+event.message+', '+event.lineno+'\n');
		showLog(locale[lang].errormessage + JSON.stringify(event.error));
		console.log(event);
		startRec();
	};
	recognition.onend = function(event) {
		showLog('.');
		// document.getElementById('miclabel').style.display = 'none';
		document.getElementById('micna').style.display = 'none';
		document.getElementById('micstart').style.display = 'none';
		document.getElementById('micstop').style.display = 'inline';
		if (keepAlive && aliveTimer==-1) aliveTimer = setTimeout(startRec, 200);
	};
	recognition.onstart = function(event) {
		if (document.location.search.indexOf('debug')==-1) $('#log').hide();
		if (firstRun) {
			firstRun = false;
			recognition.stop();
			return;
		}
		showLog('-');
		document.getElementById('miclabel').style.display = 'none';
		document.getElementById('micna').style.display = 'none';
		document.getElementById('micstart').style.display = 'inline';
		document.getElementById('micstop').style.display = 'none';
	};
	function detect_token(transcript, token) {
		const t = token.split(';');
		for (let i in t) {
			if (transcript.indexOf(t[i])>-1) return transcript.indexOf(t[i]) + t[i].length+1;
		}
		return -1;
	}
	function detectTranscript(transcript) {
		const t = + new Date();
		if ((transcript==oldText || (transcript=='te' && oldText.indexOf('te')>-1)) && t-oldTime<1500) return;
		oldTime = t;
		oldText = transcript;
		speechStat = '';
		let txt = transcript.toLowerCase();
		if (txt.length>0) {
			const tok = txt.split(' ')[0];
			if (locale[lang].open.indexOf(tok)>-1) {
				mode = 'open';
				$('#search').hide();
				$('#open').show();
				txt = txt.replace(tok, '').replace(' ', '');
			}
			if (mode=='open' && txt.length>0) {
				const token = document.location.search.indexOf('?d=')>-1? '&token='+document.location.search.split('?d=')[1].split('&')[0]: '';
				const snd = './talk.php?open='+txt.toLowerCase().replace('.', '')+token+"&lang="+lang+"&host="+document.location.search.split('host=')[1].split('&')[0];
				showLog(snd);
				showLog(' host: '+host);
				fetch(snd).then((response) => {return response.json();}).then((rlocale) => {
					if (rlocale.length>=3 && rlocale[0].ws) {
						$('#openTable').html('<tr><td colspan="5">' + locale[lang].openSelect + '</td></tr>');
						for (let i=0; i<3; i++) {
							$('#openTable').html($('#openTable').html()+'<tr><td>'+(i+1)+'</td><td>'+rlocale[i].title+'</td><td>'+rlocale[i].description+'</td><td>'+rlocale[i].exename+'</td></tr>');
							window.openData.push(rlocale[i]);
						}
					}
					$('#openTable').html('<tr><td>'+txt+'</td><td><input id="oname" class="oname"></input></td><td><button class="btn btn-primary" onClick="findPanel($(\"#oname\").val)">'+locale[lang].searchSubmit+'</button></td></tr>');
					$(function() {$(".oname").autocomplete({source: launcher, select: function(event, ui) {findPanel(ui.item.value, txt); return false;}});});
					window.openData.push(txt);								
					showLog('window.openData1');
					showLog(window.openData);
					mode = 'openTable';
					$('#search').hide();
					$('#open').show();
					txt = '';
				});
			}
			if (mode=='openTable' && txt.length>0) {
				showLog('window.openData2');
				showLog(window.openData);
				showLog(txt);
				txt = txt.replace('numero ', '');
				let num = -1;
				if (txt.indexOf(locale[lang].zero)>-1) num = -1;
				if (txt.indexOf(locale[lang].one)>-1) num = 0;
				if (txt.indexOf(locale[lang].two)>-1) num = 1;
				if (txt.indexOf(locale[lang].three)>-1) num = 2;
				if (!isNaN(txt)) num = txt - 1;
				showLog(num);
				if (num>-1) {
					const exe = (window.openData[num].path? window.openData[num].path: '/runtime/bin/')+ window.openData[num].exename + '&host=' + document.location.search.split('host=')[1].split('&')[0];
					showLog(exe);
					showLog(launcherSrc+"?open="+exe+"&speech="+window.openData[3]+"&lang="+lang+"&id="+window.openData[num].id);
					fetch(launcherSrc+"?open="+exe+"&speech="+window.openData[3]+"&lang="+lang+"&id="+window.openData[num].id)
						.then((response) => {return response.json();})
						.then((rlocale) => {showLog(rlocale);});
					/*showLog("./talk.php?speech="+window.openData[3]+"&lang="+lang+"&id="+window.openData[num].id);
					fetch("./talk.php?speech="+window.openData[3]+"&lang="+lang+"&id="+window.openData[num].id)
						.then((response) => {return response.json();})
						.then((rlocale) => {showLog(rlocale);});*/
				}
				mode = '';
				$('#search').hide();
				$('#open').hide();
				$('#openTable').html('');
			}
			if (locale[lang].search.indexOf(tok)>-1) {
				mode = 'search';
				$('#search').show(); $('#open').hide();
				// txt = txt.replace(tok+' ', '').replace('ing', 'inj').replace('inch', 'inj').split(' ').join('_').split('.').join('_').toUpperCase();
				txt = txt.replace(tok+' ', '').split(' ').join('_').split('.').join('_').split('-').join('_').toUpperCase();
				const token = document.location.search.indexOf('?d=')>-1? '&token='+document.location.search.split('?d=')[1].split('&')[0]: '';
				const snd = './talk.php?search='+txt+token+"&lang="+lang+"&speech="+tok;
				$('#openTable').html('<tr><td colspan="5">' + locale[lang].searchCorrect + '</td></tr><tr><td>'+txt+'</td><td><input id="sname" class="sname"></input></td><td><button class="btn btn-primary" onClick="findComponent($(\"#sname\").val)">'+locale[lang].searchSubmit+'</button></td></tr>');
				$(function() {$(".sname").autocomplete({source: names, select: function(event, ui) {findComponent(ui.item.value, txt); return false;}});});
				showLog(snd);
				fetch(snd).then((response) => {return response.text();}).then((rlocale) => {
					showLog(rlocale); /*alert(rlocale);*/
					if (rlocale=='OK') $('#openTable').html('');
				});
				// mode = '';
				// $('#search').hide(); $('#open').hide();
			}
			if (locale[lang].list.indexOf(tok)>-1) {
				mode = 'list';
				txt = txt.replace(tok, '').replace(' ', '');
			}
			if (mode=='list') {
				const token = document.location.search.indexOf('?d=')>-1? '&token='+document.location.search.split('?d=')[1].split('&')[0]: '';
				const snd = './talk.php?list='+tok+token;
				fetch(snd).then((response) => {return response.json();}).then((rlocale) => {showLog(rlocale); alert(rlocale);});
				mode = '';
				$('#search').hide(); $('#open').hide();
			}
			showLog('mode: '+mode+', tok: '+tok);
		}
		return;
		// transcript.replace('sky ','');
	}

	function findPanel(txt, speech) {
		console.log(txt);
		const token = document.location.search.indexOf('?d=')>-1? '&token='+document.location.search.split('?d=')[1].split('&')[0]: '';
		const snd = './talk.php?open='+txt+token+"&lang="+lang+"&speech="+speech;
		showLog(snd);
		fetch(snd).then((response) => {return response.text();}).then((rlocale) => {showLog(rlocale); /*alert(rlocale);*/});
	}
	function findComponent(txt, speech) {
		showLog(txt);
		showLog(speech);
		const token = document.location.search.indexOf('?d=')>-1? '&token='+document.location.search.split('?d=')[1].split('&')[0]: '';
		const snd = './talk.php?search='+txt+token+"&lang="+lang+"&speech="+speech;
		showLog(snd);
		fetch(snd).then((response) => {return response.text();}).then((rlocale) => {showLog(rlocale); alert(rlocale);$('#openTable').html('');});
		$(".sname").val(txt);
	}
	if (!String.prototype.replaceAll) {
		String.prototype.replaceAll = function(search, replace) {
			return this.split(search).join(replace);
		};
	}
	function extractId(name) {
		if (name.indexOf('(')>-1) {
			let tok = name.split('(');
			return [tok[0].replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), tok[1].replaceAll('.','_').replaceAll(' ','_').replaceAll(')','').replace(/_+$/, '')];
		}
		return [name.replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), false];
	}
	function initLattice(sections) { 
		for (let j=0; j<sections.length; j++) { // if(i>1) break;
			const components = sections[j].components;
			if (components) for (let i=0; i<components.length; i++) {
				const id = extractId(components[i].name);
				names.push(id[0]);
				if (components[i].embedded) {
					for (let j=0; j<components[i].embedded.length; j++)  {names.push(components[i].embedded[j]);}
				}
			}
		}
	}
	function initSearch(latticeFile) {
		fetch(latticeFile).then((response) => {return response.json();}).then((flattice) => {
			lattice = flattice;
			if (Object.keys(lattice).length>0) {
				for (let i in lattice) {
					if (lattice[i].sections) initLattice(lattice[i].sections);
				}
			}
		});
	}

	/* recognition.onresult = (event) => {
		// The SpeechRecognitionEvent results property returns a SpeechRecognitionResultList object
		// The SpeechRecognitionResultList object contains SpeechRecognitionResult objects.
		// It has a getter so it can be accessed like an array
		// The first [0] returns the SpeechRecognitionResult at position 0.
		// Each SpeechRecognitionResult object contains SpeechRecognitionAlternative objects
		// that contain individual results.
		// These also have getters so they can be accessed like arrays.
		// The second [0] returns the SpeechRecognitionAlternative at position 0.
		// We then return the transcript property of the SpeechRecognitionAlternative object
		const color = event.results[0][0].transcript;
		diagnostic.textContent = `Result received: ${color}.`;
		bg.style.backgroundColor = color;
	};*/
	recognition.onresult = (event) => {
		let interimTranscript = '';
		for (let i = event.resultIndex, len = event.results.length; i < len; i++) {
			let transcript = event.results[i][0].transcript;
			// const dt = + new Time() - t0;
			if (event.results[i][0].transcript.length) showLog(' mode '+mode+', i: '+i+', transcripts: '+event.results[i][0].transcript+(event.results[i][1]? ', transcripts: '+event.results[i][1].transcript:'')+'\n');
			if (event.results[i].isFinal) {
				detectTranscript(transcript.toLowerCase());
				var meno = transcript.toLowerCase().indexOf('meno ');
				console.log(meno, transcript.substring(meno+5,meno+6), transcript.substring(meno+5,meno+6) % 1);
				if (meno>-1 && transcript.substring(meno+5,meno+6) % 1 === 0) {
					console.log('replace');
					transcript = transcript.replace(transcript.substring(meno,meno+5), '-');
				}
				// finalTranscript += transcript+'<br>\\n';
				if (transcript.length>0) finalTranscript = transcript;
			} else {
				interimTranscript += transcript+'<br>\\n';
			}
			document.getElementById('transcriptDiv').innerHTML = '<i style=\"color:#ddd;\">' + interimTranscript + '</i>';
		}
		// document.getElementById('transcriptDiv').innerHTML = finalTranscript + '<i style=\"color:#00d;\">' + interimTranscript + '</i>';
		document.getElementById('transcriptDiv').innerHTML =  '<i style=\"color:#00d;\">' + finalTranscript + '</i>';
	};
	recognition.start();