1"use strict"; 2// Code written by Manheart Earthman=B. A. Bilgekılınç Topraksoy=忬 æºä¸å夫åå¿ 3// This file MAY NOT BE MODIFIED WITHOUT CONSENT i.e. OFFICIAL AUTHORIZATION 4 5/*-----*/ 6// TYPE1: Generic info box » Watch how 1 of 2 possible texts is injected into the button 7 8// Function that creates a div for [special case/unique feature/untranslatable thing] info NOTIFICATIONS 9/* VARIABLES AND CONSTANTS*/ 10const putNotificationTxtIntoThisP1 = document.createElement("P"); 11const putNotificationTxtIntoThisP2 = document.createElement("P"); 12const putNotificationTxtIntoThisP3 = document.createElement("P"); 13const okButtonToCloseInfoBoxType1 = document.createElement("DIV"); 14const okButtonToCloseInfoBoxType1AmidLesson = document.createElement("DIV"); 15const okButtonToCloseInfoBoxType1BeforeSpeechRecognition = document.createElement("DIV"); 16// Put something like [OK], [Got it], [I see], [Oh really?], [Wow], [That's interesting] etc into the button. 17let okTexts = "✔|✔"; // Default content of the OK box is a "tick â" mark to be shown in case fetch fails 18okButtonToCloseInfoBoxType1.innerHTML = okTexts.split("|")[0]; 19okButtonToCloseInfoBoxType1AmidLesson.innerHTML = okTexts.split("|")[0]; 20okButtonToCloseInfoBoxType1BeforeSpeechRecognition.innerHTML = okTexts.split("|")[0]; 21const pathOfOkCloseTheBox = "/user_interface/text/"+userInterfaceLanguage+"/0lesson-ok_i_understand.txt"; 22fetch(pathOfOkCloseTheBox,myHeaders).then(function(response){return response.text();}).then(function(contentOfTheTxtFile){ okTexts=contentOfTheTxtFile; assignOKButtonText(); }); 23function assignOKButtonText() { 24 if(Math.random()<0.5) { // Heads or tails 25 okButtonToCloseInfoBoxType1.innerHTML = okTexts.split("|")[0]; 26 okButtonToCloseInfoBoxType1AmidLesson.innerHTML = okTexts.split("|")[1]; 27 okButtonToCloseInfoBoxType1BeforeSpeechRecognition.innerHTML = okTexts.split("|")[0]; 28 } else { 29 okButtonToCloseInfoBoxType1.innerHTML = okTexts.split("|")[1]; 30 okButtonToCloseInfoBoxType1AmidLesson.innerHTML = okTexts.split("|")[0]; 31 okButtonToCloseInfoBoxType1BeforeSpeechRecognition.innerHTML = okTexts.split("|")[0]; 32 } 33} 34let popUpNotificationType1Sound; 35let dismissNotificationType1Sound; 36window.addEventListener("DOMContentLoaded",loadNotificationType1Sounds,{once:true}); 37function loadNotificationType1Sounds() { 38 // soundFileFormat exists in js_for_all_iframed_lesson_htmls where it is copied from the parent in js_for_different_browsers_and_devices 39 popUpNotificationType1Sound = new parent.Howl({ src: ["/user_interface/sounds/notification1_appear."+soundFileFormat] }); 40 dismissNotificationType1Sound = new parent.Howl({ src: ["/user_interface/sounds/notification1_close."+soundFileFormat] }); 41 // NO BIG DEAL IF: These sounds are loaded even if they are not used in the lesson 42} 43window.addEventListener("beforeunload",unloadNotificationType1Sounds,{once:true}); 44function unloadNotificationType1Sounds() { 45 popUpNotificationType1Sound.unload(); 46 dismissNotificationType1Sound.unload(); 47} 48 49/*Info box with only one [OK] button to show at the beginning of the lesson*/ 50function createAndHandleInfoBoxType1BeforeLessonStarts() { 51 popUpNotificationType1Sound.play(); 52 const notificationBoxContainer1 = document.createElement("DIV"); 53 notificationBoxContainer1.classList.add("notificationBG"); // See css_for_info_boxes_in_lessons 54 document.body.appendChild(notificationBoxContainer1); 55 const notificationBoxItself = document.createElement("DIV"); 56 notificationBoxItself.classList.add("notificationRoundedBox"); // See css_for_info_boxes_in_lessons 57 notificationBoxContainer1.appendChild(notificationBoxItself); 58 59 notificationBoxItself.appendChild(putNotificationTxtIntoThisP1); 60 61 okButtonToCloseInfoBoxType1.classList.add("okButtonUnderNotification"); // See css_for_info_boxes_in_lessons 62 if (needLatinFonts) { 63 okButtonToCloseInfoBoxType1.style.fontFamily = '"Oxanium SemiBold", sans-serif'; 64 notificationBoxItself.classList.add("textAlignJustifyLTR","latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 65 putNotificationTxtIntoThisP1.classList.add("latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 66 } 67 if (needHitoicJapaneseFonts) { 68 notificationBoxItself.classList.add("textAlignLeft","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 69 putNotificationTxtIntoThisP1.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 70 } 71 notificationBoxItself.appendChild(okButtonToCloseInfoBoxType1); 72 73 74 if (deviceDetector.isMobile) { okButtonToCloseInfoBoxType1.addEventListener("touchstart",okButtonIsClickedToStartLesson); } 75 else { okButtonToCloseInfoBoxType1.addEventListener("mousedown",okButtonIsClickedToStartLesson); } 76 function okButtonIsClickedToStartLesson(event) { event.preventDefault(); event.stopPropagation(); 77 dismissNotificationType1Sound.play();
78 notificationBoxContainer1.classList.add("addThisToAButtonForPlayStationStyleClick"); // See css_for_every_single_html_css 79 setTimeout(function () { notificationBoxContainer1.parentNode.removeChild(notificationBoxContainer1); },1000); // The animation completes in 600ms 80 if (typeof startTheLesson === "function") { 81 setTimeout(function () { startTheLesson(); }, 1500); 82 } else { parent.console.error("Error: createAndHandleInfoBoxType1BeforeLessonStarts() needs startTheLesson() function which doesn't exist???"); } 83 } 84} 85 86/*Info box with only one [OK] button to show in the middle or at the end of the lesson*/ 87function createAndHandleInfoBoxType1AmidLesson() { 88 popUpNotificationType1Sound.play(); 89 const notificationBoxContainer2 = document.createElement("DIV"); 90 notificationBoxContainer2.classList.add("notificationBG"); // See css_for_info_boxes_in_lessons 91 document.body.appendChild(notificationBoxContainer2); 92 const notificationBoxItself2 = document.createElement("DIV"); 93 notificationBoxItself2.classList.add("notificationRoundedBox"); // See css_for_info_boxes_in_lessons 94 notificationBoxContainer2.appendChild(notificationBoxItself2); 95 96 notificationBoxItself2.appendChild(putNotificationTxtIntoThisP2); 97 98 okButtonToCloseInfoBoxType1AmidLesson.classList.add("okButtonUnderNotification"); // See css_for_info_boxes_in_lessons 99 if (needLatinFonts) { 100 okButtonToCloseInfoBoxType1AmidLesson.style.fontFamily = '"Oxanium SemiBold", sans-serif'; 101 notificationBoxItself2.classList.add("textAlignJustifyLTR","latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 102 putNotificationTxtIntoThisP2.classList.add("latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 103 } 104 if (needHitoicJapaneseFonts) { 105 notificationBoxItself2.classList.add("textAlignLeft","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 106 putNotificationTxtIntoThisP2.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 107 } 108 notificationBoxItself2.appendChild(okButtonToCloseInfoBoxType1AmidLesson); 109 110 if (deviceDetector.isMobile) { okButtonToCloseInfoBoxType1AmidLesson.addEventListener("touchstart",okButtonIsClickedToContinueLesson); } 111 else { okButtonToCloseInfoBoxType1AmidLesson.addEventListener("mousedown",okButtonIsClickedToContinueLesson); } 112 function okButtonIsClickedToContinueLesson(event) { event.preventDefault(); event.stopPropagation(); 113 dismissNotificationType1Sound.play(); 114 notificationBoxContainer2.classList.add("addThisToAButtonForPlayStationStyleClick"); // See css_for_every_single_html_css 115 setTimeout(function () { notificationBoxContainer2.parentNode.removeChild(notificationBoxContainer2); },1000); // The animation completes in 600ms 116 if (typeof continueLesson === "function") { 117 setTimeout(function () { continueLesson(); }, 1500); 118 } else { parent.console.error("Error: createAndHandleInfoBoxType1AmidLesson() needs continueLesson() function which doesn't exist???"); } 119 } 120} 121 122/*Info box with only one [OK] button to show just before speech recognition*/ 123function createAndHandleInfoBoxType1JustBeforeSpeechRecognition() { 124 popUpNotificationType1Sound.play(); 125 const notificationBoxContainer3 = document.createElement("DIV"); 126 notificationBoxContainer3.classList.add("notificationBG"); // See css_for_info_boxes_in_lessons 127 document.body.appendChild(notificationBoxContainer3); 128 const notificationBoxItself3 = document.createElement("DIV"); 129 notificationBoxItself3.classList.add("notificationRoundedBox"); // See css_for_info_boxes_in_lessons 130 notificationBoxContainer3.appendChild(notificationBoxItself3); 131 132 notificationBoxItself3.appendChild(putNotificationTxtIntoThisP3); 133 134 okButtonToCloseInfoBoxType1BeforeSpeechRecognition.classList.add("okButtonUnderNotification"); // See css_for_info_boxes_in_lessons 135 if (needLatinFonts) { 136 okButtonToCloseInfoBoxType1BeforeSpeechRecognition.style.fontFamily = '"Oxanium SemiBold", sans-serif'; 137 notificationBoxItself3.classList.add("textAlignJustifyLTR","latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 138 putNotificationTxtIntoThisP3.classList.add("latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 139 } 140 if (needHitoicJapaneseFonts) { 141 notificationBoxItself3.classList.add("textAlignLeft","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 142 putNotificationTxtIntoThisP3.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 143 } 144 notificationBoxItself3.appendChild(okButtonToCloseInfoBoxType1BeforeSpeechRecognition); 145 146 if (deviceDetector.isMobile) { okButtonToCloseInfoBoxType1BeforeSpeechRecognition.addEventListener("touchstart",okButtonIsClickedToContinueLesson); } 147 else { okButtonToCloseInfoBoxType1BeforeSpeechRecognition.addEventListener("mousedown",okButtonIsClickedToContinueLesson); } 148 function okButtonIsClickedToContinueLesson(event) { event.preventDefault(); event.stopPropagation(); 149 dismissNotificationType1Sound.play();
150 notificationBoxContainer3.classList.add("addThisToAButtonForPlayStationStyleClick"); // See css_for_every_single_html_css 151 setTimeout(function () { notificationBoxContainer3.parentNode.removeChild(notificationBoxContainer3); },1000); // The animation completes in 600ms 152 if (typeof display_nowItsYourTurn_animation === "function") { 153 setTimeout(function () { display_nowItsYourTurn_animation(); }, 1500); 154 } else { parent.console.error("Error: createAndHandleInfoBoxType1JustBeforeSpeechRecognition() needs display_nowItsYourTurn_animation() function which doesn't exist???"); } 155 } 156} 157 158/*-----*/ 159// TYPE2 used to be the deprecated [choose input device] thingy. 160/*-----*/ 161 162/*-----*/ 163// TYPE3 LISTEN-MANY-TIMES-BOX. 164// Function that creates a div box for pronunciation (Hitonokaochan; formerly wavesurfer) NOTIFICATIONS 165/* VARIABLES AND CONSTANTS*/ 166const putVocabularyTxtIntoThisP1 = document.createElement("P"); const putVocabularyTxtIntoThisP1OUTRO = document.createElement("P"); 167const putVocabularyTxtIntoThisP2 = document.createElement("P"); const putVocabularyTxtIntoThisP2OUTRO = document.createElement("P"); 168let txtStringForP1 = null; let txtStringForP1OUTRO = null; 169let txtStringForP2 = null; let txtStringForP2OUTRO = null; 170function handleP1P2ActualText(receivedTxt) { // Called when fetch gets the file » See the lesson's own js 171 txtStringForP1=receivedTxt.split("|")[0]; putVocabularyTxtIntoThisP1.innerHTML= txtStringForP1; 172 txtStringForP2=receivedTxt.split("|")[1]; putVocabularyTxtIntoThisP2.innerHTML= txtStringForP2; 173} 174function handleP1P2ActualTextOUTRO(receivedTxt) { // Called when fetch gets the file » See the lesson's own js 175 txtStringForP1OUTRO=receivedTxt.split("|")[0]; putVocabularyTxtIntoThisP1OUTRO.innerHTML= txtStringForP1OUTRO; 176 txtStringForP2OUTRO=receivedTxt.split("|")[1]; putVocabularyTxtIntoThisP2OUTRO.innerHTML= txtStringForP2OUTRO; 177} 178const listenButtonOfTheVocabulary = document.createElement("DIV"); 179const startButtonToCloseTheVocabulary = document.createElement("DIV"); 180let listenButtonTxt, listenAgainButtonTxt, startButtonTxt, nextButtonTxt; // [Next] button replaces [Start] button if outro is enabled by the second parameter 181const listenBoxButton1Button2Path = "/user_interface/text/"+userInterfaceLanguage+"/0lesson-vocabulary_button1_button2.txt"; 182let primaryFetchIsStillTryingToGetTheFile = true; 183fetch(listenBoxButton1Button2Path,myHeaders).then(function(response){return response.text();}).then(function(contentOfTheTxtFile){ 184 listenButtonTxt = contentOfTheTxtFile.split("|")[0]; 185 listenAgainButtonTxt = contentOfTheTxtFile.split("|")[1]; 186 startButtonTxt = contentOfTheTxtFile.split("|")[2]; 187 nextButtonTxt = contentOfTheTxtFile.split("|")[3]; 188}).finally( ()=>{ primaryFetchIsStillTryingToGetTheFile = false; } ); 189// - 190let popUpVocabularySound; 191let dismissVocabularySound; 192window.addEventListener("DOMContentLoaded",loadVocabularyBoxButtonSounds,{once:true}); 193function loadVocabularyBoxButtonSounds() { 194 // soundFileFormat exists in js_for_all_iframed_lesson_htmls where it is copied from the parent in js_for_different_browsers_and_devices 195 popUpVocabularySound = new parent.Howl({ src: ["/user_interface/sounds/notification3_appear."+soundFileFormat] }); 196 dismissVocabularySound = new parent.Howl({ src: ["/user_interface/sounds/notification3_close."+soundFileFormat] }); 197 // NO BIG DEAL IF: These sounds are loaded even if they are not used in the lesson 198} 199window.addEventListener("beforeunload",unloadVocabularyBoxButtonSounds,{once:true}); 200function unloadVocabularyBoxButtonSounds() { 201 popUpVocabularySound.unload(); 202 dismissVocabularySound.unload(); 203} 204 205/*FUNCTION DECLARATION*/ 206// IN THE PAST WE SAID: IN THE FUTURE WE WILL HOPEFULLY USE RHUBARB LIP-SYNC TO PLAY AN ANIMATED MOUTH ILLUSTRATION ALONG WITH WAVESURFER 207// AND NOW THIS IS APRIL 2024: We have deprecated wavesurfer entirely and have replaced it with Hitonokaochan 208// See » https://github.com/DanielSWolf/rhubarb-lip-sync 209 210function createAndHandleListenManyTimesBox(pathToTheAudio1,pathToJSON1,pathToTheAudio2,pathToJSON2,pathToTheAudio3,pathToJSON3,isLessonOutro) { 211 popUpVocabularySound.play(); 212 const vocabularyBoxContainer = document.createElement("DIV"); // Maybe a dark theme will look nice 213 vocabularyBoxContainer.classList.add("vocabularyBG"); // See css_for_info_boxes_in_lessons 214 document.body.appendChild(vocabularyBoxContainer); 215 const vocabularyBoxItself = document.createElement("DIV"); 216 vocabularyBoxItself.classList.add("vocabularyRoundedBox"); // See css_for_info_boxes_in_lessons 217 if (needLatinFonts) { 218 vocabularyBoxItself.classList.add("textAlignJustifyLTR","latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 219 putVocabularyTxtIntoThisP1.classList.add("latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 220 putVocabularyTxtIntoThisP2.classList.add("latinLineHeightAndLetterSpacing"); // See css_for_every_single_html 221 putVocabularyTxtIntoThisP1OUTRO.classList.add("latinLineHeightAndLetterSpacing"); 222 putVocabularyTxtIntoThisP2OUTRO.classList.add("latinLineHeightAndLetterSpacing"); 223 } 224 if (needHitoicJapaneseFonts) { 225 vocabularyBoxItself.classList.add("textAlignLeft","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 226 putVocabularyTxtIntoThisP1.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 227 putVocabularyTxtIntoThisP2.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); // See css_for_every_single_html 228 putVocabularyTxtIntoThisP1OUTRO.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); 229 putVocabularyTxtIntoThisP2OUTRO.classList.add("toUseWBR_withCJK","cjkLineHeightAndLetterSpacing"); 230 } 231 vocabularyBoxContainer.appendChild(vocabularyBoxItself); 232 // 233 if (!txtStringForP1) { // If download has failed or not finished yet 234 putVocabularyTxtIntoThisP1.innerHTML="ð"; //ð 235 putVocabularyTxtIntoThisP2.innerHTML="â³"; //â³ 236 } 237 if (!txtStringForP1OUTRO) { // If download has failed or not finished yet 238 putVocabularyTxtIntoThisP1OUTRO.innerHTML="ð"; //ð 239 putVocabularyTxtIntoThisP2OUTRO.innerHTML="â³"; //â³ 240 } 241 242 // APPEND txt1 243 if (!isLessonOutro) { vocabularyBoxItself.appendChild(putVocabularyTxtIntoThisP1); } 244 else { vocabularyBoxItself.appendChild(putVocabularyTxtIntoThisP1OUTRO); } 245 246 // - 247 let introVocabulary1,introVocabulary2,introVocabulary3; let introSound1IsReady = false; let introSound2IsReady = false; let introSound3IsReady = false; let introSoundIsPlaying = false;
248 let outroVocabulary1,outroVocabulary2,outroVocabulary3; let outroSound1IsReady = false; let outroSound2IsReady = false; let outroSound3IsReady = false; let outroSoundIsPlaying = false; 249 if (!isLessonOutro) { 250 introVocabulary1 = new parent.Howl({ src: [pathToTheAudio1] }); 251 introVocabulary1.once('load', function(){ introSound1IsReady = true; getJSON(pathToJSON1,1); }); 252 introVocabulary1.on('end', function(){ introSoundIsPlaying = false; pseudoReactivationOfPlayButton(); }); 253 254 introVocabulary2 = new parent.Howl({ src: [pathToTheAudio2] }); 255 introVocabulary2.once('load', function(){ introSound2IsReady = true; getJSON(pathToJSON2,2); }); 256 introVocabulary2.on('end', function(){ introSoundIsPlaying = false; pseudoReactivationOfPlayButton(); }); 257 258 introVocabulary3 = new parent.Howl({ src: [pathToTheAudio3] }); 259 introVocabulary3.once('load', function(){ introSound3IsReady = true; getJSON(pathToJSON3,3); }); 260 introVocabulary3.on('end', function(){ introSoundIsPlaying = false; pseudoReactivationOfPlayButton(); }); 261 } 262 else { 263 outroVocabulary1 = new parent.Howl({ src: [pathToTheAudio1] }); 264 outroVocabulary1.once('load', function(){ outroSound1IsReady = true; getJSON(pathToJSON1,1); }); 265 outroVocabulary1.on('end', function(){ outroSoundIsPlaying = false; pseudoReactivationOfPlayButton(); }); 266 267 outroVocabulary2 = new parent.Howl({ src: [pathToTheAudio2] }); 268 outroVocabulary2.once('load', function(){ outroSound2IsReady = true; getJSON(pathToJSON2,2); }); 269 outroVocabulary2.on('end', function(){ outroSoundIsPlaying = false; pseudoReactivationOfPlayButton(); }); 270 271 outroVocabulary3 = new parent.Howl({ src: [pathToTheAudio3] }); 272 outroVocabulary3.once('load', function(){ outroSound3IsReady = true; getJSON(pathToJSON3,3); }); 273 outroVocabulary3.on('end', function(){ outroSoundIsPlaying = false; pseudoReactivationOfPlayButton(); }); 274 } 275 // - 276 window.addEventListener("beforeunload",unloadListenBoxVocabularySounds,{once:true}); 277 function unloadListenBoxVocabularySounds() { 278 if (introSound1IsReady) { introVocabulary1.unload(); } if (introSound2IsReady) { introVocabulary2.unload(); } if (introSound3IsReady) { introVocabulary3.unload(); } 279 if (outroSound1IsReady) { outroVocabulary1.unload(); } if (outroSound2IsReady) { outroVocabulary2.unload(); } if (outroSound3IsReady) { outroVocabulary3.unload(); } 280 } 281 // DEPRECATE: Get json file that is supposed to be named exactly the same as the audio file 282 // Deprecate: const theFileExtensionIsRemoved_1 = pathToTheAudio1.split(".")[0]; 283 // Deprecate: const jsonFilePath_1 = theFileExtensionIsRemoved_1 + ".json"; // Cached by 112.js 114.js etc in js_for_cache_handling 284 let lipSyncJSON_1 = null; let lipSyncJSON_2 = null; let lipSyncJSON_3 = null; 285 //INSTEAD OF STARTING HIDDEN let's show an animated «loading» text 286 pseudoDeactivationOfPlayButton(); //Same as » listenButtonOfTheVocabulary.style.opacity = "0.4"; // Low opacity until fetch gets json // Here used to be listenButtonOfTheVocabulary.style.visibility = "hidden"; // Its class is added down below 287 // NOTE THAT: Nothing will happen if user clicks|touches the button before the sounds are loaded because the playintroVocabulary functions will check if sound is loaded before trying to play 288 let to1,to2,to3,to4,to5,to6; 289 function getJSON(jsonFilePath,whichOne) { // Will fire only if the sound has successfully loaded 290 fetch(jsonFilePath).then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) 291 .then(data => { 292 switch (whichOne) { 293 case 1: lipSyncJSON_1 = data; break; 294 case 2: lipSyncJSON_2 = data; break; 295 case 3: lipSyncJSON_3 = data; break; 296 default: 297 } 298 }).catch(error => { parent.console.error('Fetch error:', error); parent.console.warn("Was trying to get:\n" + jsonFilePath); }) 299 .finally(()=>{ // Even if json fails the app will proceed and play sounds without lip sync animation 300 if (introSound1IsReady || outroSound1IsReady) { 301 //stop the loading animation by clearing all timeouts 302 clearTimeout(to1); clearTimeout(to2); clearTimeout(to3); clearTimeout(to4); clearTimeout(to5); clearTimeout(to6); 303 // Put "Listen" text into the button by overwriting the last state of the loading animation 304 listenButtonOfTheVocabulary.innerHTML = listenButtonTxt; 305 // Before the implementation of animated text, here used to be » listenButtonOfTheVocabulary.style.visibility = "visible"; 306 pseudoReactivationOfPlayButton(); // Same as » listenButtonOfTheVocabulary.style.opacity = "1"; 307 } else { 308 // THIS IS IMPOSSIBLE since get getJSON will never fire without sound being fully loaded by Howler first 309 } 310 }); 311 } 312 313 // -- 314 const hitonokaochanContainer = document.createElement("DIV"); 315 hitonokaochanContainer.classList.add("listenBoxHitonokaochan"); 316 const hitonokaochanA = document.createElement("IMG"); hitonokaochanA.src = "/user_interface/images/rhubarb_lip_sync/a.webp"; hitonokaochanA.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanA); 317 const hitonokaochanB = document.createElement("IMG"); hitonokaochanB.src = "/user_interface/images/rhubarb_lip_sync/b.webp"; hitonokaochanB.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanB); 318 const hitonokaochanC = document.createElement("IMG"); hitonokaochanC.src = "/user_interface/images/rhubarb_lip_sync/c.webp"; hitonokaochanC.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanC); 319 const hitonokaochanD = document.createElement("IMG"); hitonokaochanD.src = "/user_interface/images/rhubarb_lip_sync/d.webp"; hitonokaochanD.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanD); 320 const hitonokaochanE = document.createElement("IMG"); hitonokaochanE.src = "/user_interface/images/rhubarb_lip_sync/e.webp"; hitonokaochanE.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanE); 321 const hitonokaochanF = document.createElement("IMG"); hitonokaochanF.src = "/user_interface/images/rhubarb_lip_sync/f.webp"; hitonokaochanF.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanF); 322 const hitonokaochanG = document.createElement("IMG"); hitonokaochanG.src = "/user_interface/images/rhubarb_lip_sync/g.webp"; hitonokaochanG.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanG); 323 const hitonokaochanH = document.createElement("IMG"); hitonokaochanH.src = "/user_interface/images/rhubarb_lip_sync/h.webp"; hitonokaochanH.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanH); 324 const hitonokaochanN = document.createElement("IMG"); hitonokaochanN.src = "/user_interface/images/rhubarb_lip_sync/n.webp"; hitonokaochanN.style.display = "none"; hitonokaochanContainer.appendChild(hitonokaochanN); // Added on April 21st 2024 325 const hitonokaochanX = document.createElement("IMG"); hitonokaochanX.src = "/user_interface/images/rhubarb_lip_sync/x.webp"; hitonokaochanX.style.display = "block";hitonokaochanContainer.appendChild(hitonokaochanX); 326 const allMouthStates = [hitonokaochanA,hitonokaochanB,hitonokaochanC,hitonokaochanD,hitonokaochanE,hitonokaochanF,hitonokaochanG,hitonokaochanH,hitonokaochanN,hitonokaochanX]; 327 // QUESTION: Within startButtonF Hitonokaochan img container must be removed OR NOT? ANSWER: NOT IF we check whether it already exists before adding it 328 if (vocabularyBoxItself.contains(hitonokaochanContainer)) { } // This way we don't have to remove it inside startButtonF 329 else { vocabularyBoxItself.appendChild(hitonokaochanContainer); } // Will add it only when called as intro 330 // - 331 function animateHitonokachan(jsonData) { 332 const mouthCues = jsonData.mouthCues;
333 let counter = 0; 334 mouthCues.forEach(cue => { 335 if (counter>0) { // Skip setting the very first timeout at 0000ms 336 setTimeout(() => { 337 allMouthStates.forEach(frame => { frame.style.display = "none"; }); // Same for all 338 switch (cue.value) { 339 // Rhubarb standard alphabetic reference PLUS the addition of "N" for «nnn» sound 340 case "A": hitonokaochanA.style.display = "block"; break; 341 case "B": hitonokaochanB.style.display = "block"; break; 342 case "C": hitonokaochanC.style.display = "block"; break; 343 case "D": hitonokaochanD.style.display = "block"; break; 344 case "E": hitonokaochanE.style.display = "block"; break; 345 case "F": hitonokaochanF.style.display = "block"; break; 346 case "G": hitonokaochanG.style.display = "block"; break; 347 case "H": hitonokaochanH.style.display = "block"; break; 348 case "N": hitonokaochanN.style.display = "block"; break; 349 case "X": hitonokaochanX.style.display = "block"; break; 350 // Leavidence Design reference 351 case "MM": hitonokaochanA.style.display = "block"; break; 352 case "SS": hitonokaochanB.style.display = "block"; break; 353 case "EE": hitonokaochanC.style.display = "block"; break; 354 case "AA": hitonokaochanD.style.display = "block"; break; 355 case "OO": hitonokaochanE.style.display = "block"; break; 356 case "UU": hitonokaochanF.style.display = "block"; break; 357 case "FF": hitonokaochanG.style.display = "block"; break; 358 case "LL": hitonokaochanH.style.display = "block"; break; 359 case "NN": hitonokaochanN.style.display = "block"; break; 360 case "XX": hitonokaochanX.style.display = "block"; break; 361 362 default: 363 } 364 // It works! 365 }, cue.start * 1000); // Convert seconds to milliseconds 366 } 367 // - 368 counter++; 369 }); 370 } 371 372 function playintroVocabulary1() { 373 if (introSound1IsReady && !introSoundIsPlaying) { 374 introVocabulary1.play(); introSoundIsPlaying = true; pseudoDeactivationOfPlayButton(); 375 if (lipSyncJSON_1) { animateHitonokachan(lipSyncJSON_1); } // Handle lip-sync 376 } 377 } 378 function playintroVocabulary2() { 379 if (introSound2IsReady && !introSoundIsPlaying) { 380 introVocabulary2.play(); introSoundIsPlaying = true; pseudoDeactivationOfPlayButton(); 381 if (lipSyncJSON_2) { animateHitonokachan(lipSyncJSON_2); } // Handle lip-sync 382 } 383 } 384 function playintroVocabulary3() { 385 if (introSound3IsReady && !introSoundIsPlaying) { 386 introVocabulary3.play(); introSoundIsPlaying = true; pseudoDeactivationOfPlayButton(); 387 if (lipSyncJSON_3) { animateHitonokachan(lipSyncJSON_3); } // Handle lip-sync 388 } 389 } 390 function playoutroVocabulary1() { 391 if (outroSound1IsReady && !outroSoundIsPlaying) { 392 outroVocabulary1.play(); outroSoundIsPlaying = true; pseudoDeactivationOfPlayButton(); 393 if (lipSyncJSON_1) { animateHitonokachan(lipSyncJSON_1); } // Handle lip-sync 394 } 395 } 396 function playoutroVocabulary2() { 397 if (outroSound2IsReady && !outroSoundIsPlaying) { 398 outroVocabulary2.play(); outroSoundIsPlaying = true; pseudoDeactivationOfPlayButton(); 399 if (lipSyncJSON_2) { animateHitonokachan(lipSyncJSON_2); } // Handle lip-sync 400 } 401 } 402 function playoutroVocabulary3() { 403 if (outroSound3IsReady && !outroSoundIsPlaying) { 404 outroVocabulary3.play(); outroSoundIsPlaying = true; pseudoDeactivationOfPlayButton(); 405 if (lipSyncJSON_3) { animateHitonokachan(lipSyncJSON_3); } // Handle lip-sync 406 } 407 } 408 409 function pseudoDeactivationOfPlayButton() { listenButtonOfTheVocabulary.style.opacity = "0.4"; } // Event listeners will stay » callback functions will decide if the button will do something or nothing 410 function pseudoReactivationOfPlayButton() { listenButtonOfTheVocabulary.style.opacity = "1"; } // Event listeners will stay » callback functions will decide if the button will do something or nothing 411 412 // APPEND txt2 413 if (!isLessonOutro) { vocabularyBoxItself.appendChild(putVocabularyTxtIntoThisP2); } 414 else { vocabularyBoxItself.appendChild(putVocabularyTxtIntoThisP2OUTRO); } 415 // Try to fix Safari and Firefox as they ignore text align justify although the container div has it in its class 416 // putVocabularyTxtIntoThisP1.style.textAlign = "justify"; putVocabularyTxtIntoThisP2.style.textAlign = "justify"; // DIDN'T WORK! 417 418 listenButtonOfTheVocabulary.classList.add("buttonsAtTheBottomOfThePronunciationBox"); loadingAnimation(); // 🔃 is a "refresh ð" mark 419 420 function loadingAnimation() { 421 listenButtonOfTheVocabulary.innerHTML = "...::..."; 422 to1 = setTimeout(function () { listenButtonOfTheVocabulary.innerHTML = "..:··:.."; }, 100); 423 to2 = setTimeout(function () { listenButtonOfTheVocabulary.innerHTML = ".:····:."; }, 200); 424 to3 = setTimeout(function () { listenButtonOfTheVocabulary.innerHTML = ":······:"; }, 300); 425 to4 = setTimeout(function () { listenButtonOfTheVocabulary.innerHTML = ".:····:."; }, 400); 426 to5 = setTimeout(function () { listenButtonOfTheVocabulary.innerHTML = "..:··:.."; }, 500); 427 to6 = setTimeout(loadingAnimation, 600); 428 } 429 430 startButtonToCloseTheVocabulary.classList.add("buttonsAtTheBottomOfThePronunciationBox"); startButtonToCloseTheVocabulary.innerHTML = "►"; // 🎮 is a "gamepad ð®" mark // ► is a "play âº" mark 431 if (deviceDetector.isMobile) { 432 listenButtonOfTheVocabulary.classList.add("buttonsAtTheBottomOfThePronunciationBoxMOBILE"); 433 startButtonToCloseTheVocabulary.classList.add("buttonsAtTheBottomOfThePronunciationBoxMOBILE"); 434 } else { 435 listenButtonOfTheVocabulary.classList.add("buttonsAtTheBottomOfThePronunciationBoxDESKTOP"); 436 startButtonToCloseTheVocabulary.classList.add("buttonsAtTheBottomOfThePronunciationBoxDESKTOP"); 437 } 438 // --- 439 if (listenButtonTxt) { // If the primary fetch (the one that fires at global level even before window load) has already finished downloading the txt file
440 // LET listenButtonTxt STAY READY AND WAIT UNTIL THE SOUNDS AND JSONS LOAD » Before the implementation of animated text here used to be » listenButtonOfTheVocabulary.innerHTML = listenButtonTxt; 441 if (!isLessonOutro) { startButtonToCloseTheVocabulary.innerHTML = startButtonTxt; } 442 else { startButtonToCloseTheVocabulary.innerHTML = nextButtonTxt; } 443 } else { // Restart fetch but this time update buttonTxts as soon as the file is ready 444 if (primaryFetchIsStillTryingToGetTheFile) { 445 // Let it keep working 446 } else { 447 // Retry getting the file one single more time i.e. this is the second and the last try 448 fetch(listenBoxButton1Button2Path,myHeaders).then(function(response){return response.text();}).then(function(contentOfTheTxtFile){ 449 listenButtonTxt = contentOfTheTxtFile.split("|")[0]; listenButtonOfTheVocabulary.innerHTML = listenButtonTxt; 450 listenAgainButtonTxt = contentOfTheTxtFile.split("|")[1]; 451 if (!isLessonOutro) { startButtonTxt = contentOfTheTxtFile.split("|")[2]; startButtonToCloseTheVocabulary.innerHTML = startButtonTxt; } 452 else { nextButtonTxt = contentOfTheTxtFile.split("|")[3]; startButtonToCloseTheVocabulary.innerHTML = nextButtonTxt; } 453 }); 454 } 455 } 456 const twoButtonsContainer = document.createElement("DIV"); twoButtonsContainer.classList.add("vocabularyButtonsContainer"); 457 twoButtonsContainer.appendChild(listenButtonOfTheVocabulary); 458 vocabularyBoxItself.appendChild(twoButtonsContainer); 459 // Listen button 460 if (deviceDetector.isMobile) { listenButtonOfTheVocabulary.addEventListener("touchstart",playButtonF); } 461 else { listenButtonOfTheVocabulary.addEventListener("mousedown",playButtonF); 462 parent.window.addEventListener("keydown",checkIfSpaceKeyWasPressed); window.addEventListener("keydown",checkIfSpaceKeyWasPressed); 463 parent.window.addEventListener("keyup",checkIfSpaceKeyWasReleased); window.addEventListener("keyup",checkIfSpaceKeyWasReleased); 464 } 465 let clickOrTouchCount=0; 466 function playButtonF(event) { event.preventDefault(); event.stopPropagation(); 467 if (introSoundIsPlaying || outroSoundIsPlaying) { return; } // Prevent multiple instances of the sound overlap and be heard at the same time 468 // Use stopPropagation instead of parent.preventTouchConflictWithTheSlidingNavMenu(listenButtonOfTheVocabulary); // Exists in js_for_the_sliding_navigation_menu 469 clickOrTouchCount++; 470 // - 471 checkClickTouchModulus(clickOrTouchCount); 472 } 473 function checkClickTouchModulus(nthPlay) { 474 // - 475 if (nthPlay==1) { 476 if (listenAgainButtonTxt) { listenButtonOfTheVocabulary.innerHTML = listenAgainButtonTxt; } // Change button innerHTML from [Listen] to [Listen again] 477 } else if (nthPlay==2) { 478 // No change 479 } else if (nthPlay==3) { 480 startButtonToCloseTheVocabulary.classList.add("startButtonUnderListenBox"); twoButtonsContainer.appendChild(startButtonToCloseTheVocabulary); // Reveal the [Start] button 481 } 482 if (nthPlay%3 == 1) { if (!isLessonOutro) { playintroVocabulary1(); } else { playoutroVocabulary1(); } } 483 if (nthPlay%3 == 2) { if (!isLessonOutro) { playintroVocabulary2(); } else { playoutroVocabulary2(); } } 484 if (nthPlay%3 == 0) { if (!isLessonOutro) { playintroVocabulary3(); } else { playoutroVocabulary3(); } } 485 } 486 let spaceKeyIsBeingHeldDown = false; 487 function checkIfSpaceKeyWasPressed(event) { event.preventDefault(); // Can't be too safe 488 if (!spaceKeyIsBeingHeldDown) { 489 if (event.key == " " || event.code == "Space") { 490 spaceKeyIsBeingHeldDown = true; 491 // - 492 if (introSoundIsPlaying || outroSoundIsPlaying) { return; } // Prevent multiple instances of the sound overlap and be heard at the same time 493 // - 494 clickOrTouchCount++; 495 // - 496 checkClickTouchModulus(clickOrTouchCount); 497 } 498 } 499 } 500 function checkIfSpaceKeyWasReleased(event) { event.preventDefault(); // Can't be too safe 501 if (spaceKeyIsBeingHeldDown) { 502 if (event.key == " " || event.code == "Space") { 503 spaceKeyIsBeingHeldDown = false;
504 } 505 } 506 } 507 // Start button 508 if (deviceDetector.isMobile) { startButtonToCloseTheVocabulary.addEventListener("touchstart",startButtonF); } 509 else { startButtonToCloseTheVocabulary.addEventListener("mousedown",startButtonF); } 510 let lastPointerX, lastPointerY; 511 function startButtonF(event) { event.preventDefault(); event.stopPropagation(); 512 // Use stopPropagation instead of parent.preventTouchConflictWithTheSlidingNavMenu(startButtonToCloseTheVocabulary); // Exists in js_for_the_sliding_navigation_menu 513 dismissVocabularySound.play(); 514 vocabularyBoxItself.style.animationName = "vocabularyListenBoxDisappears"; 515 vocabularyBoxContainer.style.animationName = "vocabularyPronunciationDisappearsBG"; 516 setTimeout(function(){ 517 // DEPRECATED document.body.removeChild(vocabularyBoxContainer); 518 startButtonToCloseTheVocabulary.remove(); 519 listenButtonOfTheVocabulary.remove(); 520 twoButtonsContainer.remove(); 521 if (vocabularyBoxItself.contains(putVocabularyTxtIntoThisP2)) { putVocabularyTxtIntoThisP2.remove(); } 522 if (vocabularyBoxItself.contains(putVocabularyTxtIntoThisP2OUTRO)) { putVocabularyTxtIntoThisP2OUTRO.remove(); } 523 // No need to remove hitonokaochanContainer when using «contains()» 524 if (vocabularyBoxItself.contains(putVocabularyTxtIntoThisP1)) { putVocabularyTxtIntoThisP1.remove(); } 525 if (vocabularyBoxItself.contains(putVocabularyTxtIntoThisP1OUTRO)) { putVocabularyTxtIntoThisP1OUTRO.remove(); } 526 vocabularyBoxItself.remove(); 527 vocabularyBoxContainer.remove(); 528 },501); 529 530 if (deviceDetector.device == "desktop") { 531 lastPointerX = event.clientX; 532 lastPointerY = event.clientY; 533 } else { 534 lastPointerX = event.touches[0].clientX; 535 lastPointerY = event.touches[0].clientY; 536 } 537 // --- 538 if (!isLessonOutro) { // CASE: Lesson intro 539 if (typeof vocabularyBoxIsClosed === "function") { 540 setTimeout(function () { vocabularyBoxIsClosed(lastPointerX,lastPointerY); }, 500); 541 } else { parent.console.error("Error: vocabularyBoxIsClosed() function doesn't exist???"); } 542 } else { // CASE: Lesson outro 543 if (typeof vocabularyBoxIsClosed_LESSON_OUTRO === "function") { 544 setTimeout(function () { vocabularyBoxIsClosed_LESSON_OUTRO(lastPointerX,lastPointerY); }, 500); 545 } else { parent.console.error("Error: vocabularyBoxIsClosed_LESSON_OUTRO() function doesn't exist???"); } 546 } 547 548 // - 549 listenButtonOfTheVocabulary.removeEventListener("touchstart",playButtonF); 550 listenButtonOfTheVocabulary.removeEventListener("mousedown",playButtonF); 551 startButtonToCloseTheVocabulary.removeEventListener("touchstart",startButtonF); 552 startButtonToCloseTheVocabulary.removeEventListener("mousedown",startButtonF); 553 parent.window.removeEventListener("keydown",checkIfSpaceKeyWasPressed); window.removeEventListener("keydown",checkIfSpaceKeyWasPressed); 554 parent.window.removeEventListener("keyup",checkIfSpaceKeyWasReleased); window.removeEventListener("keyup",checkIfSpaceKeyWasReleased); 555 } // END OF startButtonF 556} 557// END OF createAndHandleListenManyTimesBox DEFINITION
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.