From 464b4cd4b43f0416783126592f15a837acb52975 Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:12:43 +0000 Subject: [PATCH 01/51] Add textbox and more --- js/pgm.js | 11 ++++++- src/js/user_interactions.js | 59 +++++++++++++++++++++++++++++++++++++ text_to_speech_settings.php | 31 ++++++++++++++----- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/js/pgm.js b/js/pgm.js index 3077acbc7..6b57a31ac 100644 --- a/js/pgm.js +++ b/js/pgm.js @@ -467,9 +467,18 @@ $(document).scrollTo(pos);focus();setTimeout(overlib,10);setTimeout(cClick,100)} function saveCurrentPosition(){let pos=0;const top_pos=$(window).scrollTop()-$('.wsty').not('.hide').eq(0).height();$('.wsty').not('.hide').each(function(){if($(this).offset().top>=top_pos){pos=$(this).attr('data_pos');return!1}});$.ajax({type:"POST",url:'inc/ajax.php',data:{action:"reading_position",action_type:"text",tid:TID,tposition:pos},async:!1})} function getPhoneticText(text,lang){let phoneticText;$.ajax('inc/ajax.php',{async:!1,data:{action:"query",action_type:"phonetic_reading",text:text,lang:lang},dataType:"json",type:"GET",}).done(function(data){phoneticText=data.phonetic_reading});return phoneticText} async function getPhoneticTextAsync(text,lang){return $.getJSON('inc/ajax.php',{action:"query",action_type:"phonetic_reading",text:text,lang:lang})} +function findDataString(obj){for(const key in obj){if(obj.hasOwnProperty(key)){if(typeof obj[key]==='string'&&obj[key].startsWith('data:')){return obj[key]}else if(typeof obj[key]==='object'){const result=findDataString(obj[key]);if(result){return result}}}} +return null} function readRawTextAloud(text,lang,rate,pitch,voice){let msg=new SpeechSynthesisUtterance();const trimmed=lang.substring(0,2);const prefix='tts['+trimmed;msg.text=text;if(lang){msg.lang=lang} const useVoice=voice||getCookie(prefix+'Voice]');if(useVoice){const voices=window.speechSynthesis.getVoices();for(let i=0;iresponse.json()).then(data=>{const encodeString=findDataString(data) +const utter=new Audio(encodeString) +utter.play()}).catch(error=>{console.error(error)})}else{window.speechSynthesis.speak(msg)} +return msg} function readTextAloud(text,lang,rate,pitch,voice){if(lang.startsWith('ja')){getPhoneticTextAsync(text,lang).then(function(data){readRawTextAloud(data.phonetic_reading,lang,rate,pitch,voice)})}else{readRawTextAloud(text,lang,rate,pitch,voice)}} \ No newline at end of file diff --git a/src/js/user_interactions.js b/src/js/user_interactions.js index e1ddf3a90..7d9105940 100644 --- a/src/js/user_interactions.js +++ b/src/js/user_interactions.js @@ -216,6 +216,30 @@ async function getPhoneticTextAsync(text, lang) { } ); } + + + + +/** + * Helper function used in readRawTextAloud + * + * @param {dict} obj object to search in + */ +function findDataString(obj) { +for (const key in obj) { +if (obj.hasOwnProperty(key)) { +if (typeof obj[key] === 'string' && obj[key].startsWith('data:')) { + return obj[key]; +} else if (typeof obj[key] === 'object') { + const result = findDataString(obj[key]); + if (result) { + return result; + } +} +} +} +return null; // Return null if no matching string is found +} /** * Read a text aloud, only work with a phonetic version. @@ -258,7 +282,42 @@ async function getPhoneticTextAsync(text, lang) { } else if (getCookie(prefix + 'Pitch]')) { msg.pitch = parseInt(getCookie(prefix + 'Pitch]'), 10); } + if (getCookie(prefix + 'Request]') != "") + { + let fetchRequest = JSON.parse(getCookie(prefix+ 'Request]')); +//Keep inside scope or eval won't run properly +function deepReplace(obj, searchString, replacefunc) { + for (let key in obj) { + if (typeof obj[key] === 'object') { + // Recursively search nested objects + deepReplace(obj[key], searchString, replacefunc); + } else if (typeof obj[key] === 'string' && obj[key].includes(searchString)) { + // If the property is a string and contains the searchString, replace it + obj[key] = obj[key].replace(searchString, replacefunc(searchString)); + } + } + } + deepReplace(fetchRequest,'text',eval) + deepReplace(fetchRequest,'lang',eval) + + +fetchRequest.options.body = JSON.stringify(fetchRequest.options.body) + +fetch(fetchRequest.input, fetchRequest.options) +.then(response => response.json()) +.then(data => { + +const encodeString = findDataString(data) +const utter = new Audio(encodeString) +utter.play() +}) +.catch(error => { + console.error(error) +}); + } + else { window.speechSynthesis.speak(msg); + } return msg; } diff --git a/text_to_speech_settings.php b/text_to_speech_settings.php index d61d19224..646ec5f43 100644 --- a/text_to_speech_settings.php +++ b/text_to_speech_settings.php @@ -25,6 +25,7 @@ function get_language_code($language) { global $langDefs; + //TODO Add language codes to edit_language.php to fix error of unsupported lan return $langDefs[$language][1]; } @@ -134,6 +135,20 @@ function tts_settings_form() + + Voice API Request + Enter Voice API Request
+{
+"input": ...,
+"options": ...
+}
;use text and lang with no quotes for placeholder, and leave empty or default SpeechAPI won't function + + + + + " /> + + isset($params['secure']), 'httponly' => isset($params['httponly']) */ - $cookie_options = array( - 'expires' => strtotime('+5 years'), - 'domain' => - ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false, - 'path' => '/', - 'samesite' => 'Strict' // None || Lax || Strict - ); + // $cookie_options = array( + // 'expires' => strtotime('+5 years'), + // 'domain' => + // ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false, + // 'path' => '/', + // 'samesite' => 'Strict' // None || Lax || Strict + // ); //setcookie($prefix . ']', $record['LgID'], $cookie_options); setcookie($prefix . 'Voice]', $form['LgVoice'], $cookie_options); setcookie($prefix . 'Rate]', $form['LgTTSRate'], $cookie_options); setcookie($prefix . 'Pitch]', $form['LgPitch'], $cookie_options); + setcookie($prefix. 'Request]',$form['LgRequest'],$cookie_options); } $message = ''; From 6fa7f1ccdf6ceab82b344e844c8bb1fef980d050 Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:22:39 +0000 Subject: [PATCH 02/51] replace string with var --- js/pgm.js | 6 +++--- src/js/user_interactions.js | 37 ++++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/js/pgm.js b/js/pgm.js index 6b57a31ac..e8596acf3 100644 --- a/js/pgm.js +++ b/js/pgm.js @@ -467,15 +467,15 @@ $(document).scrollTo(pos);focus();setTimeout(overlib,10);setTimeout(cClick,100)} function saveCurrentPosition(){let pos=0;const top_pos=$(window).scrollTop()-$('.wsty').not('.hide').eq(0).height();$('.wsty').not('.hide').each(function(){if($(this).offset().top>=top_pos){pos=$(this).attr('data_pos');return!1}});$.ajax({type:"POST",url:'inc/ajax.php',data:{action:"reading_position",action_type:"text",tid:TID,tposition:pos},async:!1})} function getPhoneticText(text,lang){let phoneticText;$.ajax('inc/ajax.php',{async:!1,data:{action:"query",action_type:"phonetic_reading",text:text,lang:lang},dataType:"json",type:"GET",}).done(function(data){phoneticText=data.phonetic_reading});return phoneticText} async function getPhoneticTextAsync(text,lang){return $.getJSON('inc/ajax.php',{action:"query",action_type:"phonetic_reading",text:text,lang:lang})} +function deepReplace(obj,searchString,replaceVar){for(let key in obj){if(typeof obj[key]==='object'){deepReplace(obj[key],searchString,replaceVar)}else if(typeof obj[key]==='string'&&obj[key].includes(searchString)){obj[key]=obj[key].replace(searchString,replaceVar)}}} function findDataString(obj){for(const key in obj){if(obj.hasOwnProperty(key)){if(typeof obj[key]==='string'&&obj[key].startsWith('data:')){return obj[key]}else if(typeof obj[key]==='object'){const result=findDataString(obj[key]);if(result){return result}}}} return null} function readRawTextAloud(text,lang,rate,pitch,voice){let msg=new SpeechSynthesisUtterance();const trimmed=lang.substring(0,2);const prefix='tts['+trimmed;msg.text=text;if(lang){msg.lang=lang} const useVoice=voice||getCookie(prefix+'Voice]');if(useVoice){const voices=window.speechSynthesis.getVoices();for(let i=0;iresponse.json()).then(data=>{const encodeString=findDataString(data) const utter=new Audio(encodeString) diff --git a/src/js/user_interactions.js b/src/js/user_interactions.js index 7d9105940..ec449d292 100644 --- a/src/js/user_interactions.js +++ b/src/js/user_interactions.js @@ -218,7 +218,24 @@ async function getPhoneticTextAsync(text, lang) { } - +/** + * Helper function used in readRawTextAloud + * + * @param {dict} obj to search in + * @param {string} string to find + * @param {string} replacement variable + * */ +function deepReplace(obj, searchString, replaceVar) { + for (let key in obj) { + if (typeof obj[key] === 'object') { + // Recursively search nested objects + deepReplace(obj[key], searchString, replaceVar); + } else if (typeof obj[key] === 'string' && obj[key].includes(searchString)) { + // If the property is a string and contains the searchString, replace it + obj[key] = obj[key].replace(searchString,replaceVar ); + } + } + } /** * Helper function used in readRawTextAloud @@ -285,20 +302,10 @@ return null; // Return null if no matching string is found if (getCookie(prefix + 'Request]') != "") { let fetchRequest = JSON.parse(getCookie(prefix+ 'Request]')); -//Keep inside scope or eval won't run properly -function deepReplace(obj, searchString, replacefunc) { - for (let key in obj) { - if (typeof obj[key] === 'object') { - // Recursively search nested objects - deepReplace(obj[key], searchString, replacefunc); - } else if (typeof obj[key] === 'string' && obj[key].includes(searchString)) { - // If the property is a string and contains the searchString, replace it - obj[key] = obj[key].replace(searchString, replacefunc(searchString)); - } - } - } - deepReplace(fetchRequest,'text',eval) - deepReplace(fetchRequest,'lang',eval) + + //TODO can expose more vars to Request + deepReplace(fetchRequest,'text',text) + deepReplace(fetchRequest,'lang',lang) fetchRequest.options.body = JSON.stringify(fetchRequest.options.body) From f5244412ee9fe0306875f85579ae5865185cc845 Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:44:02 +0000 Subject: [PATCH 03/51] remove comment and domain may not set cookie if on remote serv --- text_to_speech_settings.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/text_to_speech_settings.php b/text_to_speech_settings.php index 646ec5f43..541a11606 100644 --- a/text_to_speech_settings.php +++ b/text_to_speech_settings.php @@ -318,13 +318,11 @@ function tts_save_settings($form): void 'secure' => isset($params['secure']), 'httponly' => isset($params['httponly']) */ - // $cookie_options = array( - // 'expires' => strtotime('+5 years'), - // 'domain' => - // ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false, - // 'path' => '/', - // 'samesite' => 'Strict' // None || Lax || Strict - // ); + $cookie_options = array( + 'expires' => strtotime('+5 years'), + 'path' => '/', + 'samesite' => 'Strict' // None || Lax || Strict + ); //setcookie($prefix . ']', $record['LgID'], $cookie_options); setcookie($prefix . 'Voice]', $form['LgVoice'], $cookie_options); setcookie($prefix . 'Rate]', $form['LgTTSRate'], $cookie_options); From 4cb8fe551bb873f935c2f75b5fba0682a714a4d5 Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:32:05 -0500 Subject: [PATCH 04/51] Update text_to_speech_settings.php --- text_to_speech_settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_to_speech_settings.php b/text_to_speech_settings.php index 541a11606..36286092a 100644 --- a/text_to_speech_settings.php +++ b/text_to_speech_settings.php @@ -141,7 +141,7 @@ function tts_settings_form() { "input": ..., "options": ... -};use text and lang with no quotes for placeholder, and leave empty or default SpeechAPI won't function +};use the placeholders specified in docs, and leave empty or default SpeechAPI won't function From 51b46d29d5d93161d24b6477badbbefcbca09a4f Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:33:36 -0500 Subject: [PATCH 05/51] Update user_interactions.js --- src/js/user_interactions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/user_interactions.js b/src/js/user_interactions.js index ec449d292..6c333b13b 100644 --- a/src/js/user_interactions.js +++ b/src/js/user_interactions.js @@ -304,8 +304,8 @@ return null; // Return null if no matching string is found let fetchRequest = JSON.parse(getCookie(prefix+ 'Request]')); //TODO can expose more vars to Request - deepReplace(fetchRequest,'text',text) - deepReplace(fetchRequest,'lang',lang) + deepReplace(fetchRequest,'lwt_term',text) + deepReplace(fetchRequest,'lwt_lang',lang) fetchRequest.options.body = JSON.stringify(fetchRequest.options.body) @@ -353,4 +353,4 @@ function readTextAloud(text, lang, rate, pitch, voice) { } else { readRawTextAloud(text, lang, rate, pitch, voice); } -} \ No newline at end of file +} From 5df2532499157d0b124d944f4d0ad5cd5acd01dd Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:34:16 -0500 Subject: [PATCH 06/51] Update text_to_speech_settings.php --- text_to_speech_settings.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/text_to_speech_settings.php b/text_to_speech_settings.php index 36286092a..0f271ac29 100644 --- a/text_to_speech_settings.php +++ b/text_to_speech_settings.php @@ -25,7 +25,6 @@ function get_language_code($language) { global $langDefs; - //TODO Add language codes to edit_language.php to fix error of unsupported lan return $langDefs[$language][1]; } @@ -141,7 +140,7 @@ function tts_settings_form() { "input": ..., "options": ... -};use the placeholders specified in docs, and leave empty or default SpeechAPI won't function +};'lwt_text'(required),'lwt_lang"(optional) From e13a72f6ec631429af7d61b8ff20912a52493ca1 Mon Sep 17 00:00:00 2001 From: ProgramComputer <22284856+ProgramComputer@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:24:44 +0000 Subject: [PATCH 07/51] add bracket --- text_to_speech_settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_to_speech_settings.php b/text_to_speech_settings.php index 0f271ac29..78b1c3d66 100644 --- a/text_to_speech_settings.php +++ b/text_to_speech_settings.php @@ -235,7 +235,7 @@ function presetTTSData() ); $('#rate').val(getCookie('tts[' + CURRENT_LANGUAGE + 'Rate]')); $('#pitch').val(getCookie('tts[' + CURRENT_LANGUAGE + 'Pitch]')); - $('#request').val(getCookie('tts[' + CURRENT_LANGUAGE + 'Request')); + $('#request').val(getCookie('tts[' + CURRENT_LANGUAGE + 'Request]')); } /** From 1a6103a9741a9f950c7acb3abf47fd031162a16a Mon Sep 17 00:00:00 2001 From: Paul <22284856+ProgramComputer@users.noreply.github.com> Date: Tue, 26 Dec 2023 12:50:15 -0600 Subject: [PATCH 08/51] Update pgm.js --- js/pgm.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/js/pgm.js b/js/pgm.js index e8596acf3..c85e47aa2 100644 --- a/js/pgm.js +++ b/js/pgm.js @@ -27,19 +27,19 @@ r+=min<10?("0"+min):min;r+=":";r+=sec<10?("0"+sec):sec;document.getElementById(i * @license Unlicense * @since 1.6.16-fork */ -function extend_adv_xpath(){$('#adv').prepend('

'+''+'custom: '+''+''+'-'+''+'

');$('#adv').show();$('*').removeClass("lwt_marked_text");$('*[class=\'\']').removeAttr('class');var val1=$($('#mark_action :selected').data()).get(0).tagName.toLowerCase(),attr='',node_count=0,attr_v='',attr_p='',val_p='';for(var i=0,attrs=this[0].attributes,l=attrs.length;i'+''+'contains id: «'+id_cont[z]+'»'+''+'

')}} -if(attrs.item(i).nodeName=='class'){var cl_cont=attrs.item(i).nodeValue.split(' ');for(var z=0;z'+''+'contains class: «'+cl_cont[z]+'»'+''+'

')}} +function extend_adv_xpath(){$('#adv').prepend('

'+''+'custom: '+''+''+'-'+''+'

');$('#adv').show();$('*').removeClass("lwt_marked_text");$('*[class=\'\']').removeAttr('class');var val1=$($('#mark_action :selected').data()).get(0).tagName.toLowerCase(),attr='',node_count=0,attr_v='',attr_p='',val_p='';for(var i=0,attrs=this[0].attributes,l=attrs.length;i'+''+'contains id: «'+id_cont[z]+'»'+''+'

')}} +if(attrs.item(i).nodeName=='class'){var cl_cont=attrs.item(i).nodeValue.split(' ');for(var z=0;z'+''+'contains class: «'+cl_cont[z]+'»'+''+'

')}} if(i>0)attr_v+=' and ';if(i==0)attr_v+='[';attr_v+='@'+attrs.item(i).nodeName;attr_v+='="'+attrs.item(i).nodeValue+'"';if(i==(attrs.length-1))attr_v+=']'} -this.parents().each(function(){var pa=$(this).get(0);for(var i=0,attrs=pa.attributes,l=attrs.length;i'+''+'parent contains id: «'+id_cont[z]+'»'+''+'

')}} -if(attrs.item(i).nodeName=='class'){cl_cont=attrs.item(i).nodeValue.split(' ');for(var z=0;zparent contains class: «'+cl_cont[z]+'»

')}}}} +this.parents().each(function(){var pa=$(this).get(0);for(var i=0,attrs=pa.attributes,l=attrs.length;i'+''+'parent contains id: «'+id_cont[z]+'»'+''+'

')}} +if(attrs.item(i).nodeName=='class'){cl_cont=attrs.item(i).nodeValue.split(' ');for(var z=0;zparent contains class: «'+cl_cont[z]+'»

')}}}} if(attrs.length>1||attrs.item(i).nodeValue!='lwt_filtered_text'){if(i>0&&attrs.item(i).nodeValue!='lwt_filtered_text')attr_p+=' and ';if(i==0)attr_p+='[';if(attrs.item(i).nodeValue!='lwt_filtered_text')attr_p+='@'+attrs.item(i).nodeName;if(attrs.item(i).nodeValue!='lwt_filtered_text')attr_p+='="'+attrs.item(i).nodeValue.replace('lwt_filtered_text','').trim()+'"';if(i==(attrs.length-1))attr_p+=']'}} -val_p=pa.tagName.toLowerCase()+attr_p+'/'+val_p;attr_p='';pa='';node_count++});$('#adv').prepend('

all: « /'+val_p.replace('=""','')+val1+attr_v.replace('=""','')+' »

');$('#adv input[type="radio"]').each(function(z){if(typeof z=='undefined')z=1;if(typeof $(this).attr('id')=='undefined'){$(this).attr('id','rb_'+z++)} +val_p=pa.tagName.toLowerCase()+attr_p+'/'+val_p;attr_p='';pa='';node_count++});$('#adv').prepend('

all: « /'+val_p.replace('=""','')+val1+attr_v.replace('=""','')+' »

');$('#adv input[type="radio"]').each(function(z){if(typeof z=='undefined')z=1;if(typeof $(this).attr('id')=='undefined'){$(this).attr('id','rb_'+z++)} $(this).after('')})} function feedwizard_prepare_interaction(){if($('#lwt_sel').html()==''&&$('input[name=\'step\']').val()==2) $('#next').prop('disabled',!0);else $('#next').prop('disabled',!1);$('#lwt_last').css('margin-top',$('#lwt_header').height());$('#lwt_header').nextAll().on('click',function(event){if(!($(event.target).hasClass("lwt_selected_text"))){if(!($(event.target).hasClass("lwt_filtered_text"))){if($(event.target).hasClass("lwt_marked_text")){$("#mark_action").empty();$('*').removeClass("lwt_marked_text");$('*[class=\'\']').removeAttr('class');$('button[name="button"]').prop('disabled',!0);$('