diff --git a/do_text_text.php b/do_text_text.php
index bdcf8ec5..4059afbc 100644
--- a/do_text_text.php
+++ b/do_text_text.php
@@ -68,7 +68,8 @@ function getTextData($textid)
*
* @return array{LgName: string, LgDict1URI: string,
* LgDict2URI: string, LgGoogleTranslateURI: string, LgTextSize: int,
- * LgRemoveSpaces: int, LgRightToLeft: int}|false|null Record corresponding to this language.
+ * LgRegexpWordCharacters: string, LgRemoveSpaces: int,
+ * LgRightToLeft: int, Lg}|false|null Record corresponding to this language.
*
* @global string $tbpref Table name prefix
*
@@ -79,7 +80,7 @@ function get_language_settings($langid)
global $tbpref;
$sql =
'SELECT LgName, LgDict1URI, LgDict2URI, LgGoogleTranslateURI,
- LgTextSize, LgRemoveSpaces, LgRightToLeft
+ LgTextSize, LgRegexpWordCharacters, LgRemoveSpaces, LgRightToLeft
FROM ' . $tbpref . 'languages
WHERE LgID = ' . $langid;
$res = do_mysqli_query($sql);
@@ -759,24 +760,28 @@ function do_text_text_content($textid, $only_body=true): void
$var_array = array(
// Change globals from jQuery hover
'LWT_DATA' => array(
+
'language' => array(
'dict_link1' => $wb1,
'dict_link2' => $wb2,
'translator_link' => $wb3,
'delimiter' => tohtml(
- str_replace(
- array('\\',']','-','^'),
- array('\\\\','\\]','\\-','\\^'),
- getSettingWithDefault('set-term-translation-delimiters')
- )
- ),
+ str_replace(
+ array('\\',']','-','^'),
+ array('\\\\','\\]','\\-','\\^'),
+ getSettingWithDefault('set-term-translation-delimiters')
+ )
+ ),
+ 'word_parsing' => $record['LgRegexpWordCharacters'],
'rtl' => $rtlScript
),
+
'text' => array(
'id' => $textid,
'reading_position' => $pos,
'annotations' => json_decode(annotation_to_json($ann))
),
+
'settings' => array(
'jQuery_tooltip' => (
getSettingWithDefault('set-tooltip-mode') == 2 ? 1 : 0
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index aa62bbad..b0067553 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -14,15 +14,20 @@ ones are marked like "v1.0.0-fork".
* On `inc/kernel_utility.php`: `LWT_APP_VERSION` and `LWT_RELEASE_DATE`.
* On `api.php`: `LWT_API_VERSION` and `LWT_API_RELEASE_DATE`.
* `src/js/jq_pgm.js`: `LWT_DATA`.
-* Word reading can be allowed on hover or on click.
-Pull request [#147](https://github.com/HugoFara/lwt/pull/147) by
-[@ProgramComputer](https://github.com/ProgramComputer).
-* You can add a custom text reader with the new voice API feature!
-A courtesy of [@ProgramComputer](https://github.com/ProgramComputer) on pull request
-[#153](https://github.com/HugoFara/lwt/pull/153).
-A feature first requested on [#143](https://github.com/HugoFara/lwt/issues/143).
-Discussion in open on [#174](https://github.com/HugoFara/lwt/discussions/174).
-* Starts a cleaner database management. Database schema is defined in `db/schema/baseline.sql` and no longer in PHP code.
+* Imprtant additions to Text-To-Speech (TTS):
+ * Word can be read on hover or on click.
+ Pull request [#147](https://github.com/HugoFara/lwt/pull/147) by
+ [@ProgramComputer](https://github.com/ProgramComputer).
+ * You can add a custom text reader with the new voice API feature!
+ A courtesy of [@ProgramComputer](https://github.com/ProgramComputer) on pull request
+ [#153](https://github.com/HugoFara/lwt/pull/153).
+ A feature first requested on [#143](https://github.com/HugoFara/lwt/issues/143).
+ Discussion in open on [#174](https://github.com/HugoFara/lwt/discussions/174).
+ * New JS functions: `readTextWithExternal` to read a text with an external
+ application and `speechDispatcher` that can choose a text reader
+ (browser or third party).
+* Starts a cleaner database management. Database schema is defined in
+`db/schema/baseline.sql` and no longer in PHP code.
### Changed
@@ -41,6 +46,8 @@ Discussion in open on [#174](https://github.com/HugoFara/lwt/discussions/174).
Solves [#129](https://github.com/HugoFara/lwt/issues/129), thanks to the help of PR
[#168](https://github.com/HugoFara/lwt/pull/168).
* Text reading position was not working consistently when adding several known words.
+* Japanese was always requiring MeCab for TTS, even if it was not used
+([#155](https://github.com/HugoFara/lwt/pull/155)).
### Deprecated
diff --git a/docs/info.html b/docs/info.html
index 45141907..f05fe5d9 100644
--- a/docs/info.html
+++ b/docs/info.html
@@ -2195,7 +2195,9 @@
Added
src/js/jq_pgm.js
: LWT_DATA
.
-Word reading can be allowed on hover or on click.
+Imprtant additions to Text-To-Speech (TTS):
+
+- Word can be read on hover or on click.
Pull request #147 by
@ProgramComputer.
- You can add a custom text reader with the new voice API feature!
@@ -2203,7 +2205,13 @@
Added
#153.
A feature first requested on #143.
Discussion in open on #174.
-- Starts a cleaner database management. Database schema is defined in
db/schema/baseline.sql
and no longer in PHP code.
+- New JS functions:
readTextWithExternal
to read a text with an external
+application and speechDispatcher
that can choose a text reader
+(browser or third party).
+
+
+Starts a cleaner database management. Database schema is defined in
+db/schema/baseline.sql
and no longer in PHP code.
Changed
@@ -2225,6 +2233,8 @@ Fixed
Solves #129, thanks to the help of PR
#168.
- Text reading position was not working consistently when adding several known words.
+- Japanese was always requiring MeCab for TTS, even if it was not used
+(#155).
Deprecated
diff --git a/edit_languages.php b/edit_languages.php
index c4567afe..1881848e 100644
--- a/edit_languages.php
+++ b/edit_languages.php
@@ -692,7 +692,7 @@ function (translation) {
const text = document.forms.lg_form.LgVoiceAPIDemo.value;
const lang = ;
- if( readTextAloud(text, lang))
+ if( speechDispatcher(text, lang))
{
LWT_DATA.language.ttsVoiceApi = prevApi;
}
diff --git a/ggl.php b/ggl.php
index 8ff73fc8..230772b4 100644
--- a/ggl.php
+++ b/ggl.php
@@ -80,7 +80,7 @@ function translate_term($text, $file, $sl, $tl): void
$('#textToSpeech').on('click', function () {
const txt = ;
- readTextAloud(txt, );
+ speechDispatcher(txt, );
});
$(document).ready(function() {
diff --git a/js/pgm.js b/js/pgm.js
index 43200365..35d0e959 100644
--- a/js/pgm.js
+++ b/js/pgm.js
@@ -54,7 +54,7 @@ $(document).on('click','.delete_selection',lwt_feed_wizard.deleteSelection);$(do
* @author andreask7
* @since 1.6.16-fork
*/
-LWT_DATA={language:{dict_link1:'',dict_link2:'',translator_link:'',delimiter:'',rtl:!1,ttsVoiceApi:''},text:{id:0,reading_position:-1,annotations:{}},word:{id:0},test:{solution:''},settings:{jQuery_tooltip:!1,hts:0,word_status_filter:''}};TEXTPOS=-1;OPENED=0;WID=0;TID=0;WBLINK1='';WBLINK2='';WBLINK3='';SOLUTION='';ADDFILTER='';RTL=0;ANN_ARRAY={};DELIMITER='';JQ_TOOLTIP=0;HTS=0;function setTransRoman(tra,rom){let form_changed=!1;if($('textarea[name="WoTranslation"]').length==1){$('textarea[name="WoTranslation"]').val(tra);form_changed|=!0}
+LWT_DATA={language:{dict_link1:'',dict_link2:'',translator_link:'',delimiter:'',word_parsing:'',rtl:!1,ttsVoiceApi:''},text:{id:0,reading_position:-1,annotations:{}},word:{id:0},test:{solution:''},settings:{jQuery_tooltip:!1,hts:0,word_status_filter:''}};TEXTPOS=-1;OPENED=0;WID=0;TID=0;WBLINK1='';WBLINK2='';WBLINK3='';SOLUTION='';ADDFILTER='';RTL=0;ANN_ARRAY={};DELIMITER='';JQ_TOOLTIP=0;HTS=0;function setTransRoman(tra,rom){let form_changed=!1;if($('textarea[name="WoTranslation"]').length==1){$('textarea[name="WoTranslation"]').val(tra);form_changed|=!0}
if($('input[name="WoRomanization"]').length==1){$('input[name="WoRomanization"]').val(rom);form_changed|=!0}
if(form_changed)
lwt_form_check.makeDirty();}
@@ -114,17 +114,17 @@ function word_click_event_do_text_text(){const status=$(this).attr('data_status'
let hints;if(LWT_DATA.settings.jQuery_tooltip){hints=make_tooltip($(this).text(),$(this).attr('data_trans'),$(this).attr('data_rom'),status)}else{hints=$(this).attr('title')}
const multi_words=Array(7);for(let i=0;i<7;i++){multi_words[i]=$(this).attr('data_mw'+(i+2))}
if(status<1){run_overlib_status_unknown(LWT_DATA.language.dict_link1,LWT_DATA.language.dict_link2,LWT_DATA.language.translator_link,hints,LWT_DATA.text.id,$(this).attr('data_order'),$(this).text(),multi_words,LWT_DATA.language.rtl);showRightFrames('edit_word.php?tid='+LWT_DATA.text.id+'&ord='+$(this).attr('data_order')+'&wid=')}else if(status==99){run_overlib_status_99(LWT_DATA.language.dict_link1,LWT_DATA.language.dict_link2,LWT_DATA.language.translator_link,hints,LWT_DATA.text.id,$(this).attr('data_order'),$(this).text(),$(this).attr('data_wid'),multi_words,LWT_DATA.language.rtl,ann)}else if(status==98){run_overlib_status_98(LWT_DATA.language.dict_link1,LWT_DATA.language.dict_link2,LWT_DATA.language.translator_link,hints,LWT_DATA.text.id,$(this).attr('data_order'),$(this).text(),$(this).attr('data_wid'),multi_words,LWT_DATA.language.rtl,ann)}else{run_overlib_status_1_to_5(LWT_DATA.language.dict_link1,LWT_DATA.language.dict_link2,LWT_DATA.language.translator_link,hints,LWT_DATA.text.id,$(this).attr('data_order'),$(this).text(),$(this).attr('data_wid'),status,multi_words,LWT_DATA.language.rtl,ann)}
-if(LWT_DATA.settings.hts==2){const lg=getLangFromDict(LWT_DATA.language.translator_link);readTextAloud($(this).text(),lg)}
+if(LWT_DATA.settings.hts==2){const lg=getLangFromDict(LWT_DATA.language.translator_link);speechDispatcher($(this).text(),lg)}
return!1}
function mword_click_event_do_text_text(){const status=$(this).attr('data_status');if(status!=''){let ann='';if((typeof $(this).attr('data_ann'))!=='undefined'){ann=$(this).attr('data_ann')}
run_overlib_multiword(LWT_DATA.language.dict_link1,LWT_DATA.language.dict_link2,LWT_DATA.language.translator_link,LWT_DATA.settings.jQuery_tooltip?make_tooltip($(this).text(),$(this).attr('data_trans'),$(this).attr('data_rom'),status):$(this).attr('title'),LWT_DATA.text.id,$(this).attr('data_order'),$(this).attr('data_text'),$(this).attr('data_wid'),status,$(this).attr('data_code'),ann)}
-if(LWT_DATA.settings.hts==2){const lg=getLangFromDict(LWT_DATA.language.translator_link);readTextAloud($(this).text(),lg)}
+if(LWT_DATA.settings.hts==2){const lg=getLangFromDict(LWT_DATA.language.translator_link);speechDispatcher($(this).text(),lg)}
return!1}
function mword_drag_n_drop_select(event){if(LWT_DATA.settings.jQuery_tooltip)$('.ui-tooltip').remove();const context=$(this).parent();context.one('mouseup mouseout',$(this),function(){clearTimeout(to);$('.nword').removeClass('nword');$('.tword').removeClass('tword');$('.lword').removeClass('lword');$('.wsty',context).css('background-color','').css('border-bottom-color','');$('#pe').remove()});to=setTimeout(function(){let pos;context.off('mouseout');$('.wsty',context).css('background-color','inherit').css('border-bottom-color','rgba(0,0,0,0)').not('.hide,.word').each(function(){f=parseInt($(this).attr('data_code'))*2+parseInt($(this).attr('data_order'))-1;h='';$(this).nextUntil($('[id^="ID-'+f+'-"]',context),'[id$="-1"]').each(function(){l=$(this).attr('data_order');if(typeof l!=='undefined'){h+=''+$(this).text()+''}else{h+=''+$(this).text()+''}});$(this).html(h)});$('#pe').remove();$('body').append('');$('[id$="-1"]',context).not('.hide,.wsty').addClass('nword').each(function(){$(this).attr('data_order',$(this).attr('id').split('-')[1])});$('.word',context).not('.hide').each(function(){$(this).html(''+$(this).text()+'')});if(event.data.annotation==1){$('.wsty',context).not('.hide').each(function(){$(this).children('.tword').last().attr('data_ann',$(this).attr('data_ann')).attr('data_trans',$(this).attr('data_trans')).addClass('content'+$(this).removeClass('status1 status2 status3 status4 status5 status98 status99').attr('data_status'))})}else if(event.data.annotation==3){$('.wsty',context).not('.hide').each(function(){$(this).children('.tword').first().attr('data_ann',$(this).attr('data_ann')).attr('data_trans',$(this).attr('data_trans')).addClass('content'+$(this).removeClass('status1 status2 status3 status4 status5 status98 status99').attr('data_status'))})}
$(context).one('mouseover','.tword',function(){$('html').one('mouseup',function(){$('.wsty',context).each(function(){$(this).addClass('status'+$(this).attr('data_status'))});if(!$(this).hasClass('tword')){$('span',context).removeClass('nword tword lword');$('.wsty',context).css('background-color','').css('border-bottom-color','');$('#pe').remove()}});pos=parseInt($(this).attr('data_order'));$('.lword',context).removeClass('lword');$(this).addClass('lword');$(context).on('mouseleave',function(){$('.lword',context).removeClass('lword')});$(context).one('mouseup','.nword,.tword',function(ev){if(ev.handled!==!0){const len=$('.lword.tword',context).length;if(len>0){g=$('.lword',context).first().attr('data_order');if(len>1){const text=$('.lword',context).map(function(){return $(this).text()}).get().join('');if(text.length>250){alert('selected text is too long!!!')}else{showRightFrames('edit_mword.php?tid='+LWT_DATA.text.id+'&len='+len+'&ord='+g+'&txt='+text)}}else{showRightFrames('edit_word.php?tid='+LWT_DATA.text.id+'&ord='+g+'&txt='+$('#ID-'+g+'-1').text())}}
$('span',context).removeClass('tword nword');ev.handled=!0}})});$(context).hoverIntent({over:function(){$('.lword',context).removeClass('lword');const lpos=parseInt($(this).attr('data_order'));$(this).addClass('lword');if(lpos>pos){for(var i=pos;ilpos;i--){$('.tword[data_order="'+i+'"],.nword[data_order="'+i+'"]',context).addClass('lword')}}},out:function(){},sensitivity:18,selector:'.tword'})},300)}
function word_hover_over(){if(!$('.tword')[0]){const v=$(this).attr('class').replace(/.*(TERM[^ ]*)( .*)*/,'$1');$('.'+v).addClass('hword');if(LWT_DATA.settings.jQuery_tooltip){$(this).trigger('mouseover')}
-if(LWT_DATA.settings.hts==3){const lg=getLangFromDict(LWT_DATA.language.translator_link);readTextAloud($(this).text(),lg)}}}
+if(LWT_DATA.settings.hts==3){const lg=getLangFromDict(LWT_DATA.language.translator_link);speechDispatcher($(this).text(),lg)}}}
function word_hover_out(){$('.hword').removeClass('hword');if(LWT_DATA.settings.jQuery_tooltip)$('.ui-helper-hidden-accessible>div[style]').remove();}
jQuery.fn.extend({tooltip_wsty_content:function(){var re=new RegExp('(['+LWT_DATA.language.delimiter+'])(?! )','g');let title='';if($(this).hasClass('mwsty')){title=""+$(this).attr('data_text')+'
'}else{title=""+$(this).text()+'
'}
const roman=$(this).attr('data_rom');let trans=$(this).attr('data_trans').replace(re,'$1 ');let statname='';const status=parseInt($(this).attr('data_status'));if(status==0)statname='Unknown [?]';else if(status<5)statname='Learning ['+status+']';if(status==5)statname='Learned [5]';if(status==98)statname='Ignored [Ign]';if(status==99)statname='Well Known [WKn]';if(roman!=''){title+='Roman.: '+roman+'
'}
@@ -150,7 +150,7 @@ showRightFrames('set_word_on_hover.php?text='+txt+'&tid='+LWT_DATA.text.id+'&sta
if(e.which==73){if(stat=='0'){showRightFrames('set_word_on_hover.php?text='+txt+'&tid='+LWT_DATA.text.id+'&status=98')}else{showRightFrames('set_word_status.php?wid='+wid+'&tid='+LWT_DATA.text.id+'&ord='+ord+'&status=98');return!1}}
if(e.which==87){if(stat=='0'){showRightFrames('set_word_on_hover.php?text='+txt+'&tid='+LWT_DATA.text.id+'&status=99')}else{showRightFrames('set_word_status.php?wid='+wid+'&tid='+LWT_DATA.text.id+'&ord='+ord+'&status=99')}
return!1}
-if(e.which==80){const lg=getLangFromDict(LWT_DATA.language.translator_link);readTextAloud(txt,lg);return!1}
+if(e.which==80){const lg=getLangFromDict(LWT_DATA.language.translator_link);speechDispatcher(txt,lg);return!1}
if(e.which==84){let popup=!1;let dict_link=LWT_DATA.language.translator_link;if(LWT_DATA.language.translator_link.startsWith('*')){popup=!0;dict_link=substring(dict_link,1)}
if(dict_link.startsWith('ggl.php')){dict_link="http://"+dict_link}
let open_url=!0;let final_url;try{final_url=new URL(dict_link);popup|=final_url.searchParams.has("lwt_popup")}catch(err){if(err instanceof TypeError){open_url=!1}}
@@ -322,7 +322,7 @@ function make_overlib_link_delete_word(txid,wid){return' Delete term '}
function make_overlib_link_wellknown_word(txid,torder){return' I know this term well '}
function make_overlib_link_ignore_word(txid,torder){return' Ignore this term '}
-function make_overlib_audio(txt,lang){let img=document.createElement("img");img.title="Click to read!";img.src="icn/speaker-volume.png";img.style.cursor="pointer";img.setAttribute("onclick","readTextAloud('"+escape_html_chars(txt)+"', '"+(lang||"")+"')");return img.outerHTML}
+function make_overlib_audio(txt,lang){let img=document.createElement("img");img.title="Click to read!";img.src="icn/speaker-volume.png";img.style.cursor="pointer";img.setAttribute("onclick","speechDispatcher('"+escape_html_chars(txt)+"', '"+(lang||"")+"')");return img.outerHTML}
function getStatusName(status){return STATUSES[status]?STATUSES[status].name:'Unknown'}
function getStatusAbbr(status){return STATUSES[status]?STATUSES[status].abbr:'?'}
function translateSentence(url,sentctl){if(sentctl!==undefined&&url!=''){const text=sentctl.value;if(typeof text==='string'){showRightFrames(undefined,createTheDictUrl(url,text.replace(/[{}]/g,'')))}}}
@@ -426,7 +426,7 @@ theadrow=this.parentNode;forEach(theadrow.childNodes,function(cell){if(cell.node
sortrevind=document.getElementById('sorttable_sortrevind');if(sortrevind){sortrevind.parentNode.removeChild(sortrevind)}
this.className+=' sorttable_sorted';sortfwdind=document.createElement('span');sortfwdind.id="sorttable_sortfwdind";sortfwdind.innerHTML=stIsIE?' 6':' ▾';this.appendChild(sortfwdind);row_array=[];col=this.sorttable_columnindex;rows=this.sorttable_tbody.rows;for(var j=0;j12){return sorttable.sort_ddmm}else if(second>12){return sorttable.sort_mmdd}else{sortfn=sorttable.sort_ddmm}}}}
return sortfn},getInnerText:function(node){if(!node)return"";hasInputs=(typeof node.getElementsByTagName=='function')&&node.getElementsByTagName('input').length;if(node.getAttribute("sorttable_customkey")!=null){return node.getAttribute("sorttable_customkey")}else if(typeof node.textContent!='undefined'&&!hasInputs){return node.textContent.replace(/^\s+|\s+$/g,'')}else if(typeof node.innerText!='undefined'&&!hasInputs){return node.innerText.replace(/^\s+|\s+$/g,'')}else if(typeof node.text!='undefined'&&!hasInputs){return node.text.replace(/^\s+|\s+$/g,'')}else{switch(node.nodeType){case 3:if(node.nodeName.toLowerCase()=='input'){return node.value.replace(/^\s+|\s+$/g,'')}
@@ -465,17 +465,19 @@ async function getPhoneticTextAsync(text,lang){return $.getJSON('api.php/v1/phon
function deepReplace(obj,searchValue,replaceValue){for(let key in obj){if(typeof obj[key]==='object'){deepReplace(obj[key],searchValue,replaceValue)}else if(typeof obj[key]==='string'&&obj[key].includes(searchValue)){obj[key]=obj[key].replace(searchValue,replaceValue)}}}
function deepFindValue(obj,searchValue){for(const key in obj){if(obj.hasOwnProperty(key)){if(typeof obj[key]==='string'&&obj[key].startsWith(searchValue)){return obj[key]}else if(typeof obj[key]==='object'){const result=deepFindValue(obj[key],searchValue);if(result){return result}}}}
return null}
-function readTextWithExternalApp(text,lang){let fetchRequest=JSON.parse(LWT_DATA.language.ttsVoiceApi);deepReplace(fetchRequest,'lwt_term',text)
+function readTextWithExternal(text,voice_api,lang){let fetchRequest=JSON.parse(voice_api);deepReplace(fetchRequest,'lwt_term',text)
deepReplace(fetchRequest,'lwt_lang',lang)
fetchRequest.options.body=JSON.stringify(fetchRequest.options.body)
fetch(fetchRequest.input,fetchRequest.options).then(response=>response.json()).then(data=>{const encodeString=deepFindValue(data,'data:')
const utter=new Audio(encodeString)
utter.play()}).catch(error=>{console.error(error)})}
-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;i