From 562b76a13fd8b4ffbc3f0a2745149597e000c721 Mon Sep 17 00:00:00 2001 From: HugoFara Date: Wed, 3 Jan 2024 18:25:12 +0100 Subject: [PATCH 1/3] Lints JS code with ESLint, reviewed manually. --- js/pgm.js | 178 ++++++------ src/js/audio_controller.js | 218 +++++++------- src/js/jq_pgm.js | 552 +++++++++++++++++------------------- src/js/pgm.js | 236 ++++++++------- src/js/translation_api.js | 281 +++++++++--------- src/js/unloadformcheck.js | 106 +++---- src/js/user_interactions.js | 490 ++++++++++++++++---------------- 7 files changed, 1012 insertions(+), 1049 deletions(-) diff --git a/js/pgm.js b/js/pgm.js index 285f2e7e..5095a73f 100644 --- a/js/pgm.js +++ b/js/pgm.js @@ -1,12 +1,12 @@ /** * All the function to make an audio controller in do_text_header.php - * + * * @license Unlicense */ -const lwt_audio_controller={newPosition:function(position){$("#jquery_jplayer_1").jPlayer("playHead",position)},setNewPlayerSeconds:function(){const newval=$("#backtime :selected").val();do_ajax_save_setting('currentplayerseconds',newval)},setNewPlaybackRate:function(){const newval=$("#playbackrate :selected").val();do_ajax_save_setting('currentplaybackrate',newval);$("#jquery_jplayer_1").jPlayer("option","playbackRate",newval*0.1)},setCurrentPlaybackRate:function(){const val=$("#playbackrate :selected").val();$("#jquery_jplayer_1").jPlayer("option","playbackRate",val*0.1)},clickSingle:function(){$("#jquery_jplayer_1").off('bind',$.jPlayer.event.ended+".jp-repeat");$("#do-single").addClass('hide');$("#do-repeat").removeClass('hide');do_ajax_save_setting('currentplayerrepeatmode','0')},clickRepeat:function(){$("#jquery_jplayer_1").on('bind',$.jPlayer.event.ended+".jp-repeat",function(){$("#jquery_jplayer_1").jPlayer("play")});$("#do-repeat").addClass('hide');$("#do-single").removeClass('hide');do_ajax_save_setting('currentplayerrepeatmode','1')},clickBackward:function(){const t=parseInt($("#playTime").text(),10);const b=parseInt($("#backtime").val(),10);let nt=t-b;let st='pause';if(nt<0) -nt=0;if(!$('#jquery_jplayer_1').data().jPlayer.status.paused) -st='play';$("#jquery_jplayer_1").jPlayer(st,nt)},clickForward:function(){const t=parseInt($("#playTime").text(),10);const b=parseInt($("#backtime").val(),10);const nt=t+b;let st='pause';if(!$('#jquery_jplayer_1').data().jPlayer.status.paused) -st='play';$("#jquery_jplayer_1").jPlayer(st,nt)},clickSlower:function(){const val=parseFloat($("#pbvalue").text())-0.1;if(val>=0.5){$("#pbvalue").text(val.toFixed(1)).css({'color':'#BBB'}).animate({color:'#888'},150,function(){});$("#jquery_jplayer_1").jPlayer("playbackRate",val)}},clickFaster:function(){const val=parseFloat($("#pbvalue").text())+0.1;if(val<=4.0){$("#pbvalue").text(val.toFixed(1)).css({'color':'#BBB'}).animate({color:'#888'},150,function(){});$("#jquery_jplayer_1").jPlayer("playbackRate",val)}},setStdSpeed:function(){$("#playbackrate").val(10);lwt_audio_controller.setNewPlaybackRate()},setSlower:function(){let val=$("#playbackrate :selected").val();if(val>5){val--;$("#playbackrate").val(val);lwt_audio_controller.setNewPlaybackRate()}},setFaster:function(){let val=$("#playbackrate :selected").val();if(val<15){val++;$("#playbackrate").val(val);lwt_audio_controller.setNewPlaybackRate()}},} +const lwt_audio_controller={newPosition:function(position){$('#jquery_jplayer_1').jPlayer('playHead',position)},setNewPlayerSeconds:function(){const newval=$('#backtime :selected').val();do_ajax_save_setting('currentplayerseconds',newval)},setNewPlaybackRate:function(){const newval=$('#playbackrate :selected').val();do_ajax_save_setting('currentplaybackrate',newval);$('#jquery_jplayer_1').jPlayer('option','playbackRate',newval*0.1)},setCurrentPlaybackRate:function(){const val=$('#playbackrate :selected').val();$('#jquery_jplayer_1').jPlayer('option','playbackRate',val*0.1)},clickSingle:function(){$('#jquery_jplayer_1').off('bind',$.jPlayer.event.ended+'.jp-repeat');$('#do-single').addClass('hide');$('#do-repeat').removeClass('hide');do_ajax_save_setting('currentplayerrepeatmode','0')},clickRepeat:function(){$('#jquery_jplayer_1').on('bind',$.jPlayer.event.ended+'.jp-repeat',function(){$('#jquery_jplayer_1').jPlayer('play')});$('#do-repeat').addClass('hide');$('#do-single').removeClass('hide');do_ajax_save_setting('currentplayerrepeatmode','1')},clickBackward:function(){const t=parseInt($('#playTime').text(),10);const b=parseInt($('#backtime').val(),10);let nt=t-b;let st='pause';if(nt<0){nt=0} +if(!$('#jquery_jplayer_1').data().jPlayer.status.paused){st='play'} +$('#jquery_jplayer_1').jPlayer(st,nt)},clickForward:function(){const t=parseInt($('#playTime').text(),10);const b=parseInt($('#backtime').val(),10);const nt=t+b;let st='pause';if(!$('#jquery_jplayer_1').data().jPlayer.status.paused){st='play'} +$('#jquery_jplayer_1').jPlayer(st,nt)},clickSlower:function(){const val=parseFloat($('#pbvalue').text())-0.1;if(val>=0.5){$('#pbvalue').text(val.toFixed(1)).css({color:'#BBB'}).animate({color:'#888'},150,function(){});$('#jquery_jplayer_1').jPlayer('playbackRate',val)}},clickFaster:function(){const val=parseFloat($('#pbvalue').text())+0.1;if(val<=4.0){$('#pbvalue').text(val.toFixed(1)).css({color:'#BBB'}).animate({color:'#888'},150,function(){});$('#jquery_jplayer_1').jPlayer('playbackRate',val)}},setStdSpeed:function(){$('#playbackrate').val(10);lwt_audio_controller.setNewPlaybackRate()},setSlower:function(){let val=$('#playbackrate :selected').val();if(val>5){val--;$('#playbackrate').val(val);lwt_audio_controller.setNewPlaybackRate()}},setFaster:function(){let val=$('#playbackrate :selected').val();if(val<15){val++;$('#playbackrate').val(val);lwt_audio_controller.setNewPlaybackRate()}}} function new_pos(p){return lwt_audio_controller.newPosition(p)};function CountUp(server_now,server_start,id,dontrun){if(server_now0){r+=hr<10?("0"+hr):hr;r+=":"} r+=min<10?("0"+min):min;r+=":";r+=sec<10?("0"+sec):sec;document.getElementById(id).innerHTML=r;if(this.dontrun)return;var self=this;setTimeout(function(){self.update(id)},1000)};/** @@ -106,37 +106,35 @@ showRightFrames(url);return!1} return!0} function prepareTextInteractions(){$('.word').each(word_each_do_text_text);$('.mword').each(mword_each_do_text_text);$('.word').on('click',word_click_event_do_text_text);$('#thetext').on('selectstart','span',!1).on('mousedown','.wsty',{annotation:LWT_DATA.settings.annotations_mode},mword_drag_n_drop_select);$('#thetext').on('click','.mword',mword_click_event_do_text_text);$('.word').on('dblclick',word_dblclick_event_do_text_text);$('#thetext').on('dblclick','.mword',word_dblclick_event_do_text_text);$(document).on('keydown',keydown_event_do_text_text);$('#thetext').hoverIntent({over:word_hover_over,out:word_hover_out,interval:150,selector:'.wsty,.mwsty'})};/** * Interaction between LWT and jQuery - * + * * @license unlicense * @author andreask7 * @since 1.6.16-fork */ 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:'',answer_opened:!1},settings:{jQuery_tooltip:!1,hts:0,word_status_filter:''}};WID=0;TID=0;WBLINK1='';WBLINK2='';WBLINK3='';RTL=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();} +if(form_changed){lwt_form_check.makeDirty()}} function containsCharacterOutsideBasicMultilingualPlane(s){return/[\uD800-\uDFFF]/.test(s)} function alertFirstCharacterOutsideBasicMultilingualPlane(s,info){if(!containsCharacterOutsideBasicMultilingualPlane(s)){return 0} const match=/[\uD800-\uDFFF]/.exec(s);alert('ERROR\n\nText "'+info+'" contains invalid character(s) '+'(in the Unicode Supplementary Multilingual Planes, > U+FFFF) like emojis '+'or very rare characters.\n\nFirst invalid character: "'+s.substring(match.index,match.index+2)+'" at position '+(match.index+1)+'.\n\n'+'More info: https://en.wikipedia.org/wiki/Plane_(Unicode)\n\n'+'Please remove this/these character(s) and try again.');return 1} function getUTF8Length(s){return(new Blob([String(s)])).size} function scrollToAnchor(aid){document.location.href='#'+aid} -function do_ajax_save_impr_text(textid,elem_name,form_data){const idwait='#wait'+elem_name.substring(2);$(idwait).html('');$.post('api.php/v1/texts/'+textid+'/annotation',{elem:elem_name,data:form_data},function(data){$(idwait).html('');if("error" in data) -alert('Saving your changes failed, please reload the page and try again! '+'Error message: "'+data.error+'".');},"json")} +function do_ajax_save_impr_text(textid,elem_name,form_data){const idwait='#wait'+elem_name.substring(2);$(idwait).html('');$.post('api.php/v1/texts/'+textid+'/annotation',{elem:elem_name,data:form_data},function(data){$(idwait).html('');if('error' in data){alert('Saving your changes failed, please reload the page and try again! '+'Error message: "'+data.error+'".')}},'json')} function changeImprAnnText(){$(this).prev('input:radio').attr('checked','checked');const textid=$('#editimprtextdata').attr('data_id');const elem_name=$(this).attr('name');const form_data=JSON.stringify($('form').serializeObject());do_ajax_save_impr_text(textid,elem_name,form_data)} function changeImprAnnRadio(){const textid=$('#editimprtextdata').attr('data_id');const elem_name=$(this).attr('name');const form_data=JSON.stringify($('form').serializeObject());do_ajax_save_impr_text(textid,elem_name,form_data)} function updateTermTranslation(wordid,txid){const translation=$(txid).val().trim();const pagepos=$(document).scrollTop();if(translation==''||translation=='*'){alert('Text Field is empty or = \'*\'!');return} -let request={translation:translation,};const failure="Updating translation of term failed!"+"Please reload page and try again.";$.post('api.php/v1/terms/'+wordid+'/translations',request,function(d){if(d==''){alert(failure);return} -if("error" in d){alert(failure+"\n"+d.error);return} -do_ajax_edit_impr_text(pagepos,d.update,wordid)},"json")} +const request={translation};const failure='Updating translation of term failed!'+'Please reload page and try again.';$.post('api.php/v1/terms/'+wordid+'/translations',request,function(d){if(d==''){alert(failure);return} +if('error' in d){alert(failure+'\n'+d.error);return} +do_ajax_edit_impr_text(pagepos,d.update,wordid)},'json')} function addTermTranslation(txid,word,lang){const translation=$(txid).val().trim();const pagepos=$(document).scrollTop();if(translation==''||translation=='*'){alert('Text Field is empty or = \'*\'!');return} -const failure="Adding translation to term failed!"+"Please reload page and try again." -$.post('api.php/v1/terms/new',{translation:translation,term_text:word,lg_id:lang},function(d){if(d==''){alert(failure);return} -if("error" in d){alert(failure+"\n"+d.error);return} -do_ajax_edit_impr_text(pagepos,d.add,d.term_id)},"json")} -function changeTableTestStatus(wordid,up){const status_change=up?'up':'down';const wid=parseInt(wordid,10);$.post('api.php/v1/terms/'+wid+'/status/'+status_change,{},function(data){if(data==""||"error" in data){return} -$('#STAT'+wordid).html(data.increment)},"json")} +const failure='Adding translation to term failed!'+'Please reload page and try again.' +$.post('api.php/v1/terms/new',{translation,term_text:word,lg_id:lang},function(d){if(d==''){alert(failure);return} +if('error' in d){alert(failure+'\n'+d.error);return} +do_ajax_edit_impr_text(pagepos,d.add,d.term_id)},'json')} +function changeTableTestStatus(wordid,up){const status_change=up?'up':'down';const wid=parseInt(wordid,10);$.post('api.php/v1/terms/'+wid+'/status/'+status_change,{},function(data){if(data==''||'error' in data){return} +$('#STAT'+wordid).html(data.increment)},'json')} function check(){let count=0;$('.notempty').each(function(_n){if($(this).val().trim()=='')count++});if(count>0){alert('ERROR\n\n'+count+' field(s) - marked with * - must not be empty!');return!1} -count=0;$('input.checkurl').each(function(_n){if($(this).val().trim().length>0){if(($(this).val().trim().indexOf('http://')!=0)&&($(this).val().trim().indexOf('https://')!=0)&&($(this).val().trim().indexOf('#')!=0)){alert('ERROR\n\nField "'+$(this).attr('data_info')+'" must start with "http://" or "https://" if not empty.');count++}}});$('input.checkregexp').each(function(_n){const regexp=$(this).val().trim();if(regexp.length>0){$.ajax({type:'POST',url:'inc/ajax.php',data:{action:"",action_type:"check_regexp",regex:regexp},async:!1}).always(function(data){if(data!=''){alert(data);count++}})}});$('input[class*="max_int_"]').each(function(_n){const maxvalue=parseInt($(this).attr('class').replace(/.*maxint_([0-9]+).*/,'$1'));if($(this).val().trim().length>0){if($(this).val()>maxvalue){alert('ERROR\n\n Max Value of Field "'+$(this).attr('data_info')+'" is '+maxvalue);count++}}});$('input.checkdicturl').each(function(_n){const translate_input=$(this).val().trim();if(translate_input.length>0){let refinned=translate_input;if(translate_input.startsWith('*')){refinned=translate_input.substring(1)} +count=0;$('input.checkurl').each(function(_n){if($(this).val().trim().length>0){if(($(this).val().trim().indexOf('http://')!=0)&&($(this).val().trim().indexOf('https://')!=0)&&($(this).val().trim().indexOf('#')!=0)){alert('ERROR\n\nField "'+$(this).attr('data_info')+'" must start with "http://" or "https://" if not empty.');count++}}});$('input.checkregexp').each(function(_n){const regexp=$(this).val().trim();if(regexp.length>0){$.ajax({type:'POST',url:'inc/ajax.php',data:{action:'',action_type:'check_regexp',regex:regexp},async:!1}).always(function(data){if(data!=''){alert(data);count++}})}});$('input[class*="max_int_"]').each(function(_n){const maxvalue=parseInt($(this).attr('class').replace(/.*maxint_([0-9]+).*/,'$1'));if($(this).val().trim().length>0){if($(this).val()>maxvalue){alert('ERROR\n\n Max Value of Field "'+$(this).attr('data_info')+'" is '+maxvalue);count++}}});$('input.checkdicturl').each(function(_n){const translate_input=$(this).val().trim();if(translate_input.length>0){let refinned=translate_input;if(translate_input.startsWith('*')){refinned=translate_input.substring(1)} if(!/^https?:\/\//.test(refinned)){refinned='http://'+refinned} try{new URL(refinned)}catch(err){if(err instanceof TypeError){alert('ERROR\n\nField "'+$(this).attr('data_info')+'" should be an URL if not empty.');count++}}}});$('input.posintnumber').each(function(_n){if($(this).val().trim().length>0){if(!(isInt($(this).val().trim())&&(parseInt($(this).val().trim(),10)>0))){alert('ERROR\n\nField "'+$(this).attr('data_info')+'" must be an integer number > 0.');count++}}});$('input.zeroposintnumber').each(function(_n){if($(this).val().trim().length>0){if(!(isInt($(this).val().trim())&&(parseInt($(this).val().trim(),10)>=0))){alert('ERROR\n\nField "'+$(this).attr('data_info')+'" must be an integer number >= 0.');count++}}});$('input.checkoutsidebmp').each(function(_n){if($(this).val().trim().length>0){if(containsCharacterOutsideBasicMultilingualPlane($(this).val())){count+=alertFirstCharacterOutsideBasicMultilingualPlane($(this).val(),$(this).attr('data_info'))}}});$('textarea.checklength').each(function(_n){if($(this).val().trim().length>(0+$(this).attr('data_maxlength'))){alert('ERROR\n\nText is too long in field "'+$(this).attr('data_info')+'", please make it shorter! (Maximum length: '+$(this).attr('data_maxlength')+' char.)');count++}});$('textarea.checkoutsidebmp').each(function(_n){if(containsCharacterOutsideBasicMultilingualPlane($(this).val())){count+=alertFirstCharacterOutsideBasicMultilingualPlane($(this).val(),$(this).attr('data_info'))}});$('textarea.checkbytes').each(function(_n){if(getUTF8Length($(this).val().trim())>(0+$(this).attr('data_maxlength'))){alert('ERROR\n\nText is too long in field "'+$(this).attr('data_info')+'", please make it shorter! (Maximum length: '+$(this).attr('data_maxlength')+' bytes.)');count++}});$('input.noblanksnocomma').each(function(_n){if($(this).val().indexOf(' ')>0||$(this).val().indexOf(',')>0){alert('ERROR\n\nNo spaces or commas allowed in field "'+$(this).attr('data_info')+'", please remove!');count++}});return(count==0)} function isInt(value){for(let i=0;i'9')){return!1}} @@ -144,19 +142,19 @@ return!0} function markClick(){if($('input.markcheck:checked').length>0){$('#markaction').removeAttr('disabled')}else{$('#markaction').attr('disabled','disabled')}} function confirmDelete(){return confirm('CONFIRM\n\nAre you sure you want to delete?')} function showAllwordsClick(){const showAll=$('#showallwords').prop('checked')?'1':'0';const showLeaning=$('#showlearningtranslations').prop('checked')?'1':'0';const text=$('#thetextid').text();setTimeout(function(){showRightFrames('set_text_mode.php?mode='+showAll+'&showLearning='+showLeaning+'&text='+text)},500);setTimeout(function(){window.location.reload()},4000)} -function textareaKeydown(event){if(event.keyCode&&event.keyCode=='13'){if(check()) -$('input:submit').last().trigger('click');return!1}else{return!0}} +function textareaKeydown(event){if(event.keyCode&&event.keyCode=='13'){if(check()){$('input:submit').last().trigger('click')} +return!1}else{return!0}} function noShowAfter3Secs(){$('#hide3').slideUp()} function setTheFocus(){$('.setfocus').trigger('focus').trigger('select')} function word_click_event_do_test_test(){run_overlib_test(LWT_DATA.language.dict_link1,LWT_DATA.language.dict_link2,LWT_DATA.language.translator_link,$(this).attr('data_wid'),$(this).attr('data_text'),$(this).attr('data_trans'),$(this).attr('data_rom'),$(this).attr('data_status'),$(this).attr('data_sent'),$(this).attr('data_todo'));$('.todo').text(LWT_DATA.test.solution);return!1} function keydown_event_do_test_test(e){if((e.key=='Space'||e.which==32)&&!LWT_DATA.test.answer_opened){$('.word').trigger('click');cleanupRightFrames();showRightFrames('show_word.php?wid='+$('.word').attr('data_wid')+'&ann=');LWT_DATA.test.answer_opened=!0;return!1} -if(e.key=="Escape"||e.which==27){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status='+$('.word').attr('data_status'));return!1} -if(e.key=="I"||e.which==73){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status=98');return!1} -if(e.key=="W"||e.which==87){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status=99');return!1} -if(e.key=="E"||e.which==69){showRightFrames('edit_tword.php?wid='+LWT_DATA.word.id);return!1} -if(!LWT_DATA.test.answer_opened) -return!0;if(e.key=="ArrowUp"||e.which==38){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&stchange=1');return!1} -if(e.key=="ArrowDown"||e.which==40){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&stchange=-1');return!1} +if(e.key=='Escape'||e.which==27){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status='+$('.word').attr('data_status'));return!1} +if(e.key=='I'||e.which==73){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status=98');return!1} +if(e.key=='W'||e.which==87){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status=99');return!1} +if(e.key=='E'||e.which==69){showRightFrames('edit_tword.php?wid='+LWT_DATA.word.id);return!1} +if(!LWT_DATA.test.answer_opened){return!0} +if(e.key=='ArrowUp'||e.which==38){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&stchange=1');return!1} +if(e.key=='ArrowDown'||e.which==40){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&stchange=-1');return!1} for(let i=0;i<5;i++){if(e.which==(49+i)||e.which==(97+i)){showRightFrames('set_test_status.php?wid='+LWT_DATA.word.id+'&status='+(i+1));return!1}} return!0} 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()+'

'} @@ -165,20 +163,20 @@ if(trans!=''&&trans!='*'){if($(this).attr('data_ann')){const ann=$(this).attr('d title+='

Transl.: '+trans+'

'} title+='

Status: '+statname+'

';return title}});jQuery.fn.extend({tooltip_wsty_init:function(){$(this).tooltip({position:{my:'left top+10',at:'left bottom',collision:'flipfit'},items:'.hword',show:{easing:'easeOutCirc'},content:function(){return $(this).tooltip_wsty_content()}})}});function get_position_from_id(id_string){if((typeof id_string)==='undefined')return-1;const arr=id_string.split('-');return parseInt(arr[1])*10+10-parseInt(arr[2])} function do_ajax_save_setting(k,v){$.post('api.php/v1/settings',{key:k,value:v})} -function quick_select_to_input(select_elem,input_elem){let val=select_elem.options[select_elem.selectedIndex].value;if(val!='') -input_elem.value=val;select_elem.value=''} -function select_media_path(paths,folders,base_path){let options=[],temp_option=document.createElement('option');temp_option.value="";temp_option.text="[Choose...]";options.push(temp_option);for(let i=0;ichange_example_sentences_zone(data,ctl))}else{let query={lg_id:lang,word_lc:word};if(parseInt(woid,10)==-1){query.advanced_search=!0} +function change_example_sentences_zone(sentences,ctl){$('#exsent-waiting').css('display','none');$('#exsent-sentences').css('display','inherit');const new_element=display_example_sentences(sentences,ctl);$('#exsent-sentences').append(new_element)} +function do_ajax_show_sentences(lang,word,ctl,woid){$('#exsent-interactable').css('display','none');$('#exsent-waiting').css('display','inherit');if(isInt(woid)&&woid!=-1){$.getJSON('api.php/v1/sentences-with-term/'+woid,{lg_id:lang,word_lc:word},(data)=>change_example_sentences_zone(data,ctl))}else{const query={lg_id:lang,word_lc:word};if(parseInt(woid,10)==-1){query.advanced_search=!0} $.getJSON('api.php/v1/sentences-with-term',query,(data)=>change_example_sentences_zone(data,ctl))}} function do_ajax_req_sim_terms(lg_id,word_text){return $.getJSON('api.php/v1/similar-terms',{"lg_id":lg_id,"term":word_text})} function do_ajax_show_similar_terms(){$('#simwords').html('');do_ajax_req_sim_terms(parseInt($('#langfield').val(),10),$('#wordfield').val()).done(function(data){$('#simwords').html(data.similar_terms)}).fail(function(data){console.log(data)})} @@ -186,28 +184,28 @@ function do_ajax_word_counts(){const t=$('.markcheck').map(function(){return $(t function set_barchart_item(){const id=$(this).find('span').first().attr('id').split('_')[2];let v;if(SUW&16){v=parseInt(WORDCOUNTS.expru[id]||0,10)+parseInt(WORDCOUNTS.totalu[id],10)}else{v=parseInt(WORDCOUNTS.expr[id]||0,10)+parseInt(WORDCOUNTS.total[id],10)} $(this).children('li').each(function(){let cat_word_count=parseInt($(this).children('span').text(),10);cat_word_count+=1;v+=1;const h=25-Math.log(cat_word_count)/Math.log(v)*25;$(this).css('border-top-width',h+'px')})} function set_word_counts(){$.each(WORDCOUNTS.totalu,function(key,value){let knownu,known,todo,stat0;knownu=known=todo=stat0=0;const expr=WORDCOUNTS.expru[key]?parseInt((SUW&2)?WORDCOUNTS.expru[key]:WORDCOUNTS.expr[key]):0;if(!WORDCOUNTS.stat[key]){WORDCOUNTS.statu[key]=WORDCOUNTS.stat[key]=[]} -$('#total_'+key).html((SUW&1?value:WORDCOUNTS.total[key]));$.each(WORDCOUNTS.statu[key],function(k,v){if(SUW&8) -$('#stat_'+k+'_'+key).html(v);knownu+=parseInt(v)});$.each(WORDCOUNTS.stat[key],function(k,v){if(!(SUW&8)) -$('#stat_'+k+'_'+key).html(v);known+=parseInt(v)});$('#saved_'+key).html(known?((SUW&2?knownu:known)-expr+'+'+expr):0);if(SUW&4){todo=parseInt(value)+parseInt(WORDCOUNTS.expru[key]||0)-parseInt(knownu)}else{todo=parseInt(WORDCOUNTS.total[key])+parseInt(WORDCOUNTS.expr[key]||0)-parseInt(known)} +$('#total_'+key).html((SUW&1?value:WORDCOUNTS.total[key]));$.each(WORDCOUNTS.statu[key],function(k,v){if(SUW&8){$('#stat_'+k+'_'+key).html(v)} +knownu+=parseInt(v)});$.each(WORDCOUNTS.stat[key],function(k,v){if(!(SUW&8)){$('#stat_'+k+'_'+key).html(v)} +known+=parseInt(v)});$('#saved_'+key).html(known?((SUW&2?knownu:known)-expr+'+'+expr):0);if(SUW&4){todo=parseInt(value)+parseInt(WORDCOUNTS.expru[key]||0)-parseInt(knownu)}else{todo=parseInt(WORDCOUNTS.total[key])+parseInt(WORDCOUNTS.expr[key]||0)-parseInt(known)} $('#todo_'+key).html(todo);if(SUW&8){unknowncount=parseInt(value)+parseInt(WORDCOUNTS.expru[key]||0)-parseInt(knownu);unknownpercent=Math.round(unknowncount*10000/(knownu+unknowncount))/100}else{unknowncount=parseInt(WORDCOUNTS.total[key])+parseInt(WORDCOUNTS.expr[key]||0)-parseInt(known);unknownpercent=Math.round(unknowncount*10000/(known+unknowncount))/100} $('#unknownpercent_'+key).html(unknownpercent==0?0:unknownpercent.toFixed(2));if(SUW&16){stat0=parseInt(value)+parseInt(WORDCOUNTS.expru[key]||0)-parseInt(knownu)}else{stat0=parseInt(WORDCOUNTS.total[key])+parseInt(WORDCOUNTS.expr[key]||0)-parseInt(known)} $('#stat_0_'+key).html(stat0)});$('.barchart').each(set_barchart_item)} function word_count_click(){$('.wc_cont').children().each(function(){if(parseInt($(this).attr('data_wo_cnt'))==1){$(this).html('u')}else{$(this).html('t')} SUW=(parseInt($('#chart').attr('data_wo_cnt'))<<4)+(parseInt($('#unknownpercent').attr('data_wo_cnt'))<<3)+(parseInt($('#unknown').attr('data_wo_cnt'))<<2)+(parseInt($('#saved').attr('data_wo_cnt'))<<1)+(parseInt($('#total').attr('data_wo_cnt')));set_word_counts()})} -function translation_radio(curr_trans,trans_data){if(trans_data.wid===null){return""} -const trim_trans=curr_trans.trim();if(trim_trans=='*'||trim_trans==''){return""} +function translation_radio(curr_trans,trans_data){if(trans_data.wid===null){return''} +const trim_trans=curr_trans.trim();if(trim_trans=='*'||trim_trans==''){return''} const set=trim_trans==trans_data.trans;const option=` - +   `+escape_html_chars(trim_trans)+`
`;return option} -function edit_term_ann_translations(trans_data,text_id){const widset=trans_data.wid!==null;let edit_word_link;if(widset){const req_arg=$.param({fromAnn:"$(document).scrollTop()",wid:trans_data.wid,ord:trans_data.term_ord,tid:text_id}) +function edit_term_ann_translations(trans_data,text_id){const widset=trans_data.wid!==null;let edit_word_link;if(widset){const req_arg=$.param({fromAnn:'$(document).scrollTop()',wid:trans_data.wid,ord:trans_data.term_ord,tid:text_id}) edit_word_link=` Edit Term `}else{edit_word_link=' '} -$(`#editlink${trans_data.ann_index}`).html(edit_word_link);let translations_list="";trans_data.translations.forEach(function(candidate_trans){translations_list+=translation_radio(candidate_trans,trans_data)});const select_last=trans_data.translations.length==0;translations_list+=` +$(`#editlink${trans_data.ann_index}`).html(edit_word_link);let translations_list='';trans_data.translations.forEach(function(candidate_trans){translations_list+=translation_radio(candidate_trans,trans_data)});const select_last=trans_data.translations.length==0;translations_list+=`   @@ -232,7 +230,7 @@ translations_list+=`   `;$(`#transsel${trans_data.ann_index}`).html(translations_list)} function do_ajax_edit_impr_text(pagepos,word,term_id){if(word==''){$('#editimprtextdata').html('');location.reload();return} -const textid=$('#editimprtextdata').attr('data_id');$.getJSON('api.php/v1/terms/'+term_id+'/translations',{text_id:textid,term_lc:word},function(data){if("error" in data){alert(data.error)}else{edit_term_ann_translations(data,textid);$.scrollTo(pagepos);$('input.impr-ann-text').on('change',changeImprAnnText);$('input.impr-ann-radio').on('change',changeImprAnnRadio)}})} +const textid=$('#editimprtextdata').attr('data_id');$.getJSON('api.php/v1/terms/'+term_id+'/translations',{text_id:textid,term_lc:word},function(data){if('error' in data){alert(data.error)}else{edit_term_ann_translations(data,textid);$.scrollTo(pagepos);$('input.impr-ann-text').on('change',changeImprAnnText);$('input.impr-ann-radio').on('change',changeImprAnnRadio)}})} function showRightFrames(roUrl,ruUrl){if(roUrl!==undefined){top.frames.ro.location.href=roUrl} if(ruUrl!==undefined){top.frames.ru.location.href=ruUrl} if($('#frames-r').length){$('#frames-r').animate({right:'5px'});return!0} @@ -243,14 +241,14 @@ function cleanupRightFrames(){const mytimeout=function(){const rf=window.parent. window.parent.setTimeout(mytimeout,800);window.parent.document.getElementById('frame-l').focus();window.parent.setTimeout(window.parent.cClick,100)} function successSound(){document.getElementById('success_sound').pause();document.getElementById('failure_sound').pause();return document.getElementById('success_sound').play()} function failureSound(){document.getElementById('success_sound').pause();document.getElementById('failure_sound').pause();return document.getElementById('failure_sound').play()} -const lwt={prepare_word_count_click:function(){$('#total,#saved,#unknown,#chart,#unknownpercent').on('click',function(event){$(this).attr('data_wo_cnt',parseInt($(this).attr('data_wo_cnt'))^1);word_count_click();event.stopImmediatePropagation()}).attr('title',"u: Unique Word Counts\nt: Total Word Counts");do_ajax_word_counts()},save_text_word_count_settings:function(){if(SUW==SHOWUNIQUE){return} +const lwt={prepare_word_count_click:function(){$('#total,#saved,#unknown,#chart,#unknownpercent').on('click',function(event){$(this).attr('data_wo_cnt',parseInt($(this).attr('data_wo_cnt'))^1);word_count_click();event.stopImmediatePropagation()}).attr('title','u: Unique Word Counts\nt: Total Word Counts');do_ajax_word_counts()},save_text_word_count_settings:function(){if(SUW==SHOWUNIQUE){return} const a=$('#total').attr('data_wo_cnt')+$('#saved').attr('data_wo_cnt')+$('#unknown').attr('data_wo_cnt')+$('#unknownpercent').attr('data_wo_cnt')+$('#chart').attr('data_wo_cnt');do_ajax_save_setting('set-show-text-word-counts',a)}} $.fn.serializeObject=function(){const o={};const a=this.serializeArray();$.each(a,function(){if(o[this.name]!==undefined){if(!o[this.name].push){o[this.name]=[o[this.name]]} o[this.name].push(this.value||'')}else{o[this.name]=this.value||''}});return o};function wrapRadioButtons(){$(':input,.wrap_checkbox span,.wrap_radio span,a:not([name^=rec]),select,'+'#mediaselect span.click,#forwbutt,#backbutt').each(function(i){$(this).attr('tabindex',i+1)});$('.wrap_radio span').on('keydown',function(e){if(e.keyCode==32){$(this).parent().parent().find('input[type=radio]').trigger('click');return!1}})} function prepareMainAreas(){$('.edit_area').editable('inline_edit.php',{type:'textarea',indicator:'',tooltip:'Click to edit...',submit:'Save',cancel:'Cancel',rows:3,cols:35});$('select').wrap("");$('form').attr('autocomplete','off');$('input[type="file"]').each(function(){if(!$(this).is(':visible')){$(this).before('').after('').on('change',function(){let txt=this.value.replace('C:\\fakepath\\','');if(txt.length>85)txt=txt.replace(/.*(.{80})$/,' ... $1');$(this).next().text(txt)}).on('onmouseout',function(){let txt=this.value.replace('C:\\fakepath\\','');if(txt.length>85)txt=txt.replace(/.*(.{80})$/,' ... $1');$(this).next().text(txt)})}});$('input[type="checkbox"]').each(function(z){if(typeof z==='undefined')z=1;if(typeof $(this).attr('id')==='undefined'){$(this).attr('id','cb_'+z++)} $(this).after('')});$('span[class*="tts_"]').on('click',function(){const lg=$(this).attr('class').replace(/.*tts_([a-zA-Z-]+).*/,'$1');const txt=$(this).text();readRawTextAloud(txt,lg)});$(document).on('mouseup',function(){$('button,input[type=button],.wrap_radio span,.wrap_checkbox span').trigger('blur')});$('.wrap_checkbox span').on('keydown',function(e){if(e.keyCode==32){$(this).parent().parent().find('input[type=checkbox]').trigger('click');return!1}});$('input[type="radio"]').each(function(z){if(z===undefined){z=1} if(typeof $(this).attr('id')==='undefined'){$(this).attr('id','rb_'+z++)} -$(this).after('')});$('.button-file').on('click',function(){$(this).next('input[type="file"]').trigger('click');return!1});$('input.impr-ann-text').on('change',changeImprAnnText);$('input.impr-ann-radio').on('change',changeImprAnnRadio);$('form.validate').on('submit',check);$('input.markcheck').on('click',markClick);$('.confirmdelete').on('click',confirmDelete);$('textarea.textarea-noreturn').on('keydown',textareaKeydown);$('#frames-r').resizable({handles:"w",stop:function(_event,ui){$('#frames-l').css('width',ui.position.left-20);do_ajax_save_setting('set-text-l-framewidth-percent',Math.round($('#frames-l').width()/$(window).width()*100))}});$('#termtags').tagit({beforeTagAdded:function(_event,ui){return!containsCharacterOutsideBasicMultilingualPlane(ui.tag.text())},availableTags:TAGS,fieldName:'TermTags[TagList][]'});$('#texttags').tagit({beforeTagAdded:function(_event,ui){return!containsCharacterOutsideBasicMultilingualPlane(ui.tag.text())},availableTags:TEXTTAGS,fieldName:'TextTags[TagList][]'});markClick();setTheFocus();if($('#simwords').length>0&&$('#langfield').length>0&&$('#wordfield').length>0){$('#wordfield').on('blur',do_ajax_show_similar_terms);do_ajax_show_similar_terms()} +$(this).after('')});$('.button-file').on('click',function(){$(this).next('input[type="file"]').trigger('click');return!1});$('input.impr-ann-text').on('change',changeImprAnnText);$('input.impr-ann-radio').on('change',changeImprAnnRadio);$('form.validate').on('submit',check);$('input.markcheck').on('click',markClick);$('.confirmdelete').on('click',confirmDelete);$('textarea.textarea-noreturn').on('keydown',textareaKeydown);$('#frames-r').resizable({handles:'w',stop:function(_event,ui){$('#frames-l').css('width',ui.position.left-20);do_ajax_save_setting('set-text-l-framewidth-percent',Math.round($('#frames-l').width()/$(window).width()*100))}});$('#termtags').tagit({beforeTagAdded:function(_event,ui){return!containsCharacterOutsideBasicMultilingualPlane(ui.tag.text())},availableTags:TAGS,fieldName:'TermTags[TagList][]'});$('#texttags').tagit({beforeTagAdded:function(_event,ui){return!containsCharacterOutsideBasicMultilingualPlane(ui.tag.text())},availableTags:TEXTTAGS,fieldName:'TextTags[TagList][]'});markClick();setTheFocus();if($('#simwords').length>0&&$('#langfield').length>0&&$('#wordfield').length>0){$('#wordfield').on('blur',do_ajax_show_similar_terms);do_ajax_show_similar_terms()} window.setTimeout(noShowAfter3Secs,3000)} $(window).on('load',wrapRadioButtons);$(document).ready(prepareMainAreas);/** * LWT Javascript functions @@ -295,19 +293,19 @@ function make_overlib_link_wellknown_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","speechDispatcher('"+escape_html_chars(txt)+"', '"+(lang||"")+"')");return img.outerHTML};/** * LWT Javascript functions - * + * * @author andreask7 * @license Unlicense * @since 1.6.16-fork - * + * * "Learning with Texts" (LWT) is free and unencumbered software * released into the PUBLIC DOMAIN. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a * compiled binary, for any purpose, commercial or non-commercial, * and by any means. - * + * * In jurisdictions that recognize copyright laws, the author or * authors of this software dedicate any and all copyright * interest in the software to the public domain. We make this @@ -316,7 +314,7 @@ function make_overlib_audio(txt,lang){let img=document.createElement("img");img. * dedication to be an overt act of relinquishment in perpetuity * of all present and future rights to this software under * copyright law. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE @@ -325,7 +323,7 @@ function make_overlib_audio(txt,lang){let img=document.createElement("img");img. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - * + * * For more information, please refer to [http://unlicense.org/]. */ function getStatusName(status){return STATUSES[status]?STATUSES[status].name:'Unknown'} @@ -337,9 +335,9 @@ function translateWord2(url,wordctl){if(wordctl!==undefined&&url!=''){const text function translateWord3(url,word){owin(createTheDictUrl(url,word))} function getLangFromDict(wblink3){let dictUrl,urlParams;if(wblink3.trim()==''){return''} if(wblink3.startsWith('*')){wblink3=wblink3.substring(1)} -if(wblink3.startsWith("trans.php")||wblink3.startsWith("ggl.php")){wblink3='http://'+wblink3} -dictUrl=new URL(wblink3);urlParams=dictUrl.searchParams;if(urlParams.get("lwt_translator")=="libretranslate"){return urlParams.get("source")||""} -return urlParams.get("sl")||""} +if(wblink3.startsWith('trans.php')||wblink3.startsWith('ggl.php')){wblink3='http://'+wblink3} +dictUrl=new URL(wblink3);urlParams=dictUrl.searchParams;if(urlParams.get('lwt_translator')=='libretranslate'){return urlParams.get('source')||''} +return urlParams.get('sl')||''} function make_tooltip(word,trans,roman,status){const nl='\x0d';let title=word;if(roman!=''){if(title!='')title+=nl;title+='▶ '+roman} if(trans!=''&&trans!='*'){if(title!='')title+=nl;title+='▶ '+trans} if(title!='')title+=nl;title+='▶ '+getStatusName(status)+' ['+getStatusAbbr(status)+']';return title} @@ -349,20 +347,20 @@ function owin(url){window.open(url,'dictwin','width=800, height=400, scrollbars= function oewin(url){window.open(url,'editwin','width=800, height=600, scrollbars=yes, menubar=no, resizable=yes, status=no')} function createTheDictUrl(u,w){const url=u.trim();const trm=w.trim();const term_elem=url.match(/lwt_term|###/);const pos=(term_elem===null)?-1:url.indexOf(term_elem[0]);if(pos==-1){return url+encodeURIComponent(trm)} const pos2=url.indexOf('###',pos+1);if(pos2===-1){return url.replace(term_elem,trm==''?'+':encodeURIComponent(trm))} -const enc=url.substring(pos+term_elem[0].length,pos2-pos-term_elem[0].length).trim();console.warn("Trying to use encoding '"+enc+"'. This feature is abandonned since "+"2.6.0-fork. Using default UTF-8.");let output=url.substring(0,pos)+encodeURIComponent(trm);if(pos2+3'+txt+' '}else{r=' '+txtbefore+' '+txt+' '} return r} -function createSentLookupLink(torder,txid,url,txt){url=url.trim();txt=txt.trim();let r='';let popup=!1;let external=!1;const target_url='trans.php?x=1&i='+torder+'&t='+txid;if(url==''||txt==''){return r} +function createSentLookupLink(torder,txid,url,txt){url=url.trim();txt=txt.trim();const r='';let popup=!1;let external=!1;const target_url='trans.php?x=1&i='+torder+'&t='+txid;if(url==''||txt==''){return r} if(url.startsWith('*')){url=url.substring(1);popup=!0} -try{let final_url=new URL(url);popup=popup||final_url.searchParams.has('lwt_popup');external=!0}catch(err){if(!(err instanceof TypeError)){throw err}} +try{const final_url=new URL(url);popup=popup||final_url.searchParams.has('lwt_popup');external=!0}catch(err){if(!(err instanceof TypeError)){throw err}} if(popup){return' '+txt+' '} if(external){return' '+txt+' '} return r} -function escape_html_chars(s){let map={'&':'&','<':'<','>':'>','"':'"',"'":''',"\x0d":'
'};return s.replace(/[&<>"'\x0d]/g,function(m){return map[m]})} +function escape_html_chars(s){const map={'&':'&','<':'<','>':'>','"':'"',"'":''','\x0d':'
'};return s.replace(/[&<>"'\x0d]/g,function(m){return map[m]})} function escape_apostrophes(s){return s.replace(/'/g,'\\\'')} function selectToggle(toggle,form){const myForm=document.forms[form];for(let i=0;i0&&p.match(re);if(!r){alert('Table Set Name (= Table Prefix) must'+'\ncontain 1 to 20 characters (only 0-9, a-z, A-Z and _).'+'\nPlease correct your input.')} return r};/** * Standard JS interface to get translations - * + * * @author andreask7 * @license Unlicense * @since 1.6.16-fork */ -function deleteTranslation(){let frame=window.parent.frames.ro;if(frame===undefined) -frame=window.opener;if($('[name="WoTranslation"]',frame.document).val().trim().length){$('[name="WoTranslation"]',frame.document).val('');frame.lwt_form_check.makeDirty()}} -function addTranslation(s){let frame=window.parent.frames.ro;if(frame===undefined) -frame=window.opener;if(frame===undefined){alert('Translation can not be copied!');return} -let word_trans=frame.document.forms[0].WoTranslation;if(typeof word_trans!='object'){alert('Translation can not be copied!');return} -let oldValue=word_trans.value;if(oldValue.trim()==''){word_trans.value=s;frame.lwt_form_check.makeDirty()}else{if(oldValue.indexOf(s)==-1){word_trans.value=oldValue+' / '+s;frame.lwt_form_check.makeDirty()}else{if(confirm('"'+s+'" seems already to exist as a translation.\n'+'Insert anyway?')){word_trans.value=oldValue+' / '+s;frame.lwt_form_check.makeDirty()}}}} -function getGlosbeTranslation(text,lang,dest){$.ajax({url:'http://glosbe.com/gapi/translate?'+$.param({from:lang,dest:dest,format:"json",phrase:text,callback:"?"}),type:"GET",dataType:'jsonp',jsonp:'getTranslationFromGlosbeApi',jsonpCallback:'getTranslationFromGlosbeApi',async:'true'})} -function getTranslationFromGlosbeApi(data){try{$.each(data.tuc,function(i,rows){if(rows.phrase){$('#translations').append(''+'Copy'+'   '+rows.phrase.text+'
')}else if(rows.meanings){$('#translations').append(''+'Copy'+'   '+"("+rows.meanings[0].text+")"+'
')}});if(!data.tuc.length){$('#translations').before('

No translations found ('+data.from+'-'+data.dest+').

');if(data.dest!='en'&&data.from!='en'){$('#translations').attr('id','no_trans').after('

 

Glosbe Dictionary ('+data.from+'-en):   '+data.phrase+'

 

');getGlosbeTranslation(data.phrase,data.from,'en')}else $('#translations').after('
')}else $('#translations').after('

 
'+data.tuc.length+' translation'+(data.tuc.length==1?'':'s')+' retrieved via '+'Glosbe API.


')}catch(err){$('#translations').text('Retrieval error. Possible reason: There is a limit of Glosbe API '+'calls that may be done from one IP address in a fixed period of time,'+' to prevent from abuse.').after('
')}} -async function getLibreTranslateTranslationBase(text,lang,dest,key="",url="http://localhost:5000/translate"){const res=await fetch(url,{method:"POST",body:JSON.stringify({q:text,source:lang,target:dest,format:"text",api_key:key}),headers:{"Content-Type":"application/json"}});const data=await res.json();return data.translatedText} -async function getLibreTranslateTranslation(libre_url,text,lang,dest){const search_params=libre_url.searchParams;if(search_params.get("lwt_translator")!="libretranslate"){throw 'Translation API not supported: '+search_params.get("lwt_translator")+"!"} -let translator_ajax;if(search_params.get("lwt_translator_ajax")){translator_ajax=decodeURIComponent(search_params.get("lwt_translator_ajax"))}else{translator_ajax=libre_url.toString().replace(libre_url.search,'')+"translate"} -return getLibreTranslateTranslationBase(text,lang,dest,key=search_params.get("lwt_key"),translator_ajax)};/** +function deleteTranslation(){let frame=window.parent.frames.ro;if(frame===undefined){frame=window.opener} +if($('[name="WoTranslation"]',frame.document).val().trim().length){$('[name="WoTranslation"]',frame.document).val('');frame.lwt_form_check.makeDirty()}} +function addTranslation(s){let frame=window.parent.frames.ro;if(frame===undefined){frame=window.opener} +if(frame===undefined){alert('Translation can not be copied!');return} +const word_trans=frame.document.forms[0].WoTranslation;if(typeof word_trans!=='object'){alert('Translation can not be copied!');return} +const oldValue=word_trans.value;if(oldValue.trim()==''){word_trans.value=s;frame.lwt_form_check.makeDirty()}else{if(oldValue.indexOf(s)==-1){word_trans.value=oldValue+' / '+s;frame.lwt_form_check.makeDirty()}else{if(confirm('"'+s+'" seems already to exist as a translation.\n'+'Insert anyway?')){word_trans.value=oldValue+' / '+s;frame.lwt_form_check.makeDirty()}}}} +function getGlosbeTranslation(text,lang,dest){$.ajax({url:'http://glosbe.com/gapi/translate?'+$.param({from:lang,dest:dest,format:'json',phrase:text,callback:'?'}),type:'GET',dataType:'jsonp',jsonp:'getTranslationFromGlosbeApi',jsonpCallback:'getTranslationFromGlosbeApi',async:'true'})} +function getTranslationFromGlosbeApi(data){try{$.each(data.tuc,function(i,rows){if(rows.phrase){$('#translations').append(''+'Copy'+'   '+rows.phrase.text+'
')}else if(rows.meanings){$('#translations').append(''+'Copy'+'   '+'('+rows.meanings[0].text+')'+'
')}});if(!data.tuc.length){$('#translations').before('

No translations found ('+data.from+'-'+data.dest+').

');if(data.dest!='en'&&data.from!='en'){$('#translations').attr('id','no_trans').after('

 

Glosbe Dictionary ('+data.from+'-en):   '+data.phrase+'

 

');getGlosbeTranslation(data.phrase,data.from,'en')}else $('#translations').after('
')}else{$('#translations').after('

 
'+data.tuc.length+' translation'+(data.tuc.length==1?'':'s')+' retrieved via '+'Glosbe API.


')}}catch(err){$('#translations').text('Retrieval error. Possible reason: There is a limit of Glosbe API '+'calls that may be done from one IP address in a fixed period of time,'+' to prevent from abuse.').after('
')}} +async function getLibreTranslateTranslationBase(text,lang,dest,key='',url='http://localhost:5000/translate'){const res=await fetch(url,{method:'POST',body:JSON.stringify({q:text,source:lang,target:dest,format:'text',api_key:key}),headers:{'Content-Type':'application/json'}});const data=await res.json();return data.translatedText} +async function getLibreTranslateTranslation(libre_url,text,lang,dest){const search_params=libre_url.searchParams;if(search_params.get('lwt_translator')!='libretranslate'){throw 'Translation API not supported: '+search_params.get('lwt_translator')+'!'} +let translator_ajax;if(search_params.get('lwt_translator_ajax')){translator_ajax=decodeURIComponent(search_params.get('lwt_translator_ajax'))}else{translator_ajax=libre_url.toString().replace(libre_url.search,'')+'translate'} +return getLibreTranslateTranslationBase(text,lang,dest,key=search_params.get('lwt_key'),translator_ajax)};/** * Check for unsaved changes when unloading window. - * + * * @license unlicense * @author andreask7 * @since 1.6.16-fork @@ -450,36 +448,36 @@ if(!Array.forEach){Array.forEach=function(array,block,context){for(var i=0;i * @license Unlicense * @since 2.0.3-fork */ -function quickMenuRedirection(value){const qm=document.getElementById('quickmenu');qm.selectedIndex=0;if(value=='') -return;if(value=='INFO'){top.location.href='docs/info.html'}else if(value=='rss_import'){top.location.href='do_feeds.php?check_autoupdate=1'}else{top.location.href=value+'.php'}} -function newExpressionInteractable(text,attrs,length,hex,showallwords){const context=window.parent.document;for(key in text){$('#ID-'+key+'-'+length,context).remove();let next_term_key='';for(let j=length-1;j>0;j--){if(j==1) -next_term_key='#ID-'+key+'-1';if($('#ID-'+key+'-'+j,context).length){next_term_key='#ID-'+key+'-'+j;break}} -$(next_term_key,context).before(''+text[key]+'');const multi_word=$('#ID-'+key+'-'+length,context);multi_word.addClass('order'+key).attr('data_order',key);const txt=multi_word.nextUntil($('#ID-'+(parseInt(key)+length*2-1)+'-1',context),'[id$="-1"]').map(function(){return $(this).text()}).get().join("");const pos=$('#ID-'+key+'-1',context).attr('data_pos');multi_word.attr('data_text',txt).attr('data_pos',pos);if(showallwords){return} +function quickMenuRedirection(value){const qm=document.getElementById('quickmenu');qm.selectedIndex=0;if(value==''){return} +if(value=='INFO'){top.location.href='docs/info.html'}else if(value=='rss_import'){top.location.href='do_feeds.php?check_autoupdate=1'}else{top.location.href=value+'.php'}} +function newExpressionInteractable(text,attrs,length,hex,showallwords){const context=window.parent.document;for(key in text){$('#ID-'+key+'-'+length,context).remove();let next_term_key='';for(let j=length-1;j>0;j--){if(j==1){next_term_key='#ID-'+key+'-1'} +if($('#ID-'+key+'-'+j,context).length){next_term_key='#ID-'+key+'-'+j;break}} +$(next_term_key,context).before(''+text[key]+'');const multi_word=$('#ID-'+key+'-'+length,context);multi_word.addClass('order'+key).attr('data_order',key);const txt=multi_word.nextUntil($('#ID-'+(parseInt(key)+length*2-1)+'-1',context),'[id$="-1"]').map(function(){return $(this).text()}).get().join('');const pos=$('#ID-'+key+'-1',context).attr('data_pos');multi_word.attr('data_text',txt).attr('data_pos',pos);if(showallwords){return} const next_words=[];for(let i=0;i0){let posObj=$(".wsty[data_pos="+lookPos+"]").not(".hide").eq(0);if(posObj.attr("data_pos")===undefined){pos=$(".wsty").not(".hide").filter(function(){return $(this).attr("data_pos")<=lookPos}).eq(-1)}} +function goToLastPosition(){const lookPos=LWT_DATA.text.reading_position;let pos=0;if(lookPos>0){const posObj=$('.wsty[data_pos='+lookPos+']').not('.hide').eq(0);if(posObj.attr('data_pos')===undefined){pos=$('.wsty').not('.hide').filter(function(){return $(this).attr('data_pos')<=lookPos}).eq(-1)}} $(document).scrollTo(pos);focus();setTimeout(overlib,10);setTimeout(cClick,100)} function saveReadingPosition(text_id,position){$.post('api.php/v1/texts/'+text_id+'/reading-position',{position:position})} function saveAudioPosition(text_id,pos){$.post('api.php/v1/texts/'+text_id+'/audio-position',{position:pos})} -function getPhoneticText(text,lang){let phoneticText;$.ajax('api.php/v1/phonetic-reading',{async:!1,data:{text:text,lang:lang},dataType:"json",type:"GET",}).done(function(data){phoneticText=data.phonetic_reading});return phoneticText} +function getPhoneticText(text,lang){let phoneticText;$.ajax('api.php/v1/phonetic-reading',{async:!1,data:{text:text,lang:lang},dataType:'json',type:'GET'}).done(function(data){phoneticText=data.phonetic_reading});return phoneticText} async function getPhoneticTextAsync(text,lang){return $.getJSON('api.php/v1/phonetic-reading',{text:text,lang:lang})} 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 readTextWithExternal(text,voice_api,lang){let fetchRequest=JSON.parse(voice_api);deepReplace(fetchRequest,'lwt_term',text) +function readTextWithExternal(text,voice_api,lang){const 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 cookieTTSSettings(language){const prefix='tts['+language;let lang_settings={};const num_vals=['Rate','Pitch'];const cookies=['Rate','Pitch','Voice'];let cookie_val;for(let cook in cookies){cookie_val=getCookie(prefix+cook+']');if(cookie_val){if(num_vals.includes(cook)){lang_settings[cook.toLowerCase()]=parseFloat(cookie_val)}else{lang_settings[cook.toLowerCase()]=cookie_val}}} +function cookieTTSSettings(language){const prefix='tts['+language;const lang_settings={};const num_vals=['Rate','Pitch'];const cookies=['Rate','Pitch','Voice'];let cookie_val;for(const cook in cookies){cookie_val=getCookie(prefix+cook+']');if(cookie_val){if(num_vals.includes(cook)){lang_settings[cook.toLowerCase()]=parseFloat(cookie_val)}else{lang_settings[cook.toLowerCase()]=cookie_val}}} return lang_settings} -function readRawTextAloud(text,lang,rate,pitch,voice){let msg=new SpeechSynthesisUtterance();const tts_settings=cookieTTSSettings(lang.substring(0,2));msg.text=text;if(lang){msg.lang=lang} +function readRawTextAloud(text,lang,rate,pitch,voice){const msg=new SpeechSynthesisUtterance();const tts_settings=cookieTTSSettings(lang.substring(0,2));msg.text=text;if(lang){msg.lang=lang} const useVoice=voice||tts_settings.voice;if(useVoice){const voices=window.speechSynthesis.getVoices();for(let i=0;i= 0.5) { - $("#pbvalue").text(val.toFixed(1)).css({'color': '#BBB'}) - .animate({color: '#888'},150,function() {}); - $("#jquery_jplayer_1").jPlayer("playbackRate", val); - } - }, - - clickFaster: function () { - const val = parseFloat($("#pbvalue").text()) + 0.1; - if (val <= 4.0){ - $("#pbvalue").text(val.toFixed(1)).css({'color': '#BBB'}) - .animate({color: '#888'},150,function() {}); - $("#jquery_jplayer_1").jPlayer("playbackRate", val); - } - }, - - setStdSpeed: function () { - $("#playbackrate").val(10); - lwt_audio_controller.setNewPlaybackRate(); - }, - - setSlower: function () { - let val = $("#playbackrate :selected").val(); - if (val > 5) { - val--; - $("#playbackrate").val(val); - lwt_audio_controller.setNewPlaybackRate(); - } - }, - - setFaster: function () { - let val = $("#playbackrate :selected").val(); - if (val < 15) { - val++; - $("#playbackrate").val(val); - lwt_audio_controller.setNewPlaybackRate(); - } - }, + /** + * Change the position of the audio player head. + * + * @param {Number} position New player head + */ + newPosition: function (position) { + $('#jquery_jplayer_1').jPlayer('playHead', position); + }, + + setNewPlayerSeconds: function () { + const newval = $('#backtime :selected').val(); + do_ajax_save_setting('currentplayerseconds', newval); + }, + + setNewPlaybackRate: function () { + const newval = $('#playbackrate :selected').val(); + do_ajax_save_setting('currentplaybackrate', newval); + $('#jquery_jplayer_1').jPlayer('option', 'playbackRate', newval * 0.1); + }, + + setCurrentPlaybackRate: function () { + const val = $('#playbackrate :selected').val(); + $('#jquery_jplayer_1').jPlayer('option', 'playbackRate', val * 0.1); + }, + + clickSingle: function () { + $('#jquery_jplayer_1').off('bind', $.jPlayer.event.ended + '.jp-repeat'); + $('#do-single').addClass('hide'); + $('#do-repeat').removeClass('hide'); + do_ajax_save_setting('currentplayerrepeatmode', '0'); + }, + + clickRepeat: function () { + $('#jquery_jplayer_1') + .on('bind', $.jPlayer.event.ended + '.jp-repeat', function () { + $('#jquery_jplayer_1').jPlayer('play'); + }); + $('#do-repeat').addClass('hide'); + $('#do-single').removeClass('hide'); + do_ajax_save_setting('currentplayerrepeatmode', '1'); + }, + + clickBackward: function () { + const t = parseInt($('#playTime').text(), 10); + const b = parseInt($('#backtime').val(), 10); + let nt = t - b; + let st = 'pause'; + if (nt < 0) { nt = 0; } + if (!$('#jquery_jplayer_1').data().jPlayer.status.paused) { st = 'play'; } + $('#jquery_jplayer_1').jPlayer(st, nt); + }, + + clickForward: function () { + const t = parseInt($('#playTime').text(), 10); + const b = parseInt($('#backtime').val(), 10); + const nt = t + b; + let st = 'pause'; + if (!$('#jquery_jplayer_1').data().jPlayer.status.paused) { st = 'play'; } + $('#jquery_jplayer_1').jPlayer(st, nt); + }, + + clickSlower: function () { + const val = parseFloat($('#pbvalue').text()) - 0.1; + if (val >= 0.5) { + $('#pbvalue').text(val.toFixed(1)).css({ color: '#BBB' }) + .animate({ color: '#888' }, 150, function () {}); + $('#jquery_jplayer_1').jPlayer('playbackRate', val); + } + }, + + clickFaster: function () { + const val = parseFloat($('#pbvalue').text()) + 0.1; + if (val <= 4.0) { + $('#pbvalue').text(val.toFixed(1)).css({ color: '#BBB' }) + .animate({ color: '#888' }, 150, function () {}); + $('#jquery_jplayer_1').jPlayer('playbackRate', val); + } + }, + + setStdSpeed: function () { + $('#playbackrate').val(10); + lwt_audio_controller.setNewPlaybackRate(); + }, + + setSlower: function () { + let val = $('#playbackrate :selected').val(); + if (val > 5) { + val--; + $('#playbackrate').val(val); + lwt_audio_controller.setNewPlaybackRate(); + } + }, + + setFaster: function () { + let val = $('#playbackrate :selected').val(); + if (val < 15) { + val++; + $('#playbackrate').val(val); + lwt_audio_controller.setNewPlaybackRate(); + } + } } /** * Change the position of the audio player head. - * + * * @param {Number} p New player head - * + * * @deprecated Since LWT 2.9.1, use lwt_audio_controller.newPosition */ -function new_pos(p) { - return lwt_audio_controller.newPosition(p); +function new_pos (p) { + return lwt_audio_controller.newPosition(p); } diff --git a/src/js/jq_pgm.js b/src/js/jq_pgm.js index 633c8fac..934088d2 100644 --- a/src/js/jq_pgm.js +++ b/src/js/jq_pgm.js @@ -1,6 +1,6 @@ /** * Interaction between LWT and jQuery - * + * * @license unlicense * @author andreask7 * @since 1.6.16-fork @@ -19,12 +19,12 @@ LWT_DATA = { dict_link2: '', /** Translator URL */ translator_link: '', - + delimiter: '', /** Word parsing strategy, usually regular expression or 'mecab' */ word_parsing: '', - + rtl: false, /** Third-party voice API */ ttsVoiceApi: '' @@ -67,29 +67,28 @@ LWT jQuery functions /** * Set translation and romanization in a form when possible. - * + * * Marj the form as edited if something was changed. - * + * * @param {string} tra Translation * @param {string} rom Romanization */ -function setTransRoman(tra, rom) { +function setTransRoman (tra, rom) { let form_changed = false; - if ($('textarea[name="WoTranslation"]').length == 1) { + if ($('textarea[name="WoTranslation"]').length == 1) { $('textarea[name="WoTranslation"]').val(tra); form_changed |= true; } - if ($('input[name="WoRomanization"]').length == 1) { + if ($('input[name="WoRomanization"]').length == 1) { $('input[name="WoRomanization"]').val(rom); form_changed |= true; } - if (form_changed) - lwt_form_check.makeDirty(); + if (form_changed) { lwt_form_check.makeDirty(); } } /** * Return whether characters are outside the multilingual plane. - * + * * @param {string} s Input string * @returns {boolean} true is some characters are outside the plane */ @@ -99,7 +98,7 @@ function containsCharacterOutsideBasicMultilingualPlane (s) { /** * Alert if characters are outside the multilingual plane. - * + * * @param {string} s Input string * @returns {boolean} true is some characters are outside the plane */ @@ -109,12 +108,12 @@ function alertFirstCharacterOutsideBasicMultilingualPlane (s, info) { } const match = /[\uD800-\uDFFF]/.exec(s); alert( - 'ERROR\n\nText "' + info + '" contains invalid character(s) ' + - '(in the Unicode Supplementary Multilingual Planes, > U+FFFF) like emojis ' + - 'or very rare characters.\n\nFirst invalid character: "' + - s.substring(match.index, match.index + 2) + '" at position ' + - (match.index + 1) + '.\n\n' + - 'More info: https://en.wikipedia.org/wiki/Plane_(Unicode)\n\n' + + 'ERROR\n\nText "' + info + '" contains invalid character(s) ' + + '(in the Unicode Supplementary Multilingual Planes, > U+FFFF) like emojis ' + + 'or very rare characters.\n\nFirst invalid character: "' + + s.substring(match.index, match.index + 2) + '" at position ' + + (match.index + 1) + '.\n\n' + + 'More info: https://en.wikipedia.org/wiki/Plane_(Unicode)\n\n' + 'Please remove this/these character(s) and try again.' ); return 1; @@ -122,7 +121,7 @@ function alertFirstCharacterOutsideBasicMultilingualPlane (s, info) { /** * Return the memory size of an UTF8 string. - * + * * @param {string} s String to evaluate * @returns {number} Size in bytes */ @@ -132,7 +131,7 @@ function getUTF8Length (s) { /** * Force the user scrolling to an anchor. - * + * * @param {string} aid Anchor ID */ function scrollToAnchor (aid) { @@ -141,13 +140,13 @@ function scrollToAnchor (aid) { /** * Set an existing translation as annotation for a term. - * - * @param {int} textid Text ID - * @param {string} elem_name Name of the element of which to change annotation (e. g.: "rg1") - * @param {Object} form_data All the data from the form + * + * @param {int} textid Text ID + * @param {string} elem_name Name of the element of which to change annotation (e. g.: "rg1") + * @param {Object} form_data All the data from the form * (e. g. {"rg0": "foo", "rg1": "bar"}) */ -function do_ajax_save_impr_text(textid, elem_name, form_data) { +function do_ajax_save_impr_text (textid, elem_name, form_data) { const idwait = '#wait' + elem_name.substring(2); $(idwait).html(''); // elem: "rg2", form_data: {"rg2": "translation"} @@ -159,13 +158,14 @@ function do_ajax_save_impr_text(textid, elem_name, form_data) { }, function (data) { $(idwait).html(''); - if ("error" in data) + if ('error' in data) { alert( 'Saving your changes failed, please reload the page and try again! ' + 'Error message: "' + data.error + '".' ); + } }, - "json" + 'json' ); } @@ -190,25 +190,24 @@ function changeImprAnnRadio () { do_ajax_save_impr_text(textid, elem_name, form_data); } - /** * Update a word translation. - * + * * @param {int} wordid Word ID * @param {string} txid Text HTML ID or unique HTML selector */ -function updateTermTranslation(wordid, txid) { +function updateTermTranslation (wordid, txid) { const translation = $(txid).val().trim(); const pagepos = $(document).scrollTop(); if (translation == '' || translation == '*') { alert('Text Field is empty or = \'*\'!'); return; } - let request = { - translation: translation, + const request = { + translation }; - const failure = "Updating translation of term failed!" + - "Please reload page and try again."; + const failure = 'Updating translation of term failed!' + + 'Please reload page and try again.'; $.post( 'api.php/v1/terms/' + wordid + '/translations', request, @@ -217,36 +216,36 @@ function updateTermTranslation(wordid, txid) { alert(failure); return; } - if ("error" in d) { - alert(failure + "\n" + d.error); + if ('error' in d) { + alert(failure + '\n' + d.error); return; } do_ajax_edit_impr_text(pagepos, d.update, wordid); }, - "json" + 'json' ); } /** * Add (new word) a word translation. - * + * * @param {string} txid Text HTML ID or unique HTML selector * @param {string} word Word text * @param {int} lang Language ID */ -function addTermTranslation(txid, word, lang) { +function addTermTranslation (txid, word, lang) { const translation = $(txid).val().trim(); const pagepos = $(document).scrollTop(); if (translation == '' || translation == '*') { alert('Text Field is empty or = \'*\'!'); return; } - const failure = "Adding translation to term failed!" + - "Please reload page and try again." + const failure = 'Adding translation to term failed!' + + 'Please reload page and try again.' $.post( 'api.php/v1/terms/new', { - translation: translation, + translation, term_text: word, lg_id: lang }, @@ -255,19 +254,19 @@ function addTermTranslation(txid, word, lang) { alert(failure); return; } - if ("error" in d) { - alert(failure + "\n" + d.error); + if ('error' in d) { + alert(failure + '\n' + d.error); return; } do_ajax_edit_impr_text(pagepos, d.add, d.term_id); }, - "json" + 'json' ); } /** * Set a new status for a word in the test table. - * + * * @param {string} wordid Word ID * @param {bool} up true if status sould be increased, false otherwise */ @@ -276,20 +275,20 @@ function changeTableTestStatus (wordid, up) { const wid = parseInt(wordid, 10); $.post( 'api.php/v1/terms/' + wid + '/status/' + status_change, - {}, + {}, function (data) { - if (data == "" || "error" in data) { + if (data == '' || 'error' in data) { return; } $('#STAT' + wordid).html(data.increment); }, - "json" + 'json' ); } /** * Check if there is no problem with the text. - * + * * @returns {boolean} true if all checks were successfull */ function check () { @@ -304,11 +303,11 @@ function check () { count = 0; $('input.checkurl').each(function (_n) { if ($(this).val().trim().length > 0) { - if (($(this).val().trim().indexOf('http://') != 0) && - ($(this).val().trim().indexOf('https://') != 0) && + if (($(this).val().trim().indexOf('http://') != 0) && + ($(this).val().trim().indexOf('https://') != 0) && ($(this).val().trim().indexOf('#') != 0)) { alert( - 'ERROR\n\nField "' + $(this).attr('data_info') + + 'ERROR\n\nField "' + $(this).attr('data_info') + '" must start with "http://" or "https://" if not empty.' ); count++; @@ -323,9 +322,9 @@ function check () { type: 'POST', url: 'inc/ajax.php', data: { - action: "", - action_type: "check_regexp", - regex: regexp + action: '', + action_type: 'check_regexp', + regex: regexp }, async: false } @@ -341,11 +340,11 @@ function check () { // change the following «input[class*="max_int_"]» into «input[class*="maxint_"]» $('input[class*="max_int_"]').each(function (_n) { const maxvalue = parseInt($(this).attr('class') - .replace(/.*maxint_([0-9]+).*/, '$1')); + .replace(/.*maxint_([0-9]+).*/, '$1')); if ($(this).val().trim().length > 0) { if ($(this).val() > maxvalue) { alert( - 'ERROR\n\n Max Value of Field "' + $(this).attr('data_info') + + 'ERROR\n\n Max Value of Field "' + $(this).attr('data_info') + '" is ' + maxvalue ); count++; @@ -368,7 +367,7 @@ function check () { } catch (err) { if (err instanceof TypeError) { alert( - 'ERROR\n\nField "' + $(this).attr('data_info') + + 'ERROR\n\nField "' + $(this).attr('data_info') + '" should be an URL if not empty.' ); count++; @@ -380,7 +379,7 @@ function check () { if ($(this).val().trim().length > 0) { if (!(isInt($(this).val().trim()) && (parseInt($(this).val().trim(), 10) > 0))) { alert( - 'ERROR\n\nField "' + $(this).attr('data_info') + + 'ERROR\n\nField "' + $(this).attr('data_info') + '" must be an integer number > 0.' ); count++; @@ -391,7 +390,7 @@ function check () { if ($(this).val().trim().length > 0) { if (!(isInt($(this).val().trim()) && (parseInt($(this).val().trim(), 10) >= 0))) { alert( - 'ERROR\n\nField "' + $(this).attr('data_info') + + 'ERROR\n\nField "' + $(this).attr('data_info') + '" must be an integer number >= 0.' ); count++; @@ -410,8 +409,8 @@ function check () { $('textarea.checklength').each(function (_n) { if ($(this).val().trim().length > (0 + $(this).attr('data_maxlength'))) { alert( - 'ERROR\n\nText is too long in field "' + $(this).attr('data_info') + - '", please make it shorter! (Maximum length: ' + + 'ERROR\n\nText is too long in field "' + $(this).attr('data_info') + + '", please make it shorter! (Maximum length: ' + $(this).attr('data_maxlength') + ' char.)' ); count++; @@ -427,8 +426,8 @@ function check () { $('textarea.checkbytes').each(function (_n) { if (getUTF8Length($(this).val().trim()) > (0 + $(this).attr('data_maxlength'))) { alert( - 'ERROR\n\nText is too long in field "' + $(this).attr('data_info') + - '", please make it shorter! (Maximum length: ' + + 'ERROR\n\nText is too long in field "' + $(this).attr('data_info') + + '", please make it shorter! (Maximum length: ' + $(this).attr('data_maxlength') + ' bytes.)' ); count++; @@ -437,7 +436,7 @@ function check () { $('input.noblanksnocomma').each(function (_n) { if ($(this).val().indexOf(' ') > 0 || $(this).val().indexOf(',') > 0) { alert( - 'ERROR\n\nNo spaces or commas allowed in field "' + + 'ERROR\n\nNo spaces or commas allowed in field "' + $(this).attr('data_info') + '", please remove!' ); count++; @@ -468,7 +467,7 @@ function confirmDelete () { } /** - * Enable/disable words hint. + * Enable/disable words hint. * Function called when clicking on "Show All". */ function showAllwordsClick () { @@ -476,18 +475,18 @@ function showAllwordsClick () { const showLeaning = $('#showlearningtranslations').prop('checked') ? '1' : '0'; const text = $('#thetextid').text(); // Timeout necessary because the button is clicked on the left (would hide frames) - setTimeout(function () { + setTimeout(function () { showRightFrames( - 'set_text_mode.php?mode=' + showAll + '&showLearning=' + showLeaning + + 'set_text_mode.php?mode=' + showAll + '&showLearning=' + showLeaning + '&text=' + text - );}, 500); - setTimeout(function () {window.location.reload();}, 4000); + ); + }, 500); + setTimeout(function () { window.location.reload(); }, 4000); } function textareaKeydown (event) { if (event.keyCode && event.keyCode == '13') { - if (check()) - $('input:submit').last().trigger('click'); + if (check()) { $('input:submit').last().trigger('click'); } return false; } else { return true; @@ -503,13 +502,13 @@ function noShowAfter3Secs () { */ function setTheFocus () { $('.setfocus') - .trigger('focus') - .trigger('select'); + .trigger('focus') + .trigger('select'); } /** * Prepare a dialog when the user clicks a word during a test. - * + * * @returns {false} */ function word_click_event_do_test_test () { @@ -529,12 +528,12 @@ function word_click_event_do_test_test () { /** * Handle keyboard interaction when testing a word. - * - * @param {object} e A keystroke object + * + * @param {object} e A keystroke object * @returns {bool} true if nothing was done, false otherwise */ function keydown_event_do_test_test (e) { - if ((e.key == 'Space' || e.which == 32) && !LWT_DATA.test.answer_opened) { + if ((e.key == 'Space' || e.which == 32) && !LWT_DATA.test.answer_opened) { // space : show solution $('.word').trigger('click'); cleanupRightFrames(); @@ -542,46 +541,45 @@ function keydown_event_do_test_test (e) { LWT_DATA.test.answer_opened = true; return false; } - if (e.key == "Escape" || e.which == 27) { + if (e.key == 'Escape' || e.which == 27) { // esc : skip term, don't change status - showRightFrames( - 'set_test_status.php?wid=' + LWT_DATA.word.id + + showRightFrames( + 'set_test_status.php?wid=' + LWT_DATA.word.id + '&status=' + $('.word').attr('data_status') ); return false; } - if (e.key == "I" || e.which == 73) { + if (e.key == 'I' || e.which == 73) { // I : ignore, status=98 - showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&status=98'); + showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&status=98'); return false; } - if (e.key == "W" || e.which == 87) { + if (e.key == 'W' || e.which == 87) { // W : well known, status=99 - showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&status=99'); + showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&status=99'); return false; } - if (e.key == "E" || e.which == 69) { + if (e.key == 'E' || e.which == 69) { // E : edit - showRightFrames('edit_tword.php?wid=' + LWT_DATA.word.id); + showRightFrames('edit_tword.php?wid=' + LWT_DATA.word.id); return false; } // The next interactions should only be available with displayed solution - if (!LWT_DATA.test.answer_opened) - return true; - if (e.key == "ArrowUp" || e.which == 38) { + if (!LWT_DATA.test.answer_opened) { return true; } + if (e.key == 'ArrowUp' || e.which == 38) { // up : status+1 - showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&stchange=1'); + showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&stchange=1'); return false; } - if (e.key == "ArrowDown" || e.which == 40) { + if (e.key == 'ArrowDown' || e.which == 40) { // down : status-1 - showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&stchange=-1'); + showRightFrames('set_test_status.php?wid=' + LWT_DATA.word.id + '&stchange=-1'); return false; } for (let i = 0; i < 5; i++) { - if (e.which == (49 + i) || e.which == (97 + i)) { + if (e.which == (49 + i) || e.which == (97 + i)) { // 1,.. : status=i - showRightFrames( + showRightFrames( 'set_test_status.php?wid=' + LWT_DATA.word.id + '&status=' + (i + 1) ); return false; @@ -590,14 +588,12 @@ function keydown_event_do_test_test (e) { return true; } - - 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') + + title = "

" + $(this).attr('data_text') + '

'; } else { title = "

" + $(this).text() + '

'; @@ -619,9 +615,9 @@ jQuery.fn.extend({ const ann = $(this).attr('data_ann'); if (ann != '' && ann != '*') { var re = new RegExp( - '(.*[' + LWT_DATA.language.delimiter + '][ ]{0,1}|^)(' + - ann.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')($|[ ]{0,1}[' + - LWT_DATA.language.delimiter + '].*$| \\[.*$)', + '(.*[' + LWT_DATA.language.delimiter + '][ ]{0,1}|^)(' + + ann.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')($|[ ]{0,1}[' + + LWT_DATA.language.delimiter + '].*$| \\[.*$)', '' ); trans = trans.replace(re, '$1$2$3'); @@ -629,7 +625,7 @@ jQuery.fn.extend({ } title += '

Transl.: ' + trans + '

'; } - title += '

Status: ' + statname + + title += '

Status: ' + statname + '

'; return title; } @@ -654,9 +650,9 @@ function get_position_from_id (id_string) { /** * Save a setting to the database. - * - * @param {string} k Setting name as a key - * @param {string} v Setting value + * + * @param {string} k Setting name as a key + * @param {string} v Setting value */ function do_ajax_save_setting (k, v) { $.post( @@ -669,40 +665,37 @@ function do_ajax_save_setting (k, v) { } /** - * Assign the display value of a select element to the value element of another input. - * - * @param {elem} select_elem - * @param {elem} input_elem + * Assign the display value of a select element to the value element of another input. + * + * @param {elem} select_elem + * @param {elem} input_elem */ -function quick_select_to_input(select_elem, input_elem) -{ - let val = select_elem.options[select_elem.selectedIndex].value; - if (val != '') - input_elem.value = val; +function quick_select_to_input (select_elem, input_elem) { + const val = select_elem.options[select_elem.selectedIndex].value; + if (val != '') { input_elem.value = val; } select_elem.value = ''; } /** * Return an HTML group of options to add to a select field. - * + * * @param {string[]} paths All paths (files and folders) * @param {string[]} folders Folders paths, should be a subset of paths * @param {string} base_path Base path for LWT to append - * + * * @returns {HTMLOptionElement[]} List of options to append to the select. - * + * * @since 2.9.1-fork Base path is no longer used */ -function select_media_path(paths, folders, base_path) -{ - let options = [], temp_option = document.createElement('option'); - temp_option.value = ""; - temp_option.text = "[Choose...]"; +function select_media_path (paths, folders, base_path) { + const options = []; let temp_option = document.createElement('option'); + temp_option.value = ''; + temp_option.text = '[Choose...]'; options.push(temp_option); for (let i = 0; i < paths.length; i++) { temp_option = document.createElement('option') if (folders.includes(paths[i])) { - temp_option.setAttribute("disabled", "disabled"); + temp_option.setAttribute('disabled', 'disabled'); temp_option.text = '-- Directory: ' + paths[i] + '--'; } else { temp_option.value = paths[i]; @@ -715,39 +708,39 @@ function select_media_path(paths, folders, base_path) /** * Process the received data from media selection query - * - * @param {Object} data Received data as a JSON object + * + * @param {Object} data Received data as a JSON object */ -function media_select_receive_data(data) { - $('#mediaSelectLoadingImg').css("display", "none"); - if (data["error"] !== undefined) { - let msg; - if (data["error"] == "not_a_directory") { - msg = '[Error: "../' + data["base_path"] + '/media" exists, but it is not a directory.]'; - } else if (data["error"] == "does_not_exist") { - msg = '[Directory "../' + data["base_path"] + '/media" does not yet exist.]'; - } else { - msg = "[Unknown error!]"; - } - $('#mediaSelectErrorMessage').text(msg); - $('#mediaSelectErrorMessage').css("display", "inherit"); +function media_select_receive_data (data) { + $('#mediaSelectLoadingImg').css('display', 'none'); + if (data.error !== undefined) { + let msg; + if (data.error == 'not_a_directory') { + msg = '[Error: "../' + data.base_path + '/media" exists, but it is not a directory.]'; + } else if (data.error == 'does_not_exist') { + msg = '[Directory "../' + data.base_path + '/media" does not yet exist.]'; } else { - const options = select_media_path(data["paths"], data["folders"], data["base_path"]); - $('#mediaselect select').empty(); - for (let i = 0; i < options.length; i++) { - $('#mediaselect select').append(options[i]); - } - $('#mediaselect select').css("display", "inherit"); + msg = '[Unknown error!]'; + } + $('#mediaSelectErrorMessage').text(msg); + $('#mediaSelectErrorMessage').css('display', 'inherit'); + } else { + const options = select_media_path(data.paths, data.folders, data.base_path); + $('#mediaselect select').empty(); + for (let i = 0; i < options.length; i++) { + $('#mediaselect select').append(options[i]); } + $('#mediaselect select').css('display', 'inherit'); + } } /** * Perform an AJAX query to retrieve and display the media files path. */ function do_ajax_update_media_select () { - $('#mediaSelectErrorMessage').css("display", "none"); - $('#mediaselect select').css("display", "none"); - $('#mediaSelectLoadingImg').css("display", "inherit"); + $('#mediaSelectErrorMessage').css('display', 'none'); + $('#mediaselect select').css('display', 'none'); + $('#mediaSelectLoadingImg').css('display', 'inherit'); $.getJSON( 'api.php/v1/media-files', {}, @@ -757,84 +750,82 @@ function do_ajax_update_media_select () { /** * Prepare am HTML element that formats the sentences - * - * @param {JSON} sentences A list of sentences to display. + * + * @param {JSON} sentences A list of sentences to display. * @param {string} click_target The selector for the element that should change value on click * @returns {HTMLElement} A formatted group of sentences */ -function display_example_sentences(sentences, click_target) -{ +function display_example_sentences (sentences, click_target) { let img, clickable, parentDiv; - const outElement = document.createElement("div"); + const outElement = document.createElement('div'); for (let i = 0; i < sentences.length; i++) { // Add the checbox - img = document.createElement("img"); - img.src = "icn/tick-button.png"; - img.title = "Choose"; + img = document.createElement('img'); + img.src = 'icn/tick-button.png'; + img.title = 'Choose'; // Clickable element clickable = document.createElement('span'); - clickable.classList.add("click"); + clickable.classList.add('click'); // Doesn't feel the right way to do it clickable.setAttribute( - "onclick", - "{" + click_target + ".value = '" + sentences[i][1].replaceAll("'", "\\'") + + 'onclick', + '{' + click_target + ".value = '" + sentences[i][1].replaceAll("'", "\\'") + "';lwt_form_check.makeDirty();}" ); clickable.appendChild(img); // Create parent - parentDiv = document.createElement("div"); + parentDiv = document.createElement('div'); parentDiv.appendChild(clickable); - parentDiv.innerHTML += "  " + sentences[i][0]; + parentDiv.innerHTML += '  ' + sentences[i][0]; // Add to the output outElement.appendChild(parentDiv); } return outElement; } - /** * Prepare am HTML element that formats the sentences - * - * @param {JSON} sentences A list of sentences to display. + * + * @param {JSON} sentences A list of sentences to display. * @param {string} click_target The selector for the element that should change value on click * @returns {HTMLElement} A formatted group of sentences */ -function change_example_sentences_zone(sentences, ctl) { - $('#exsent-waiting').css("display", "none"); - $('#exsent-sentences').css("display", "inherit"); +function change_example_sentences_zone (sentences, ctl) { + $('#exsent-waiting').css('display', 'none'); + $('#exsent-sentences').css('display', 'inherit'); const new_element = display_example_sentences(sentences, ctl); $('#exsent-sentences').append(new_element); } /** * Get and display the sentences containing specific word. - * - * @param {int} lang Language ID - * @param {string} word Term text (the looked for term) + * + * @param {int} lang Language ID + * @param {string} word Term text (the looked for term) * @param {string} ctl Selector for the element to edit on click * @param {int} woid Term id (word or multi-word) * @returns {undefined} */ function do_ajax_show_sentences (lang, word, ctl, woid) { - $('#exsent-interactable').css("display", "none"); - $('#exsent-waiting').css("display", "inherit"); + $('#exsent-interactable').css('display', 'none'); + $('#exsent-waiting').css('display', 'inherit'); if (isInt(woid) && woid != -1) { $.getJSON( 'api.php/v1/sentences-with-term/' + woid, { - lg_id: lang, + lg_id: lang, word_lc: word }, (data) => change_example_sentences_zone(data, ctl) ); } else { - let query = { - lg_id: lang, + const query = { + lg_id: lang, word_lc: word }; if (parseInt(woid, 10) == -1) { - query["advanced_search"] = true; + query.advanced_search = true; } $.getJSON( 'api.php/v1/sentences-with-term', @@ -846,12 +837,12 @@ function do_ajax_show_sentences (lang, word, ctl, woid) { /** * Send an AJAX request to get similar terms to a term. - * + * * @param {number} lg_id Language ID * @param {string} word_text Text to match * @returns {JSON} Request used */ -function do_ajax_req_sim_terms(lg_id, word_text) { +function do_ajax_req_sim_terms (lg_id, word_text) { return $.getJSON( 'api.php/v1/similar-terms', { @@ -869,27 +860,27 @@ function do_ajax_show_similar_terms () { do_ajax_req_sim_terms( parseInt($('#langfield').val(), 10), $('#wordfield').val() ) - .done( - function (data) { - $('#simwords').html(data.similar_terms); - } - ).fail( - function (data) { - console.log(data); - } - ); + .done( + function (data) { + $('#simwords').html(data.similar_terms); + } + ).fail( + function (data) { + console.log(data); + } + ); } /** * Update WORDCOUNTS in with an AJAX request. - * + * * @returns {undefined} */ function do_ajax_word_counts () { - const t = $('.markcheck').map(function () { - return $(this).val(); + const t = $('.markcheck').map(function () { + return $(this).val(); }) - .get().join(','); + .get().join(','); $.getJSON( 'api.php/v1/texts/statistics', { @@ -905,18 +896,18 @@ function do_ajax_word_counts () { /** * Set a unique item in barchart to reflect how many words are known. - * + * * @returns {undefined} */ -function set_barchart_item() { +function set_barchart_item () { const id = $(this).find('span').first().attr('id').split('_')[2]; /** @var {int} v Number of terms in the text */ let v; if (SUW & 16) { - v = parseInt(WORDCOUNTS.expru[id] || 0, 10) + + v = parseInt(WORDCOUNTS.expru[id] || 0, 10) + parseInt(WORDCOUNTS.totalu[id], 10); } else { - v = parseInt(WORDCOUNTS.expr[id] || 0, 10) + + v = parseInt(WORDCOUNTS.expr[id] || 0, 10) + parseInt(WORDCOUNTS.total[id], 10); } $(this).children('li').each(function () { @@ -926,17 +917,17 @@ function set_barchart_item() { Linear version const h = (v - $(this).children('span').text()) * 25 / v; */ - /* + /* Logarithmic version (25 / v) is vocab per pixel log scale so the size scaled becomes Math.log(($(this).children('span').text())) - so the total height corresponding to text vocab after scaling should be - Math.log(v) the proportion of column height to box height is thus + so the total height corresponding to text vocab after scaling should be + Math.log(v) the proportion of column height to box height is thus (Math.log(($(this).children('span').text())) / Math.log(v)) - putting this back in pixel, we get - (Math.log(($(this).children('span').text())) / Math.log(v)) * 25 + putting this back in pixel, we get + (Math.log(($(this).children('span').text())) / Math.log(v)) * 25 should be the column height - so (25 - (Math.log(($(this).children('span').text())) / Math.log(v)) * 25) + so (25 - (Math.log(($(this).children('span').text())) / Math.log(v)) * 25) is the intended border top size. */ // Avoid to put 0 in logarithm @@ -949,7 +940,7 @@ function set_barchart_item() { /** * Set the number of words known in a text (in edit_texts.php main page). - * + * * @returns {undefined} */ function set_word_counts () { @@ -962,13 +953,11 @@ function set_word_counts () { } $('#total_' + key).html((SUW & 1 ? value : WORDCOUNTS.total[key])); $.each(WORDCOUNTS.statu[key], function (k, v) { - if (SUW & 8) - $('#stat_' + k + '_' + key).html(v); + if (SUW & 8) { $('#stat_' + k + '_' + key).html(v); } knownu += parseInt(v); }); $.each(WORDCOUNTS.stat[key], function (k, v) { - if (!(SUW & 8)) - $('#stat_' + k + '_' + key).html(v); + if (!(SUW & 8)) { $('#stat_' + k + '_' + key).html(v); } known += parseInt(v); }); $('#saved_' + key).html(known ? ((SUW & 2 ? knownu : known) - expr + '+' + expr) : 0); @@ -1001,9 +990,9 @@ function set_word_counts () { } /** - * Handle the click event to switch between total and + * Handle the click event to switch between total and * unique words count in edit_texts.php. - * + * * @returns {undefined} */ function word_count_click () { @@ -1013,10 +1002,10 @@ function word_count_click () { } else { $(this).html('t'); } - SUW = (parseInt($('#chart').attr('data_wo_cnt')) << 4) + - (parseInt($('#unknownpercent').attr('data_wo_cnt')) << 3) + - (parseInt($('#unknown').attr('data_wo_cnt')) << 2) + - (parseInt($('#saved').attr('data_wo_cnt')) << 1) + + SUW = (parseInt($('#chart').attr('data_wo_cnt')) << 4) + + (parseInt($('#unknownpercent').attr('data_wo_cnt')) << 3) + + (parseInt($('#unknown').attr('data_wo_cnt')) << 2) + + (parseInt($('#saved').attr('data_wo_cnt')) << 1) + (parseInt($('#total').attr('data_wo_cnt'))); set_word_counts(); }); @@ -1024,25 +1013,24 @@ function word_count_click () { /** * Create a radio button with a candidate choice for a term annotation. - * - * @param {string} curr_trans Current anotation (translation) set for the term + * + * @param {string} curr_trans Current anotation (translation) set for the term * @param {string} trans_data All the useful data for the term * @returns {string} An HTML-formatted option */ -function translation_radio(curr_trans, trans_data) -{ +function translation_radio (curr_trans, trans_data) { if (trans_data.wid === null) { - return ""; + return ''; } const trim_trans = curr_trans.trim(); if (trim_trans == '*' || trim_trans == '') { - return ""; + return ''; } const set = trim_trans == trans_data.trans; const option = ` - + trans_data.ann_index + '" value="' + escape_html_chars(trim_trans) + `" />   ` + escape_html_chars(trim_trans) + `
`; @@ -1051,18 +1039,17 @@ function translation_radio(curr_trans, trans_data) /** * When a term translation is edited, recreate it's annotations. - * + * * @param {Object} trans_data Useful data for this term * @param {int} text_id Text ID */ -function edit_term_ann_translations(trans_data, text_id) -{ +function edit_term_ann_translations (trans_data, text_id) { const widset = trans_data.wid !== null; // First create a link to edit the word in a new window let edit_word_link; if (widset) { const req_arg = $.param({ - fromAnn: "$(document).scrollTop()", + fromAnn: '$(document).scrollTop()', wid: trans_data.wid, ord: trans_data.term_ord, tid: text_id @@ -1077,7 +1064,7 @@ function edit_term_ann_translations(trans_data, text_id) } $(`#editlink${trans_data.ann_index}`).html(edit_word_link); // Now edit translations (if necessary) - let translations_list = ""; + let translations_list = ''; trans_data.translations.forEach( function (candidate_trans) { translations_list += translation_radio(candidate_trans, trans_data); @@ -1087,12 +1074,12 @@ function edit_term_ann_translations(trans_data, text_id) const select_last = trans_data.translations.length == 0; // Empty radio button and text field after the list of translations translations_list += ` -   -   Save another translation to existent term`; - } else { - translations_list += + `'#tx${trans_data.ann_index}');" />`; + } else { + translations_list += `Save translation to new term`; + `${trans_data.term_lc},${trans_data.lang_id});" />`; } translations_list += `   @@ -1129,15 +1116,14 @@ function edit_term_ann_translations(trans_data, text_id) /** * Load the possible translations for a word. - * - * @param {int} pagepos Position to scroll to + * + * @param {int} pagepos Position to scroll to * @param {string} word Word in lowercase to get annotations * @param {int} term_id Term ID - * + * * @since 2.9.0 The new parameter $wid is now necessary */ -function do_ajax_edit_impr_text(pagepos, word, term_id) -{ +function do_ajax_edit_impr_text (pagepos, word, term_id) { // Special case, on empty word reload the main annotations form if (word == '') { $('#editimprtextdata').html(''); @@ -1149,12 +1135,12 @@ function do_ajax_edit_impr_text(pagepos, word, term_id) $.getJSON( 'api.php/v1/terms/' + term_id + '/translations', { - text_id: textid, - term_lc: word + text_id: textid, + term_lc: word }, function (data) { - if ("error" in data) { - alert(data["error"]); + if ('error' in data) { + alert(data.error); } else { edit_term_ann_translations(data, textid); $.scrollTo(pagepos); @@ -1167,12 +1153,12 @@ function do_ajax_edit_impr_text(pagepos, word, term_id) /** * Show the right frames if found, and can load an URL in those frames - * - * @param {string|undefined} roUrl Upper-right frame URL to laod + * + * @param {string|undefined} roUrl Upper-right frame URL to laod * @param {string|undefined} ruUrl Lower-right frame URL to load * @returns {boolean} true if frames were found, false otherwise */ -function showRightFrames(roUrl, ruUrl) { +function showRightFrames (roUrl, ruUrl) { if (roUrl !== undefined) { top.frames.ro.location.href = roUrl; } @@ -1180,7 +1166,7 @@ function showRightFrames(roUrl, ruUrl) { top.frames.ru.location.href = ruUrl; } if ($('#frames-r').length) { - $('#frames-r').animate({right: '5px'}); + $('#frames-r').animate({ right: '5px' }); return true; } return false; @@ -1188,12 +1174,12 @@ function showRightFrames(roUrl, ruUrl) { /** * Hide the right frames if found. - * + * * @returns {boolean} true if frames were found, false otherwise */ -function hideRightFrames() { +function hideRightFrames () { if ($('#frames-r').length) { - $('#frames-r').animate({right: '-100%'}); + $('#frames-r').animate({ right: '-100%' }); return true; } return false; @@ -1204,11 +1190,10 @@ function hideRightFrames() { * * Called from several places: insert_word_ignore.php, * set_word_status.php, delete_word.php, etc. - * + * * @returns {undefined} */ -function cleanupRightFrames() { - +function cleanupRightFrames () { // A very annoying hack to get right frames to hide correctly. // Calling hideRightFrames directly in window.parent.setTimeout // does //not work* for some reason ... when called that way, @@ -1219,7 +1204,7 @@ function cleanupRightFrames() { // // We have to use an anon function to ensure that the frames-r // gets resolved when the timeout fires. - const mytimeout = function() { + const mytimeout = function () { const rf = window.parent.document.getElementById('frames-r'); rf.click(); } @@ -1229,13 +1214,12 @@ function cleanupRightFrames() { window.parent.setTimeout(window.parent.cClick, 100); } - /** * Play the success sound. - * + * * @returns {object} Promise on the status of sound */ -function successSound() { +function successSound () { document.getElementById('success_sound').pause(); document.getElementById('failure_sound').pause(); return document.getElementById('success_sound').play(); @@ -1246,7 +1230,7 @@ function successSound() { * * @returns {object} Promise on the status of sound */ -function failureSound() { +function failureSound () { document.getElementById('success_sound').pause(); document.getElementById('failure_sound').pause(); return document.getElementById('failure_sound').play(); @@ -1255,36 +1239,36 @@ function failureSound() { const lwt = { /** - * Prepare the action so that a click switches between + * Prepare the action so that a click switches between * unique word count and total word count. - * + * * @returns {undefined} */ prepare_word_count_click: function () { $('#total,#saved,#unknown,#chart,#unknownpercent') - .on('click', function( event ) { - $(this).attr('data_wo_cnt',parseInt($(this).attr('data_wo_cnt'))^1); + .on('click', function (event) { + $(this).attr('data_wo_cnt', parseInt($(this).attr('data_wo_cnt')) ^ 1); word_count_click(); event.stopImmediatePropagation(); - }).attr('title',"u: Unique Word Counts\nt: Total Word Counts"); + }).attr('title', 'u: Unique Word Counts\nt: Total Word Counts'); do_ajax_word_counts(); }, /** * Save the settings about unique/total words count. - * + * * @returns {undefined} */ save_text_word_count_settings: function () { - if (SUW == SHOWUNIQUE) { - return; - } - const a = $('#total').attr('data_wo_cnt') + - $('#saved').attr('data_wo_cnt') + - $('#unknown').attr('data_wo_cnt') + - $('#unknownpercent').attr('data_wo_cnt') + + if (SUW == SHOWUNIQUE) { + return; + } + const a = $('#total').attr('data_wo_cnt') + + $('#saved').attr('data_wo_cnt') + + $('#unknown').attr('data_wo_cnt') + + $('#unknownpercent').attr('data_wo_cnt') + $('#chart').attr('data_wo_cnt'); - do_ajax_save_setting('set-show-text-word-counts', a); + do_ajax_save_setting('set-show-text-word-counts', a); } } @@ -1308,9 +1292,9 @@ $.fn.serializeObject = function () { /** * Wrap the radio buttons into stylised elements. */ -function wrapRadioButtons() { +function wrapRadioButtons () { $( - ':input,.wrap_checkbox span,.wrap_radio span,a:not([name^=rec]),select,' + + ':input,.wrap_checkbox span,.wrap_radio span,a:not([name^=rec]),select,' + '#mediaselect span.click,#forwbutt,#backbutt' ).each(function (i) { $(this).attr('tabindex', i + 1); }); $('.wrap_radio span').on('keydown', function (e) { @@ -1324,7 +1308,7 @@ function wrapRadioButtons() { /** * Do a lot of different DOM manipulations */ -function prepareMainAreas() { +function prepareMainAreas () { $('.edit_area').editable('inline_edit.php', { type: 'textarea', @@ -1360,7 +1344,7 @@ function prepareMainAreas() { $(this).attr('id', 'cb_' + z++); } $(this).after( - '' ); }); @@ -1371,7 +1355,7 @@ function prepareMainAreas() { }); $(document).on('mouseup', function () { $('button,input[type=button],.wrap_radio span,.wrap_checkbox span') - .trigger('blur'); + .trigger('blur'); }); $('.wrap_checkbox span').on('keydown', function (e) { if (e.keyCode == 32) { @@ -1387,13 +1371,13 @@ function prepareMainAreas() { $(this).attr('id', 'rb_' + z++); } $(this).after( - '' ); }); - $('.button-file').on('click', function () { - $(this).next('input[type="file"]').trigger('click'); - return false; + $('.button-file').on('click', function () { + $(this).next('input[type="file"]').trigger('click'); + return false; }); $('input.impr-ann-text').on('change', changeImprAnnText); $('input.impr-ann-radio').on('change', changeImprAnnRadio); @@ -1403,13 +1387,13 @@ function prepareMainAreas() { $('textarea.textarea-noreturn').on('keydown', textareaKeydown); // Resizable from right frames $('#frames-r').resizable({ - handles: "w", + handles: 'w', stop: function (_event, ui) { // Resize left frames $('#frames-l').css('width', ui.position.left - 20); // Save settings do_ajax_save_setting( - 'set-text-l-framewidth-percent', + 'set-text-l-framewidth-percent', Math.round($('#frames-l').width() / $(window).width() * 100) ); } @@ -1435,9 +1419,9 @@ function prepareMainAreas() { markClick(); setTheFocus(); if ( - $('#simwords').length > 0 && $('#langfield').length > 0 && + $('#simwords').length > 0 && $('#langfield').length > 0 && $('#wordfield').length > 0 - ) { + ) { $('#wordfield').on('blur', do_ajax_show_similar_terms); do_ajax_show_similar_terms(); } diff --git a/src/js/pgm.js b/src/js/pgm.js index 98ca4ec8..3d4f4d00 100644 --- a/src/js/pgm.js +++ b/src/js/pgm.js @@ -1,18 +1,18 @@ /** * LWT Javascript functions - * + * * @author andreask7 * @license Unlicense * @since 1.6.16-fork - * + * * "Learning with Texts" (LWT) is free and unencumbered software * released into the PUBLIC DOMAIN. - * + * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a * compiled binary, for any purpose, commercial or non-commercial, * and by any means. - * + * * In jurisdictions that recognize copyright laws, the author or * authors of this software dedicate any and all copyright * interest in the software to the public domain. We make this @@ -21,7 +21,7 @@ * dedication to be an overt act of relinquishment in perpetuity * of all present and future rights to this software under * copyright law. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE @@ -30,18 +30,17 @@ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - * + * * For more information, please refer to [http://unlicense.org/]. */ - /************************************************************** * Other JS utility functions **************************************************************/ /** * Return the name of a given status. - * + * * @param {int} status Status number (int<1, 5>|98|99) * @returns {string} */ @@ -51,7 +50,7 @@ function getStatusName (status) { /** * Return the abbreviation of a status - * + * * @param {int} status Status number (int<1, 5>|98|99) * @returns {string} Abbreviation */ @@ -59,12 +58,11 @@ function getStatusAbbr (status) { return STATUSES[status] ? STATUSES[status].abbr : '?'; } - /** * Translate a sentence. - * - * @param {string} url Translation URL with "{term}" marking the interesting term - * @param {object} sentctl Textarea contaning sentence + * + * @param {string} url Translation URL with "{term}" marking the interesting term + * @param {object} sentctl Textarea contaning sentence * @returns {void} */ function translateSentence (url, sentctl) { @@ -78,9 +76,9 @@ function translateSentence (url, sentctl) { /** * Translate a sentence. - * - * @param {string} url Translation URL with "{term}" marking the interesting term - * @param {object} sentctl Textarea contaning sentence + * + * @param {string} url Translation URL with "{term}" marking the interesting term + * @param {object} sentctl Textarea contaning sentence * @returns {void} */ function translateSentence2 (url, sentctl) { @@ -95,12 +93,12 @@ function translateSentence2 (url, sentctl) { /** * Open a new window with the translation of the word. - * + * * @param {string} url Dictionary URL * @param {object} wordctl Textarea containing word to translate. * @returns {void} */ -function translateWord(url, wordctl) { +function translateWord (url, wordctl) { if (wordctl !== undefined && url != '') { const text = wordctl.value; if (typeof text === 'string') { @@ -111,12 +109,12 @@ function translateWord(url, wordctl) { /** * Open a new window with the translation of the word. - * + * * @param {string} url Dictionary URL * @param {object} wordctl Textarea containing word to translate. * @returns {void} */ -function translateWord2(url, wordctl) { +function translateWord2 (url, wordctl) { if (wordctl !== undefined && url != '') { const text = wordctl.value; if (typeof text === 'string') { @@ -127,24 +125,24 @@ function translateWord2(url, wordctl) { /** * Open a new window with the translation of the word. - * + * * @param {string} url Dictionary URL * @param {string} word Word to translate. * @returns {void} */ -function translateWord3(url, word) { +function translateWord3 (url, word) { owin(createTheDictUrl(url, word)); } /** * Get the language name from the Google Translate URL. - * + * * @param {string} wblink3 Google Translate Dictionary URL * @returns {string} Language name - * + * * @since 2.7.0 Also works with a LibreTranslate URL */ -function getLangFromDict(wblink3) { +function getLangFromDict (wblink3) { let dictUrl, urlParams; if (wblink3.trim() == '') { return ''; @@ -153,26 +151,26 @@ function getLangFromDict(wblink3) { if (wblink3.startsWith('*')) { wblink3 = wblink3.substring(1); } - if (wblink3.startsWith("trans.php") || wblink3.startsWith("ggl.php")) { + if (wblink3.startsWith('trans.php') || wblink3.startsWith('ggl.php')) { wblink3 = 'http://' + wblink3; } dictUrl = new URL(wblink3); urlParams = dictUrl.searchParams; - if (urlParams.get("lwt_translator") == "libretranslate") { - return urlParams.get("source") || ""; + if (urlParams.get('lwt_translator') == 'libretranslate') { + return urlParams.get('source') || ''; } // Fallback to Google Translate - return urlParams.get("sl") || ""; + return urlParams.get('sl') || ''; } /** - * Return a tooltip, a short string describing the word (word, translation, - * romanization and learning status) - * - * @param {string} word The word + * Return a tooltip, a short string describing the word (word, translation, + * romanization and learning status) + * + * @param {string} word The word * @param {string} trans Translation of the word - * @param {string} roman Romanized version - * @param {int} status Learning status of the word + * @param {string} roman Romanized version + * @param {int} status Learning status of the word * @returns {string} Tooltip for this word */ function make_tooltip (word, trans, roman, status) { @@ -194,7 +192,7 @@ function make_tooltip (word, trans, roman, status) { /** * Escape the HTML characters, with an eventual annotation - * + * * @param {string} title String to be escaped * @param {string} ann An annotation to show in red * @returns {string} Escaped string @@ -204,14 +202,14 @@ function escape_html_chars_2 (title, ann) { const ann2 = escape_html_chars(ann); return escape_html_chars(title).replace(ann2, '' + ann2 + ''); - } + } return escape_html_chars(title); } /** * Open a window. - * - * @param {string} url URL of the window + * + * @param {string} url URL of the window */ function owin (url) { window.open( @@ -223,7 +221,7 @@ function owin (url) { /** * Open a window in edit mode. - * + * * @param {string} url Window URL */ function oewin (url) { @@ -236,18 +234,18 @@ function oewin (url) { /** * Create a dictionary URL. - * + * * JS alter ego of the createTheDictLink PHP function. - * + * * Case 1: url without any ### or "lwt_term": append term * Case 2: url with one ### or "lwt_term": substitute term - * + * * @param {string} u Dictionary URL * @param {string} w Term to be inserted in the URL * @returns {string} A link to external dictionary to get a translation of the word - * - * @since 2.6.0-fork Internals rewrote, do no longer use PHP code. - * The option putting encoding between ###enc### does no + * + * @since 2.6.0-fork Internals rewrote, do no longer use PHP code. + * The option putting encoding between ###enc### does no * longer work. It is deprecated and will be removed. * @since 2.7.0-fork Using "###" is deprecated, "lwt_term" recommended instead */ @@ -258,13 +256,13 @@ function createTheDictUrl (u, w) { const pos = (term_elem === null) ? -1 : url.indexOf(term_elem[0]); // No ###/lwt_term found if (pos == -1) { - return url + encodeURIComponent(trm); + return url + encodeURIComponent(trm); } // ###/lwt_term found const pos2 = url.indexOf('###', pos + 1); if (pos2 === -1) { - // 1 ###/lwt_term found - return url.replace(term_elem, trm == '' ? '+' : encodeURIComponent(trm)); + // 1 ###/lwt_term found + return url.replace(term_elem, trm == '' ? '+' : encodeURIComponent(trm)); } // 2 ### found // Get encoding @@ -272,19 +270,19 @@ function createTheDictUrl (u, w) { pos + term_elem[0].length, pos2 - pos - term_elem[0].length ).trim(); console.warn( - "Trying to use encoding '" + enc + "'. This feature is abandonned since " + - "2.6.0-fork. Using default UTF-8." + "Trying to use encoding '" + enc + "'. This feature is abandonned since " + + '2.6.0-fork. Using default UTF-8.' ); let output = url.substring(0, pos) + encodeURIComponent(trm); - if (pos2+3 < url.length) { - output += url.substring(pos2 + 3); + if (pos2 + 3 < url.length) { + output += url.substring(pos2 + 3); } return output; } /** * Create an HTML link for a dictionary. - * + * * @param {string} u Dictionary URL * @param {string} w Word or sentence to be translated * @param {string} t Text to display @@ -293,7 +291,7 @@ function createTheDictUrl (u, w) { */ function createTheDictLink (u, w, t, b) { let url = u.trim(); - let popup = false; + let popup = false; const trm = w.trim(); const txt = t.trim(); const txtbefore = b.trim(); @@ -306,7 +304,7 @@ function createTheDictLink (u, w, t, b) { popup = true; } try { - let final_url = new URL(url); + const final_url = new URL(url); popup = popup || final_url.searchParams.has('lwt_popup'); } catch (err) { if (!(err instanceof TypeError)) { @@ -315,12 +313,12 @@ function createTheDictLink (u, w, t, b) { } if (popup) { r = ' ' + txtbefore + - ' ' + txt + ' '; + ' ' + txt + ' '; } else { r = ' ' + txtbefore + - ' ' + txt + ' '; } return r; @@ -328,17 +326,17 @@ function createTheDictLink (u, w, t, b) { /** * Create a sentence lookup link. - * - * @param {int} torder Text order + * + * @param {int} torder Text order * @param {int} txid Text ID - * @param {string} url Translator URL + * @param {string} url Translator URL * @param {string} txt Word text * @returns {string} HTML-formatted link. */ function createSentLookupLink (torder, txid, url, txt) { url = url.trim(); txt = txt.trim(); - let r = ''; + const r = ''; let popup = false; let external = false; const target_url = 'trans.php?x=1&i=' + torder + '&t=' + txid; @@ -350,7 +348,7 @@ function createSentLookupLink (torder, txid, url, txt) { popup = true; } try { - let final_url = new URL(url); + const final_url = new URL(url); popup = popup || final_url.searchParams.has('lwt_popup'); external = true; } catch (err) { @@ -359,40 +357,40 @@ function createSentLookupLink (torder, txid, url, txt) { } } if (popup) { - return ' ' + + return ' ' + txt + ' '; - } + } if (external) { - return ' ' - + txt + ' '; + return ' ' + + txt + ' '; } return r; } /** * Replace html characters with encodings - * + * * See https://stackoverflow.com/questions/1787322/what-is-the-htmlspecialchars-equivalent-in-javascript - * - * @param {string} s String to be escaped + * + * @param {string} s String to be escaped * @returns {string} Escaped string */ -function escape_html_chars(s) { - let map = { +function escape_html_chars (s) { + const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', - "\x0d": '
' // This one inserts HTML, delete? (2.9.0) + '\x0d': '
' // This one inserts HTML, delete? (2.9.0) }; - return s.replace(/[&<>"'\x0d]/g, function(m) { return map[m]; }); + return s.replace(/[&<>"'\x0d]/g, function (m) { return map[m]; }); } /** * Escape only single apostrophe ("'") from string - * + * * @param {string} s String to be escaped * @returns {string} Escaped string */ @@ -423,10 +421,10 @@ function multiActionGo (f, sel) { while (notok) { answer = prompt( '*** ' + t + ' ***' + - '\n\n*** ' + $('input.markcheck:checked').length + - ' Record(s) will be affected ***' + - '\n\nPlease enter one tag (20 char. max., no spaces, no commas -- ' + - 'or leave empty to cancel:', + '\n\n*** ' + $('input.markcheck:checked').length + + ' Record(s) will be affected ***' + + '\n\nPlease enter one tag (20 char. max., no spaces, no commas -- ' + + 'or leave empty to cancel:', answer ); if (typeof answer === 'object') answer = ''; @@ -443,12 +441,12 @@ function multiActionGo (f, sel) { f.submit(); } } else if ( - v == 'del' || v == 'smi1' || v == 'spl1' || v == 's1' || v == 's5' || - v == 's98' || v == 's99' || v == 'today' || v == 'delsent' || + v == 'del' || v == 'smi1' || v == 'spl1' || v == 's1' || v == 's5' || + v == 's98' || v == 's99' || v == 'today' || v == 'delsent' || v == 'lower' || v == 'cap' ) { var answer = confirm( - '*** ' + t + ' ***\n\n*** ' + $('input.markcheck:checked').length + + '*** ' + t + ' ***\n\n*** ' + $('input.markcheck:checked').length + ' Record(s) will be affected ***\n\nAre you sure?' ); if (answer) { @@ -472,11 +470,11 @@ function allActionGo (f, sel, n) { var answer = ''; while (notok) { answer = prompt( - 'THIS IS AN ACTION ON ALL RECORDS\n' + - 'ON ALL PAGES OF THE CURRENT QUERY!\n\n' + + 'THIS IS AN ACTION ON ALL RECORDS\n' + + 'ON ALL PAGES OF THE CURRENT QUERY!\n\n' + '*** ' + t + ' ***\n\n*** ' + n + ' Record(s) will be affected ***\n\n' + - 'Please enter one tag (20 char. max., no spaces, no commas -- ' + - 'or leave empty to cancel:', + 'Please enter one tag (20 char. max., no spaces, no commas -- ' + + 'or leave empty to cancel:', answer ); if (typeof answer === 'object') answer = ''; @@ -493,13 +491,13 @@ function allActionGo (f, sel, n) { f.submit(); } } else if ( - v == 'delall' || v == 'smi1all' || v == 'spl1all' || v == 's1all' || - v == 's5all' || v == 's98all' || v == 's99all' || v == 'todayall' || + v == 'delall' || v == 'smi1all' || v == 'spl1all' || v == 's1all' || + v == 's5all' || v == 's98all' || v == 's99all' || v == 'todayall' || v == 'delsentall' || v == 'capall' || v == 'lowerall' ) { var answer = confirm( - 'THIS IS AN ACTION ON ALL RECORDS\nON ALL PAGES OF THE CURRENT QUERY!\n\n'+ - '*** ' + t + ' ***\n\n*** ' + n + ' Record(s) will be affected ***\n\n' + + 'THIS IS AN ACTION ON ALL RECORDS\nON ALL PAGES OF THE CURRENT QUERY!\n\n' + + '*** ' + t + ' ***\n\n*** ' + n + ' Record(s) will be affected ***\n\n' + 'ARE YOU SURE?' ); if (answer) { @@ -515,7 +513,7 @@ function allActionGo (f, sel, n) { /** * Check if cookies are enabled by setting a cookie. - * + * * @returns {boolean} true if cookies are enabled, false otherwise */ function areCookiesEnabled () { @@ -531,9 +529,9 @@ function areCookiesEnabled () { /** * Set the current language. - * - * @param {string} ctl Current language name - * @param {string} url + * + * @param {string} ctl Current language name + * @param {string} url * @returns {void} */ function setLang (ctl, url) { @@ -544,9 +542,9 @@ function setLang (ctl, url) { /** * Reset current language to default. - * - * @param {string} url - * @returns {void} + * + * @param {string} url + * @returns {void} */ function resetAll (url) { location.href = 'inc/save_setting_redirect.php?k=currentlanguage&v=&u=' + url; @@ -554,10 +552,10 @@ function resetAll (url) { /** * Get a specific cookie by its name. - * - * @param {string} check_name Cookie name + * + * @param {string} check_name Cookie name * @returns {string|null} Value of the cookie if found, null otherwise - * + * * @since 2.6.0-fork Use decodeURIComponent instead of deprecated unescape */ function getCookie (check_name) { @@ -589,15 +587,15 @@ function getCookie (check_name) { /** * Set a new cookie. - * - * @param {string} name Name of the cookie - * @param {string} value Cookie value - * @param {number} expires Number of DAYS before the cookie expires. + * + * @param {string} name Name of the cookie + * @param {string} value Cookie value + * @param {number} expires Number of DAYS before the cookie expires. * @param {string} path Cookie path - * @param {string} domain Cookie domain - * @param {boolean} secure If it should only be sent through secure connection + * @param {string} domain Cookie domain + * @param {boolean} secure If it should only be sent through secure connection * @returns {void} - * + * * @since 2.6.0-fork Use encodeURIComponent instead of deprecated escape */ function setCookie (name, value, expires, path, domain, secure) { @@ -616,7 +614,7 @@ function setCookie (name, value, expires, path, domain, secure) { /** * Delete a cookie. - * + * * @param {string} name Cookie name * @param {string} path Cookie path * @param {string} domain Cookie domain @@ -633,10 +631,10 @@ function deleteCookie (name, path, domain) { /** * Prepare a window to make all words from a text well-known - * + * * @param {string} t Text ID */ -function iknowall(t) { +function iknowall (t) { const answer = confirm('Are you sure?'); if (answer) { showRightFrames('all_words_wellknown.php?text=' + t); @@ -646,19 +644,19 @@ function iknowall(t) { /** * Check is the table prefix is a valid alphanumeric character. * Create an alert if not. - * - * @param {string} p Table prefix + * + * @param {string} p Table prefix * @returns {boolean} true is the prefix is valid */ function check_table_prefix (p) { const re = /^[_a-zA-Z0-9]*$/; const r = p.length <= 20 && p.length > 0 && p.match(re); - if (!r) { + if (!r) { alert( - 'Table Set Name (= Table Prefix) must' - + '\ncontain 1 to 20 characters (only 0-9, a-z, A-Z and _).' - + '\nPlease correct your input.' - ); + 'Table Set Name (= Table Prefix) must' + + '\ncontain 1 to 20 characters (only 0-9, a-z, A-Z and _).' + + '\nPlease correct your input.' + ); } return r; } diff --git a/src/js/translation_api.js b/src/js/translation_api.js index c5ac05a3..40994655 100644 --- a/src/js/translation_api.js +++ b/src/js/translation_api.js @@ -1,134 +1,131 @@ /** * Standard JS interface to get translations - * + * * @author andreask7 * @license Unlicense * @since 1.6.16-fork */ -function deleteTranslation (){ - let frame = window.parent.frames['ro']; - if (frame === undefined) - frame = window.opener; - if ($('[name="WoTranslation"]', frame.document).val().trim().length) { - $('[name="WoTranslation"]', frame.document).val(''); - frame.lwt_form_check.makeDirty(); - } +function deleteTranslation () { + let frame = window.parent.frames.ro; + if (frame === undefined) { frame = window.opener; } + if ($('[name="WoTranslation"]', frame.document).val().trim().length) { + $('[name="WoTranslation"]', frame.document).val(''); + frame.lwt_form_check.makeDirty(); + } } function addTranslation (s) { - let frame = window.parent.frames['ro']; - if (frame === undefined) - frame = window.opener; - if (frame === undefined) { - alert('Translation can not be copied!'); - return; - } - let word_trans = frame.document.forms[0].WoTranslation; - if (typeof word_trans != 'object') { - alert('Translation can not be copied!'); - return; - } - let oldValue = word_trans.value; - if (oldValue.trim() == '') { - word_trans.value = s; - frame.lwt_form_check.makeDirty(); - } else { - if (oldValue.indexOf(s) == -1) { - word_trans.value = oldValue + ' / ' + s; - frame.lwt_form_check.makeDirty(); - } else { - if (confirm( - '"' + s + '" seems already to exist as a translation.\n' + + let frame = window.parent.frames.ro; + if (frame === undefined) { frame = window.opener; } + if (frame === undefined) { + alert('Translation can not be copied!'); + return; + } + const word_trans = frame.document.forms[0].WoTranslation; + if (typeof word_trans !== 'object') { + alert('Translation can not be copied!'); + return; + } + const oldValue = word_trans.value; + if (oldValue.trim() == '') { + word_trans.value = s; + frame.lwt_form_check.makeDirty(); + } else { + if (oldValue.indexOf(s) == -1) { + word_trans.value = oldValue + ' / ' + s; + frame.lwt_form_check.makeDirty(); + } else { + if (confirm( + '"' + s + '" seems already to exist as a translation.\n' + 'Insert anyway?' - )) - { - word_trans.value = oldValue + ' / ' + s; - frame.lwt_form_check.makeDirty(); - } - } - } + )) { + word_trans.value = oldValue + ' / ' + s; + frame.lwt_form_check.makeDirty(); + } + } + } } -function getGlosbeTranslation(text, lang, dest) { - // Note from 2.9.0: make asynchronous if possible - // Note: the Glosbe API is closed and may not be open again - $.ajax({ - url: 'http://glosbe.com/gapi/translate?' + $.param({ - from: lang, - dest: dest, - format: "json", - phrase: text, - callback: "?" - }), - type:"GET", - dataType: 'jsonp', - jsonp: 'getTranslationFromGlosbeApi', - jsonpCallback: 'getTranslationFromGlosbeApi', - async:'true' - }); +function getGlosbeTranslation (text, lang, dest) { + // Note from 2.9.0: make asynchronous if possible + // Note: the Glosbe API is closed and may not be open again + $.ajax({ + url: 'http://glosbe.com/gapi/translate?' + $.param({ + from: lang, + dest: dest, + format: 'json', + phrase: text, + callback: '?' + }), + type: 'GET', + dataType: 'jsonp', + jsonp: 'getTranslationFromGlosbeApi', + jsonpCallback: 'getTranslationFromGlosbeApi', + async: 'true' + }); } -function getTranslationFromGlosbeApi(data) { - try { - $.each(data.tuc,function(i,rows){ - if(rows.phrase){ - $('#translations') - .append( - '' + - 'Copy' + - '   ' + rows.phrase.text + +function getTranslationFromGlosbeApi (data) { + try { + $.each(data.tuc, function (i, rows) { + if (rows.phrase) { + $('#translations') + .append( + '' + + 'Copy' + + '   ' + rows.phrase.text + '
' - ); - } else if(rows.meanings){ - $('#translations') - .append( - '' + + ); + } else if (rows.meanings) { + $('#translations') + .append( + '' + 'Copy' + - '   ' + "(" + rows.meanings[0].text + ")" + + '   ' + '(' + rows.meanings[0].text + ')' + '
' - ); - } - }); - if(!data.tuc.length){ - $('#translations') - .before( - '

No translations found (' + data.from + '-' + data.dest + ').

' - ); - if(data.dest!='en' && data.from!='en'){ - $('#translations').attr('id','no_trans') - .after( - '

 

Glosbe Dictionary (' + - data.from + '-en):   ' + + ); + } + }); + if (!data.tuc.length) { + $('#translations') + .before( + '

No translations found (' + data.from + '-' + data.dest + ').

' + ); + if (data.dest != 'en' && data.from != 'en') { + $('#translations').attr('id', 'no_trans') + .after( + '

 

Glosbe Dictionary (' + + data.from + '-en):   ' + data.phrase + '

 

' - ); - getGlosbeTranslation(data.phrase,data.from,'en'); - } else $('#translations').after('
'); - } else - $('#translations') - .after('

 
' + data.tuc.length + ' translation' + - (data.tuc.length==1 ? '' : 's') + - ' retrieved via ' + + ); + getGlosbeTranslation(data.phrase, data.from, 'en'); + } else $('#translations').after('


'); + } else { + $('#translations') + .after('

 
' + data.tuc.length + ' translation' + + (data.tuc.length == 1 ? '' : 's') + + ' retrieved via
' + 'Glosbe API.


' - ); - } - catch(err) { - $('#translations') - .text( - 'Retrieval error. Possible reason: There is a limit of Glosbe API ' + - 'calls that may be done from one IP address in a fixed period of time,' + + ); + } + } catch (err) { + $('#translations') + .text( + 'Retrieval error. Possible reason: There is a limit of Glosbe API ' + + 'calls that may be done from one IP address in a fixed period of time,' + ' to prevent from abuse.' - ) - .after('
'); - } + ) + .after('
'); + } } /** * Base function to get a translation from LibreTranslate. - * + * * @param {string} text Text to translate * @param {string} lang Source language (language of the text, two letters or "auto") * @param {string} dest Destination language (two language) @@ -136,50 +133,50 @@ function getTranslationFromGlosbeApi(data) { * @param {string} url API URL * @returns {string} Translation */ -async function getLibreTranslateTranslationBase( - text, lang, dest, key="", url="http://localhost:5000/translate" - ) { - const res = await fetch( - url, - { - method: "POST", - body: JSON.stringify({ - q: text, - source: lang, - target: dest, - format: "text", - api_key: key - }), - headers: { "Content-Type": "application/json" } - } - ); +async function getLibreTranslateTranslationBase ( + text, lang, dest, key = '', url = 'http://localhost:5000/translate' +) { + const res = await fetch( + url, + { + method: 'POST', + body: JSON.stringify({ + q: text, + source: lang, + target: dest, + format: 'text', + api_key: key + }), + headers: { 'Content-Type': 'application/json' } + } + ); - const data = await res.json(); - return data.translatedText; + const data = await res.json(); + return data.translatedText; } /** * Main wrapper for LibreTranslate translation. - * + * * @param {URL} libre_url URL of LibreTranslate. * @param {string} text Text to translate * @param {string} lang Source language (language of the text, two letters or "auto") * @param {string} dest Destination language (two language) * @returns {string} Translation */ -async function getLibreTranslateTranslation(libre_url, text, lang, dest) { - const search_params = libre_url.searchParams; - if (search_params.get("lwt_translator") != "libretranslate") { - throw 'Translation API not supported: ' + - search_params.get("lwt_translator") + "!"; - } - let translator_ajax; - if (search_params.get("lwt_translator_ajax")) { - translator_ajax = decodeURIComponent(search_params.get("lwt_translator_ajax")); - } else { - translator_ajax = libre_url.toString().replace(libre_url.search, '') + "translate"; - } - return getLibreTranslateTranslationBase( - text, lang, dest, key=search_params.get("lwt_key"), translator_ajax - ); -} \ No newline at end of file +async function getLibreTranslateTranslation (libre_url, text, lang, dest) { + const search_params = libre_url.searchParams; + if (search_params.get('lwt_translator') != 'libretranslate') { + throw 'Translation API not supported: ' + + search_params.get('lwt_translator') + '!'; + } + let translator_ajax; + if (search_params.get('lwt_translator_ajax')) { + translator_ajax = decodeURIComponent(search_params.get('lwt_translator_ajax')); + } else { + translator_ajax = libre_url.toString().replace(libre_url.search, '') + 'translate'; + } + return getLibreTranslateTranslationBase( + text, lang, dest, key = search_params.get('lwt_key'), translator_ajax + ); +} diff --git a/src/js/unloadformcheck.js b/src/js/unloadformcheck.js index 92469fc8..8a8648f5 100644 --- a/src/js/unloadformcheck.js +++ b/src/js/unloadformcheck.js @@ -1,6 +1,6 @@ /** * Check for unsaved changes when unloading window. - * + * * @license unlicense * @author andreask7 * @since 1.6.16-fork @@ -10,9 +10,9 @@ */ /** - * Set to 1 if a form was altered (set "dirty"), + * Set to 1 if a form was altered (set "dirty"), * ask for confirmation before leaving. - * + * * @deprecated Since 2.10.0, use lwt_form_check instead */ var DIRTY = 0; @@ -22,114 +22,114 @@ var DIRTY = 0; */ const lwt_form_check = { - dirty: false, + dirty: false, - /** - * Check the DIRTY status and ask before leaving. - * - * @returns {string} Confirmation string - */ - isDirtyMessage: function() { + /** + * Check the DIRTY status and ask before leaving. + * + * @returns {string} Confirmation string + */ + isDirtyMessage: function () { if (lwt_form_check.dirty) { return '** You have unsaved changes! **'; } - }, - - /** + }, + + /** * Set the DIRTY variable to 1. */ - makeDirty: function() { - lwt_form_check.dirty = true; - }, - - /** + makeDirty: function () { + lwt_form_check.dirty = true; + }, + + /** * Set the DIRTY variable to 0. */ - resetDirty: function() { - lwt_form_check.dirty = false; - }, - - /** + resetDirty: function () { + lwt_form_check.dirty = false; + }, + + /** * Set DIRTY to 1 if tag object changed. - * + * * @param {*} _ An event, unused * @param {object} ui UI object * @returns {true} Always return true */ - tagChanged: function(_, ui) { + tagChanged: function (_, ui) { if (!ui.duringInitialization) { - lwt_form_check.dirty = true; + lwt_form_check.dirty = true; } return true; - }, - - /** - * Call this function if you want to ask the user + }, + + /** + * Call this function if you want to ask the user * before exiting the form. - * + * * @returns {undefined} */ - askBeforeExit: function() { + askBeforeExit: function () { $('#termtags').tagit({ - afterTagAdded: lwt_form_check.tagChanged, + afterTagAdded: lwt_form_check.tagChanged, afterTagRemoved: lwt_form_check.tagChanged }); $('#texttags').tagit({ - afterTagAdded: lwt_form_check.tagChanged, + afterTagAdded: lwt_form_check.tagChanged, afterTagRemoved: lwt_form_check.tagChanged - }); + }); $('input,checkbox,textarea,radio,select') .not('#quickmenu').on('change', lwt_form_check.makeDirty); $(':reset,:submit').on('click', lwt_form_check.resetDirty); $(window).on('beforeunload', lwt_form_check.isDirtyMessage); - } - }; - + } +}; + /** * Check the DIRTY status and ask before leaving. - * + * * @returns {string} Confirmation string * @deprecated Since 2.10.0, use return lwt_form_check.isDirtyMessage instead */ -function askConfirmIfDirty() { - return lwt_form_check.askConfirmIfDirty(); +function askConfirmIfDirty () { + return lwt_form_check.askConfirmIfDirty(); } /** * Set the DIRTY variable to 1. * @deprecated Since 2.10.0, use return lwt_form_check.makeDirty instead */ -function makeDirty() { - return lwt_form_check.makeDirty(); +function makeDirty () { + return lwt_form_check.makeDirty(); } /** * Set the DIRTY variable to 0. * @deprecated Since 2.10.0, use return lwt_form_check.resetDirty instead */ -function resetDirty() { - return lwt_form_check.resetDirty(); +function resetDirty () { + return lwt_form_check.resetDirty(); } /** * Set DIRTY to 1 if tag object changed. - * + * * @param {*} _ An event, unused * @param {object} ui UI object * @returns {true} Always return true * @deprecated Since 2.10.0, use return lwt_form_check.tagChanged instead */ -function tagChanged(_, ui) { - return lwt_form_check.tagChanged(_, ui); +function tagChanged (_, ui) { + return lwt_form_check.tagChanged(_, ui); } /** - * Call this function if you want to ask the user + * Call this function if you want to ask the user * before exiting the form. - * + * * @returns {undefined} * @deprecated Since 2.10.0, use return lwt_form_check.askBeforeExit instead */ -function ask_before_exiting() { - return lwt_form_check.askBeforeExiting(); -} +function ask_before_exiting () { + return lwt_form_check.askBeforeExiting(); +} diff --git a/src/js/user_interactions.js b/src/js/user_interactions.js index fba30c70..a455e470 100644 --- a/src/js/user_interactions.js +++ b/src/js/user_interactions.js @@ -1,6 +1,6 @@ /** * General file to control dynamic interactions with the user. - * + * * @author HugoFara * @license Unlicense * @since 2.0.3-fork @@ -9,354 +9,344 @@ /** * Redirect the user to a specific page depending on the value */ -function quickMenuRedirection(value) { - const qm = document.getElementById('quickmenu'); - qm.selectedIndex = 0; - if (value == '') - return; - if (value == 'INFO') { - top.location.href = 'docs/info.html'; - } else if (value == 'rss_import') { - top.location.href = 'do_feeds.php?check_autoupdate=1'; - } else { - top.location.href = value + '.php'; - } +function quickMenuRedirection (value) { + const qm = document.getElementById('quickmenu'); + qm.selectedIndex = 0; + if (value == '') { return; } + if (value == 'INFO') { + top.location.href = 'docs/info.html'; + } else if (value == 'rss_import') { + top.location.href = 'do_feeds.php?check_autoupdate=1'; + } else { + top.location.href = value + '.php'; + } } /** * Create an interactable to add a new expression. - * + * * WARNING! This function was not properly tested! - * - * @param {string[]} text An array of words forming the expression - * @param {string} attrs A group of attributes to add + * + * @param {string[]} text An array of words forming the expression + * @param {string} attrs A group of attributes to add * @param {int} length Number of words, should correspond to WoWordCount * @param {string} hex Lowercase formatted version of the text. * @param {bool} showallwords true: multi-word is a superscript, show mw index + words * false: only show the multiword, hide the words * @returns {undefined} - * - * @since 2.5.2-fork Don't hide multi-word index when inserting new multi-word. + * + * @since 2.5.2-fork Don't hide multi-word index when inserting new multi-word. */ -function newExpressionInteractable(text, attrs, length, hex, showallwords) { +function newExpressionInteractable (text, attrs, length, hex, showallwords) { + const context = window.parent.document; + // From each multi-word group + for (key in text) { + // Remove any previous multi-word of same length + same position + $('#ID-' + key + '-' + length, context).remove(); - const context = window.parent.document; - // From each multi-word group - for (key in text) { - // Remove any previous multi-word of same length + same position - $('#ID-' + key + '-' + length, context).remove(); - - // From text, select the first mword smaller than this one, or the first - // word in this mword - let next_term_key = ''; - for (let j = length - 1; j > 0; j--) { - if (j == 1) - next_term_key = '#ID-' + key + '-1'; - if ($('#ID-' + key + '-' + j, context).length) { - next_term_key = '#ID-' + key + '-' + j; - break; - } - } - // Add the multi-word marker before - $(next_term_key, context) - .before( - '' + text[key] + + // From text, select the first mword smaller than this one, or the first + // word in this mword + let next_term_key = ''; + for (let j = length - 1; j > 0; j--) { + if (j == 1) { next_term_key = '#ID-' + key + '-1'; } + if ($('#ID-' + key + '-' + j, context).length) { + next_term_key = '#ID-' + key + '-' + j; + break; + } + } + // Add the multi-word marker before + $(next_term_key, context) + .before( + '' + text[key] + '' - ); + ); - // Change multi-word properties - const multi_word = $('#ID-' + key + '-' + length, context); - multi_word.addClass('order' + key).attr('data_order', key); - const txt = multi_word - .nextUntil( - $('#ID-' + (parseInt(key) + length * 2 - 1) + '-1', context), - '[id$="-1"]' - ) - .map(function() { - return $(this).text(); - }) - .get().join(""); - const pos = $('#ID-' + key + '-1', context).attr('data_pos'); - multi_word.attr('data_text', txt).attr('data_pos', pos); + // Change multi-word properties + const multi_word = $('#ID-' + key + '-' + length, context); + multi_word.addClass('order' + key).attr('data_order', key); + const txt = multi_word + .nextUntil( + $('#ID-' + (parseInt(key) + length * 2 - 1) + '-1', context), + '[id$="-1"]' + ) + .map(function () { + return $(this).text(); + }) + .get().join(''); + const pos = $('#ID-' + key + '-1', context).attr('data_pos'); + multi_word.attr('data_text', txt).attr('data_pos', pos); - // Hide the next words if necessary - if (showallwords) { - return; - } - const next_words = []; - // TODO: overlapsing multi-words - for (let i = 0; i < length * 2 - 1; i++) { - next_words.push('span[id="ID-' + (parseInt(key) + i) + '-1"]'); - } - $(next_words.join(','), context).hide(); + // Hide the next words if necessary + if (showallwords) { + return; } + const next_words = []; + // TODO: overlapsing multi-words + for (let i = 0; i < length * 2 - 1; i++) { + next_words.push('span[id="ID-' + (parseInt(key) + i) + '-1"]'); + } + $(next_words.join(','), context).hide(); + } } - -/** +/** * Scroll to a specific reading position - * + * * @since 2.0.3-fork */ -function goToLastPosition() { - // Last registered position to go to - const lookPos = LWT_DATA.text.reading_position; - // Position to scroll to - let pos = 0; - if (lookPos > 0) { - let posObj = $(".wsty[data_pos=" + lookPos + "]").not(".hide").eq(0); - if (posObj.attr("data_pos") === undefined) { - pos = $(".wsty").not(".hide").filter(function() { - return $(this).attr("data_pos") <= lookPos; - }).eq(-1); - } +function goToLastPosition () { + // Last registered position to go to + const lookPos = LWT_DATA.text.reading_position; + // Position to scroll to + let pos = 0; + if (lookPos > 0) { + const posObj = $('.wsty[data_pos=' + lookPos + ']').not('.hide').eq(0); + if (posObj.attr('data_pos') === undefined) { + pos = $('.wsty').not('.hide').filter(function () { + return $(this).attr('data_pos') <= lookPos; + }).eq(-1); } - $(document).scrollTo(pos); - focus(); - setTimeout(overlib, 10); - setTimeout(cClick, 100); + } + $(document).scrollTo(pos); + focus(); + setTimeout(overlib, 10); + setTimeout(cClick, 100); } - /** * Save the current reading position. - * + * * @param {int} text_id Text id * @param {int} position Position to save - * + * * @since 2.9.0-fork */ -function saveReadingPosition(text_id, position) { - $.post( - 'api.php/v1/texts/' + text_id + '/reading-position', - { position: position } - ); +function saveReadingPosition (text_id, position) { + $.post( + 'api.php/v1/texts/' + text_id + '/reading-position', + { position: position } + ); } - /** * Save audio position */ -function saveAudioPosition(text_id, pos) { - $.post( - 'api.php/v1/texts/' + text_id + '/audio-position', - { position: pos } - ); +function saveAudioPosition (text_id, pos) { + $.post( + 'api.php/v1/texts/' + text_id + '/audio-position', + { position: pos } + ); } /** * Get the phonetic version of a text. - * + * * @param {string} text Text to convert to phonetics. * @param {string} lang Language, either two letters code or four letters (BCP 47). - * + * * @deprecated Since 2.10.0 use getPhoneticTextAsync */ -function getPhoneticText(text, lang) { - let phoneticText; - $.ajax( - 'api.php/v1/phonetic-reading', - { - async: false, - data: { - text: text, - lang: lang - }, - dataType: "json", - type: "GET", - } - ) +function getPhoneticText (text, lang) { + let phoneticText; + $.ajax( + 'api.php/v1/phonetic-reading', + { + async: false, + data: { + text: text, + lang: lang + }, + dataType: 'json', + type: 'GET' + } + ) .done( - function (data) { - phoneticText = data["phonetic_reading"]; - } + function (data) { + phoneticText = data.phonetic_reading; + } ); - return phoneticText; + return phoneticText; } /** * Get the phonetic version of a text, asynchronous. - * + * * @param {string} text Text to convert to phonetics. * @param {string} lang Language, either two letters code or four letters (BCP 47) */ -async function getPhoneticTextAsync(text, lang) { - return $.getJSON( - 'api.php/v1/phonetic-reading', - { - text: text, - lang: lang - } - ); +async function getPhoneticTextAsync (text, lang) { + return $.getJSON( + 'api.php/v1/phonetic-reading', + { + text: text, + lang: lang + } + ); } - /** * Replace any searchValue on object value by replaceValue with deepth. - * + * * @param {dict} obj Object to search in * @param {string} searchValue Value to find * @param {string} replaceValue Value to replace with * */ -function deepReplace(obj, searchValue, replaceValue) { - for (let key in obj) { - if (typeof obj[key] === 'object') { - // Recursively search nested objects - deepReplace(obj[key], searchValue, replaceValue); - } else if (typeof obj[key] === 'string' && obj[key].includes(searchValue)) { - // If the property is a string and contains the searchValue, replace it - obj[key] = obj[key].replace(searchValue, replaceValue); - } +function deepReplace (obj, searchValue, replaceValue) { + for (let key in obj) { + if (typeof obj[key] === 'object') { + // Recursively search nested objects + deepReplace(obj[key], searchValue, replaceValue); + } else if (typeof obj[key] === 'string' && obj[key].includes(searchValue)) { + // If the property is a string and contains the searchValue, replace it + obj[key] = obj[key].replace(searchValue, replaceValue); } } - +} + /** * Find the first string starting with searchValue in object. - * + * * @param {dict} obj Object to search in * @param {string} searchValue Value to search */ -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; - } - } +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; // Return null if no matching string is found + } + return null; // Return null if no matching string is found } +function readTextWithExternal (text, voice_api, lang) { + const fetchRequest = JSON.parse(voice_api); -function readTextWithExternal(text, voice_api, lang) { - let fetchRequest = JSON.parse(voice_api); + // TODO: can expose more vars to Request + deepReplace(fetchRequest, 'lwt_term', text) + deepReplace(fetchRequest, 'lwt_lang', lang) - // TODO: can expose more vars to Request - deepReplace(fetchRequest, 'lwt_term', text) - deepReplace(fetchRequest, 'lwt_lang', lang) + fetchRequest.options.body = JSON.stringify(fetchRequest.options.body) - - fetchRequest.options.body = JSON.stringify(fetchRequest.options.body) - - fetch(fetchRequest.input, fetchRequest.options) + fetch(fetchRequest.input, fetchRequest.options) .then(response => response.json()) .then(data => { - const encodeString = deepFindValue(data, 'data:') - const utter = new Audio(encodeString) - utter.play() + const encodeString = deepFindValue(data, 'data:') + const utter = new Audio(encodeString) + utter.play() }) .catch(error => { - console.error(error) + console.error(error) }); } -function cookieTTSSettings(language) { - const prefix = 'tts[' + language; - let lang_settings = {}; - const num_vals = ['Rate', 'Pitch']; - const cookies = ['Rate', 'Pitch', 'Voice']; - let cookie_val; - for (let cook in cookies) { - cookie_val = getCookie(prefix + cook + ']'); - if (cookie_val) { - if (num_vals.includes(cook)) { - lang_settings[cook.toLowerCase()] = parseFloat(cookie_val); - } else { - lang_settings[cook.toLowerCase()] = cookie_val; - } - } +function cookieTTSSettings (language) { + const prefix = 'tts[' + language; + const lang_settings = {}; + const num_vals = ['Rate', 'Pitch']; + const cookies = ['Rate', 'Pitch', 'Voice']; + let cookie_val; + for (const cook in cookies) { + cookie_val = getCookie(prefix + cook + ']'); + if (cookie_val) { + if (num_vals.includes(cook)) { + lang_settings[cook.toLowerCase()] = parseFloat(cookie_val); + } else { + lang_settings[cook.toLowerCase()] = cookie_val; + } } - return lang_settings; -} + } + return lang_settings; +} /** * Read a text aloud, works with a phonetic version only. - * + * * @param {string} text Text to read, won't be parsed further. - * @param {string} lang Language code with BCP 47 convention - * (e. g. "en-US" for English with an American accent) - * @param {number} rate Reading rate - * @param {number} pitch Pitch value - * + * @param {string} lang Language code with BCP 47 convention + * (e. g. "en-US" for English with an American accent) + * @param {number} rate Reading rate + * @param {number} pitch Pitch value + * * @return {SpeechSynthesisUtterance} The spoken message object - * + * * @since 2.9.0 Accepts "voice" as a new optional argument */ - function readRawTextAloud(text, lang, rate, pitch, voice) { - let msg = new SpeechSynthesisUtterance(); - const tts_settings = cookieTTSSettings(lang.substring(0, 2)); - msg.text = text; - if (lang) { - msg.lang = lang; - } - // Voice is a string but we have to assign a SpeechSynthesysVoice - const useVoice = voice || tts_settings.voice; - if (useVoice) { - const voices = window.speechSynthesis.getVoices(); - for (let i = 0; i < voices.length; i++) { - if (voices[i].name === useVoice) { - msg.voice = voices[i]; - } - } - } - if (rate) { - msg.rate = rate; - } else if (tts_settings.rate) { - msg.rate = tts_settings.rate; - } - if (pitch) { - msg.pitch = pitch; - } else if (tts_settings.pitch) { - msg.pitch = tts_settings.pitch; +function readRawTextAloud (text, lang, rate, pitch, voice) { + const msg = new SpeechSynthesisUtterance(); + const tts_settings = cookieTTSSettings(lang.substring(0, 2)); + msg.text = text; + if (lang) { + msg.lang = lang; + } + // Voice is a string but we have to assign a SpeechSynthesysVoice + const useVoice = voice || tts_settings.voice; + if (useVoice) { + const voices = window.speechSynthesis.getVoices(); + for (let i = 0; i < voices.length; i++) { + if (voices[i].name === useVoice) { + msg.voice = voices[i]; + } } - window.speechSynthesis.speak(msg); - return msg; + } + if (rate) { + msg.rate = rate; + } else if (tts_settings.rate) { + msg.rate = tts_settings.rate; + } + if (pitch) { + msg.pitch = pitch; + } else if (tts_settings.pitch) { + msg.pitch = tts_settings.pitch; + } + window.speechSynthesis.speak(msg); + return msg; } /** * Read a text aloud, may parse the text to get a phonetic version. - * + * * @param {string} text Text to read, do not need to be phonetic - * @param {string} lang Language code with BCP 47 convention - * (e. g. "en-US" for English with an American accent) - * @param {number} rate Reading rate + * @param {string} lang Language code with BCP 47 convention + * (e. g. "en-US" for English with an American accent) + * @param {number} rate Reading rate * @param {number} pitch Pitch value * @param {string} voice Optional voice, the result will depend on the browser used - * + * * @since 2.9.0 Accepts "voice" as a new optional argument */ -function readTextAloud(text, lang, rate, pitch, voice, convert_to_phonetic) { - if (convert_to_phonetic) { - getPhoneticTextAsync(text, lang) - .then( - function (data) { - readRawTextAloud( - data.phonetic_reading, lang, rate, pitch, voice - ); - } - ); - } else { - readRawTextAloud(text, lang, rate, pitch, voice); - } +function readTextAloud (text, lang, rate, pitch, voice, convert_to_phonetic) { + if (convert_to_phonetic) { + getPhoneticTextAsync(text, lang) + .then( + function (data) { + readRawTextAloud( + data.phonetic_reading, lang, rate, pitch, voice + ); + } + ); + } else { + readRawTextAloud(text, lang, rate, pitch, voice); + } } - -function speechDispatcher(term, lang_abbr, lang_data) { - const loc_data = lang_data ?? LWT_DATA.language; - const loc_lang_abbr = lang_abbr ?? loc_data.abbr; - if (loc_data.ttsVoiceApi) { - readTextWithExternal(term, loc_data.ttsVoiceApi, loc_lang_abbr); - } else { - const convert_to_phonetic = loc_data.word_parsing == 'mecab'; - const lang_settings = cookieTTSSettings(loc_lang_abbr.substring(0, 2)); - readTextAloud( - term, loc_lang_abbr, lang_settings.rate, lang_settings.pitch, - lang_settings.voice, convert_to_phonetic - ); - } +function speechDispatcher (term, lang_abbr, lang_data) { + const loc_data = lang_data ?? LWT_DATA.language; + const loc_lang_abbr = lang_abbr ?? loc_data.abbr; + if (loc_data.ttsVoiceApi) { + readTextWithExternal(term, loc_data.ttsVoiceApi, loc_lang_abbr); + } else { + const convert_to_phonetic = loc_data.word_parsing == 'mecab'; + const lang_settings = cookieTTSSettings(loc_lang_abbr.substring(0, 2)); + readTextAloud( + term, loc_lang_abbr, lang_settings.rate, lang_settings.pitch, + lang_settings.voice, convert_to_phonetic + ); + } } From cf87044069713a5080d3345fda55fbfa286f5e50 Mon Sep 17 00:00:00 2001 From: HugoFara Date: Wed, 3 Jan 2024 20:16:02 +0100 Subject: [PATCH 2/3] Missing function, fixes style. --- js/pgm.js | 6 +-- src/js/unloadformcheck.js | 80 +++++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/js/pgm.js b/js/pgm.js index 5095a73f..863a8c21 100644 --- a/js/pgm.js +++ b/js/pgm.js @@ -408,15 +408,15 @@ return getLibreTranslateTranslationBase(text,lang,dest,key=search_params.get('lw * @author andreask7 * @since 1.6.16-fork * @since 2.3.1-fork You should not only include this script to check before unload - * but also call ask_before_exiting once. + * but also call ask_before_exiting once. * @since 2.10.0-fork This file was refactored in a single object, use it instead */ -var DIRTY=0;const lwt_form_check={dirty:!1,isDirtyMessage:function(){if(lwt_form_check.dirty){return'** You have unsaved changes! **'}},makeDirty:function(){lwt_form_check.dirty=!0},resetDirty:function(){lwt_form_check.dirty=!1},tagChanged:function(_,ui){if(!ui.duringInitialization){lwt_form_check.dirty=!0} +let DIRTY=0;const lwt_form_check={dirty:!1,isDirtyMessage:function(){if(lwt_form_check.dirty){return'** You have unsaved changes! **'}},makeDirty:function(){lwt_form_check.dirty=!0},resetDirty:function(){lwt_form_check.dirty=!1},tagChanged:function(_,ui){if(!ui.duringInitialization){lwt_form_check.dirty=!0} return!0},askBeforeExit:function(){$('#termtags').tagit({afterTagAdded:lwt_form_check.tagChanged,afterTagRemoved:lwt_form_check.tagChanged});$('#texttags').tagit({afterTagAdded:lwt_form_check.tagChanged,afterTagRemoved:lwt_form_check.tagChanged});$('input,checkbox,textarea,radio,select').not('#quickmenu').on('change',lwt_form_check.makeDirty);$(':reset,:submit').on('click',lwt_form_check.resetDirty);$(window).on('beforeunload',lwt_form_check.isDirtyMessage)}};function askConfirmIfDirty(){return lwt_form_check.askConfirmIfDirty()} function makeDirty(){return lwt_form_check.makeDirty()} function resetDirty(){return lwt_form_check.resetDirty()} function tagChanged(_,ui){return lwt_form_check.tagChanged(_,ui)} -function ask_before_exiting(){return lwt_form_check.askBeforeExiting()};var stIsIE=!1;sorttable={init:function(){if(arguments.callee.done)return;arguments.callee.done=!0;if(_timer)clearInterval(_timer);if(!document.createElement||!document.getElementsByTagName)return;sorttable.DATE_RE=/^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;forEach(document.getElementsByTagName('table'),function(table){if(table.className.search(/\bsortable\b/)!=-1){sorttable.makeSortable(table)}})},makeSortable:function(table){if(table.getElementsByTagName('thead').length==0){the=document.createElement('thead');the.appendChild(table.rows[0]);table.insertBefore(the,table.firstChild)} +function ask_before_exiting(){return lwt_form_check.askBeforeExit()};var stIsIE=!1;sorttable={init:function(){if(arguments.callee.done)return;arguments.callee.done=!0;if(_timer)clearInterval(_timer);if(!document.createElement||!document.getElementsByTagName)return;sorttable.DATE_RE=/^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;forEach(document.getElementsByTagName('table'),function(table){if(table.className.search(/\bsortable\b/)!=-1){sorttable.makeSortable(table)}})},makeSortable:function(table){if(table.getElementsByTagName('thead').length==0){the=document.createElement('thead');the.appendChild(table.rows[0]);table.insertBefore(the,table.firstChild)} if(table.tHead==null)table.tHead=table.getElementsByTagName('thead')[0];if(table.tHead.rows.length!=1)return;sortbottomrows=[];for(var i=0;i * @since 1.6.16-fork * @since 2.3.1-fork You should not only include this script to check before unload - * but also call ask_before_exiting once. + * but also call ask_before_exiting once. * @since 2.10.0-fork This file was refactored in a single object, use it instead */ @@ -15,7 +15,7 @@ * * @deprecated Since 2.10.0, use lwt_form_check instead */ -var DIRTY = 0; +let DIRTY = 0; /** * Keeps track of a modified form. @@ -25,63 +25,63 @@ const lwt_form_check = { dirty: false, /** - * Check the DIRTY status and ask before leaving. - * - * @returns {string} Confirmation string - */ + * Check the DIRTY status and ask before leaving. + * + * @returns {string} Confirmation string + */ isDirtyMessage: function () { - if (lwt_form_check.dirty) { - return '** You have unsaved changes! **'; - } + if (lwt_form_check.dirty) { + return '** You have unsaved changes! **'; + } }, /** - * Set the DIRTY variable to 1. - */ + * Set the DIRTY variable to 1. + */ makeDirty: function () { lwt_form_check.dirty = true; }, /** - * Set the DIRTY variable to 0. - */ + * Set the DIRTY variable to 0. + */ resetDirty: function () { lwt_form_check.dirty = false; }, /** - * Set DIRTY to 1 if tag object changed. - * - * @param {*} _ An event, unused - * @param {object} ui UI object - * @returns {true} Always return true - */ + * Set DIRTY to 1 if tag object changed. + * + * @param {*} _ An event, unused + * @param {object} ui UI object + * @returns {true} Always return true + */ tagChanged: function (_, ui) { - if (!ui.duringInitialization) { + if (!ui.duringInitialization) { lwt_form_check.dirty = true; - } - return true; + } + return true; }, /** - * Call this function if you want to ask the user - * before exiting the form. - * - * @returns {undefined} - */ + * Call this function if you want to ask the user + * before exiting the form. + * + * @returns {undefined} + */ askBeforeExit: function () { - $('#termtags').tagit({ - afterTagAdded: lwt_form_check.tagChanged, - afterTagRemoved: lwt_form_check.tagChanged - }); - $('#texttags').tagit({ - afterTagAdded: lwt_form_check.tagChanged, - afterTagRemoved: lwt_form_check.tagChanged - }); - $('input,checkbox,textarea,radio,select') - .not('#quickmenu').on('change', lwt_form_check.makeDirty); - $(':reset,:submit').on('click', lwt_form_check.resetDirty); - $(window).on('beforeunload', lwt_form_check.isDirtyMessage); + $('#termtags').tagit({ + afterTagAdded: lwt_form_check.tagChanged, + afterTagRemoved: lwt_form_check.tagChanged + }); + $('#texttags').tagit({ + afterTagAdded: lwt_form_check.tagChanged, + afterTagRemoved: lwt_form_check.tagChanged + }); + $('input,checkbox,textarea,radio,select') + .not('#quickmenu').on('change', lwt_form_check.makeDirty); + $(':reset,:submit').on('click', lwt_form_check.resetDirty); + $(window).on('beforeunload', lwt_form_check.isDirtyMessage); } }; @@ -131,5 +131,5 @@ function tagChanged (_, ui) { * @deprecated Since 2.10.0, use return lwt_form_check.askBeforeExit instead */ function ask_before_exiting () { - return lwt_form_check.askBeforeExiting(); + return lwt_form_check.askBeforeExit(); } From af20ce99cf5327ee409592c2ac81eeef0a0565f7 Mon Sep 17 00:00:00 2001 From: HugoFara Date: Wed, 3 Jan 2024 20:18:42 +0100 Subject: [PATCH 3/3] Renames lwt_form_check to lwtFormCheck. --- check_text.php | 2 +- docs/CHANGELOG.md | 4 ++-- docs/info.html | 4 ++-- edit_archivedtexts.php | 4 ++-- edit_languages.php | 6 ++--- edit_mword.php | 4 ++-- edit_tags.php | 8 +++---- edit_texts.php | 4 ++-- edit_texttags.php | 8 +++---- edit_tword.php | 2 +- edit_word.php | 2 +- edit_words.php | 8 +++---- js/pgm.js | 20 ++++++++-------- long_text_import.php | 12 +++++----- new_word.php | 2 +- settings.php | 6 ++--- src/js/jq_pgm.js | 4 ++-- src/js/translation_api.js | 8 +++---- src/js/unloadformcheck.js | 46 ++++++++++++++++++------------------- text_to_speech_settings.php | 2 +- 20 files changed, 78 insertions(+), 78 deletions(-) diff --git a/check_text.php b/check_text.php index 28c43d6b..1613f8f2 100644 --- a/check_text.php +++ b/check_text.php @@ -78,7 +78,7 @@ function change_textboxes_language() { $('#TxText').attr('lang', language_data[lid]); } - $(document).ready(lwt_form_check.askBeforeExit); + $(document).ready(lwtFormCheck.askBeforeExit); $(document).ready(change_textboxes_language);
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9626a1db..b20c9e50 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,7 +15,7 @@ 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`. - * `unloadformcheck.js` now declares a new object `lwt_form_check` that contains + * `unloadformcheck.js` now declares a new object `lwtFormCheck` that contains all the functions needed. * Important additions to Text-To-Speech (TTS): * Word can be read on hover or on click. @@ -96,7 +96,7 @@ missing in `INSTALL.sh` (and `composer.json`), as stated in ### Deprecated -* Using any function from `unloadformcheck.js` without using `lwt_form_check` is deprecated. +* Using any function from `unloadformcheck.js` without using `lwtFormCheck` is deprecated. * `get_database_prefixes` is deprecated, superseded by `getDatabasePrefix` which is much cleaner. * Globals defined in `jq_pgm.js` are going into a single global `LWT_DATA`. diff --git a/docs/info.html b/docs/info.html index 65af1a92..011ac405 100644 --- a/docs/info.html +++ b/docs/info.html @@ -2231,7 +2231,7 @@

Added

  • src/js/jq_pgm.js: LWT_DATA.
  • -unloadformcheck.js now declares a new object lwt_form_check that contains +unloadformcheck.js now declares a new object lwtFormCheck that contains all the functions needed.
  • @@ -2338,7 +2338,7 @@

    Fixed

    Deprecated