PageSourceSearch

https://toolbox.injoyonline.com/InJoyLibrary/injoy/js/tts.js?update=24

js injoyonline.com collected 2026-09-30 14:34:30 UTC 87,548 bytes, 2,142 lines download raw bytes

1var audioPlayer;
2var oldPlayerCheck = false;
3var oldPlayer;
4
5var ttsContent = {
6    "element": null,
7    "junkText": null,
8    "unorderedListText": null,
9    "orderedListText": null,
10    "iframeText": [],
11    "trimmedContent": null,
12    "TTS": null,
13    "pageKey": null
14};
15
16var ttsCurrentKey = null;
17
18var iframeElement = null;
19
20var ttsVoice = {
21    "languageCode": "en-US",
22    "name": "en-US-Wavenet-C",
23    "ssmlGender": "Female",
24};
25
26var sandbox = false;
27
28function removeOldPlayer() {
29    try {
30        ttsCurrentKey = null;
31        ttsContent['pageKey'] = null;
32
33        if (document.getElementsByClassName("audio__player")[0]) {
34            oldPlayerCheck = true;
35            oldPlayer = document.getElementsByClassName("audio__player")[0];
36            oldPlayer.style.display = "none";
37        } else {
38            oldPlayerCheck = false;
39        }
40
41        if (document.getElementById("roboVoice")) {
42            document.getElementById("roboVoice").parentNode.removeChild(document.getElementById("roboVoice"));
43            resetTTS();
44            ttsContent['trimmedContent'] = null;
45            ttsContent['TTS'] = null;
46            ttsContent['pageKey'] = null;
47            checkForText();
48            checkForPlacement();
49        }
50
51        if (document.getElementById("playSpan")) {
52            document.getElementById("playSpan").parentNode.removeChild(document.getElementById("playSpan"));
53        }
54    } catch (err) {
55        // console.log(err.stack)
56    }
57}
58
59function resetTTSState(reason) {
60    ttsCurrentKey = null;
61    ttsContent['element'] = null;
62    ttsContent['junkText'] = null;
63    ttsContent['unorderedListText'] = null;
64    ttsContent['orderedListText'] = null;
65    ttsContent['iframeText'] = [];
66    ttsContent['trimmedContent'] = null;
67    ttsContent['TTS'] = null;
68    ttsContent['pageKey'] = null;
69
70    var audio = document.getElementById("roboVoice");
71    if (audio) {
72        try {
73            audio.pause();
74            audio.removeAttribute("src");
75            audio.load();
76        } catch (err) {
77            // console.log(err.stack);
78        }
79        if (audio.parentNode) {
80            audio.parentNode.removeChild(audio);
81        }
82    }
83
84    if (document.getElementById("playSpan")) {
85        document.getElementById("playSpan").parentNode.removeChild(document.getElementById("playSpan"));
86    }
87}
88
89function removeEmptyPlayer() {
90    if (document.getElementById("playSpan")) {
91        var emptyPlayer = document.getElementById("playSpan");
92        emptyPlayer.parentNode.removeChild(emptyPlayer);
93    }
94}
95
96function getTtsPageKey() {
97    try {
98        return InJoy.pageID() + "_" + getLanguageTransl();
99    } catch (err) {
100        return null;
101    }
102}
103
104function clearTtsAudio() {
105    ttsCurrentKey = null;
106    ttsContent['TTS'] = null;
107
108    var audio = document.getElementById("roboVoice");
109    if (audio) {
110        try {
111            audio.pause();
112            audio.removeAttribute("src");
113            audio.load();
114        } catch (err) {
115            // console.log(err.stack);
116        }
117        if (audio.parentNode) {
118            audio.parentNode.removeChild(audio);
119        }
120    }
121}
122
123function updateTtsPageKey() {
124    var pageKey = getTtsPageKey();
125    if (pageKey && ttsContent['pageKey'] !== pageKey) {
126        clearTtsAudio();
127    }
128    ttsContent['pageKey'] = pageKey;
129    return pageKey;
130}
131
132function getTtsUiElements() {
133    var pageKey = getTtsPageKey();
134    var root = null;
135    if (pageKey) {
136        root = document.querySelector("[tts-id='" + pageKey + "']");
137    }
138    if (!root) {
139        root = document;
140    }
141    return {
142        playButton: root.querySelector("#playButton"),
143        playIcon: root.querySelector("#playIcon"),
144        pauseIcon: root.querySelector("#pauseIcon"),
145        loadingIcon: root.querySelector("#loadingIcon")
146    };
147}
148
149function setTtsUiState(state) {
150    var ui = getTtsUiElements();
151    if (!ui.playButton || !ui.playIcon || !ui.pauseIcon || !ui.loadingIcon) {
152        return;
153    }
154    if (state === "playing") {
155        ui.pauseIcon.style.display = "";
156        ui.playIcon.style.display = "none";
157        ui.loadingIcon.style.display = "none";
158        ui.playButton.removeAttribute("onclick");
159        ui.playButton.setAttribute("onclick", "pauseTTS()");
160    } else if (state === "loading") {
161        ui.pauseIcon.style.display = "none";
162        ui.playIcon.style.display = "none";
163        ui.loadingIcon.style.display = "";
164        ui.playButton.removeAttribute("onclick");
165        ui.playButton.setAttribute("onclick", "playTTS()");
166    } else {
167        ui.pauseIcon.style.display = "none";
168        ui.playIcon.style.display = "";
169        ui.loadingIcon.style.display = "none";
170        ui.playButton.removeAttribute("onclick");
171        ui.playButton.setAttribute("onclick", "playTTS()");
172    }
173}
174
175function removeStalePlayButton(pageKey) {
176    if (!pageKey) {
177        return;
178    }
179    if (document.getElementById("playSpan")) {
180        var existingKey = document.getElementById("playSpan").getAttribute("tts-id");
181        if (existingKey !== pageKey) {
182            clearTtsAudio();
183            ttsContent['trimmedContent'] = null;
184            ttsContent['pageKey'] = null;
185            document.getElementById("playSpan").remove();
186        }
187    }
188}
189
190function stripHTML(element) {
191
192    // adds pauses to table cells and headers
193    var headers = element.getElementsByTagName("th");
194    for (var x = 0; x < headers.length; x++) {
195        var txt = headers[x].textContent + ".";
196        headers[x].textContent = txt;
197    }
198
199    var cells = element.getElementsByTagName("td");
200    for (var x = 0; x < cells.length; x++) {
201        var txt = cells[x].textContent + ".";
202        cells[x].textContent = txt;
203    }
204
205    // consolidates paragraphs into text content and removes any html
206    var paragraphs = element.getElementsByTagName("P");
207    var content = element.textContent;
208    for (var x = 0; x < paragraphs.length; x++) {
209        var paragraph = paragraphs[x].textContent;
210        var newParagraph = paragraph + ".";
211        content = content.replace(paragraph, newParagraph);
212    }
213    return content;
214}
215
216function translateText(trimmedContent, language) {
217    // console.log(language)
218
219    var keyTranlate = "AIzaSyD1YXvDyH6F-pdizJwrAeAgXGqBqccA5rs";
220    var requestBodyTranslate = {
221        "q": trimmedContent,
222        "source": 'en',
223        "target": language,
224        "format": 'text',
225        "model": "nmt"
226    };
227
228    var target = "https://content-translation.googleapis.com/language/translate/v2?key=" + keyTranlate + "&alt=json";
229
230    var xmlhttp = new XMLHttpRequest();
231    xmlhttp.onreadystatechange = function () {
232        if (this.readyState == 4 && this.status == 200) {
233            var output = this.responseText;
234            output = JSON.parse(output);
235            var translatedText = output.data.translations[0].translatedText;
236            // console.log(translatedText);
237            ttsContent['trimmedContent'] = translatedText;
238            // generateSpeech();
239        }
240    };
241    xmlhttp.open("POST", target);
242    xmlhttp.setRequestHeader("accept", "*/*");
243    xmlhttp.setRequestHeader("content-type", "application/json");
244    xmlhttp.setRequestHeader("x-goog-encode-response-if-executable", "base64");
245    xmlhttp.setRequestHeader("accept-language", "en-US,en;q=1,es;q=1");
246    // xmlhttp.setRequestHeader("Authorization", "Bearer " + key);
247    xmlhttp.send(JSON.stringify(requestBodyTranslate));
248}
249
250function buildAudioElement(audioContent, azure, language) {
251
252    // console.log("audioContent:",audioContent);
253    // console.log("azure:",azure);
254
255    if (document.getElementById('textToSpeechName')) {
256        // console.log('inside TTS Generator');
257        var fileName = document.getElementById("textToSpeechName").value;
258        var linkTag = document.createElement('a');
259        if (azure == true) {
260            linkTag.href = audioContent;
261        } else {
262            linkTag.href = "data:audio/mp3;base64, " + audioContent;
263        }
264        linkTag.download = fileName;
265        document.body.appendChild(linkTag);
266        linkTag.click();
267        linkTag.parentElement.removeChild(linkTag);
268    } else {
269        // console.log('inside ILP');
270        var audio = document.createElement("audio");
271        audio.id = "roboVoice";
272        audio.setAttribute("controls", "controls");
273        audio.setAttribute("style", "visibility:hidden;position:fixed;bottom:0;left:0;");
274        audio.setAttribute("onended", "resetTTS()");
275        audio.setAttribute("data-language", language);
276        // audio.addEventListener("load",function(){
277        //     audio.play();
278        // })
279
280        var source = document.createElement("source");
281        if (azure == true) {
282            source.src = audioContent;
283            audio.src = audioContent;
284        } else {
285            source.src = "data:audio/mp3;base64, " + audioContent;
286            audio.src = "data:audio/mp3;base64, " + audioContent;
287        }
288        audio.setAttribute("type", "audio/mp3");
289
290        // audio.appendChild(source);
291        document.body.appendChild(audio);
292
293        ttsContent['TTS'] = source;
294        playTTS();
295    }
296}
297
298function getLessonIndex() {
299    try {
300        return window.CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:lesson"].page - 1;
301    } catch (e) {
302        // console.log(e);
303        return 0;
304    }
305    // var index;
306    // if (window.location.search){
307    //     var search = window.location.search;
308    //     search = search.split("=")[1];
309    //     index = search - 1;
310    // } else {
311    //     index = 0;
312    // }
313    // return index;
314}
315
316function parseOverride(target) {
317    // parseOverride can handle up to 10 path segments
318    var lessonIndex = getLessonIndex();
319    var topic = window.CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:lesson/index"].model._data.topics[lessonIndex];
320
321    target = target.replace(/\[/g, ".");
322    target = target.replace(/\]/g, "");
323    target = target.split(".");
324
325    var len = target.length;
326    if (len == 1) {
327        return topic[target[0]];
328    } else if (len == 2) {
329        return topic[target[0]][target[1]];
330    } else if (len == 3) {
331        return topic[target[0]][target[1]][target[2]];
332    } else if (len == 4) {
333        return topic[target[0]][target[1]][target[2]][target[3]];
334    } else if (len == 5) {
335        return topic[target[0]][target[1]][target[2]][target[3]][target[4]];
336    } else if (len == 6) {
337        return topic[target[0]][target[1]][target[2]][target[3]][target[4]][target[5]];
338    } else if (len == 7) {
339        return topic[target[0]][target[1]][target[2]][target[3]][target[4]][target[5]][target[6]];
340    } else if (len == 8) {
341        return topic[target[0]][target[1]][target[2]][target[3]][target[4]][target[5]][target[6]][target[7]];
342    } else if (len == 9) {
343        return topic[target[0]][target[1]][target[2]][target[3]][target[4]][target[5]][target[6]][target[7]][target[8]];
344    } else if (len == 10) {
345        return topic[target[0]][target[1]][target[2]][target[3]][target[4]][target[5]][target[6]][target[7]][target[8]][target[9]];
346    }
347}
348
349function getQuestions() {
350    return new Promise(function (resolve, reject) {
351
352        let id = getEleInfoTransl("pageID");
353        let type = getEleInfoTransl("page").type;
354
355        // console.log('Collect Questions');
356        var operationName = "LoadQuizAttemptWithQuestions";
357        var variables = {
358            courseID: getEleInfoTransl("courseID"),
359            id: id,
360            instructorAssessmentUser: null,
361            linkedWorkbook: null,
362            quizAttemptId: null,
363            topicType: type,
364        }
365        if (graphQLHelper.operationTokens[operationName]) {
366            var token = graphQLHelper.operationTokens[operationName];
367            var payload = [{
368                "token": token,
369                "variables": variables,
370                "operationName": operationName
371            }];
372            try {
373
374                function json(response) {
375                    return response.json()
376                }
377
378                var endpoint = "https://" + location.hostname + "/graphql";
379
380                fetch(endpoint, {
381                    method: 'post',
382                    headers: {
383                        "Accept": "*/*",
384                        "Accept-Encoding": "gzip, deflate, br",
385                        "Accept-Language": "en-US,en;q=1,es;q=1",
386                        "Content-type": "application/json",
387                        "authToken": window.CONF.authToken,
388                        "Connection": "keep-alive",
389                        "nonce": window.CONF.nonce,
390                        "Sec-Fetch-Mode": "cors",
391                        "Sec-Fetch-Site": "same-origin"
392                    },
393                    credentials: "include",
394                    body: JSON.stringify(payload),
395                })
396                    .then(json)
397                    .then(function (data) {
398                        // updateQuizQuestions(data,courseID,id);
399                        // console.log(data);
400                        resolve(data);
401
402                    })
403                    .catch(function (error) {
404                        reject(error);
405                    });
406            } catch (err) {
407                // console.log(err.stack);
408                reject(err);
409            }
410        } else {
411            reject("No Token");
412        }
413    })
414}
415
416function retrieveCLA(htmlString) {
417    return new Promise(function (resolve, reject) {
418        // console.log("replaceCLA");
419        if (htmlString.includes("central-library-item")) {
420            // console.log("CLA found");
421            let ele = document.createElement("div");
422            ele.innerHTML = htmlString;
423            let clas = ele.querySelectorAll(".central-library-item");
424            if (clas.length > 0) {
425                let ids = [];
426                for (let x = 0; x < clas.length; x++) {
427                    let id = clas[x].getAttribute("data-asset-id");
428                    ids.push(id);
429                }
430                // console.log("CLA IDs:",ids);
431                let operationName = "AssetsByIdsQuery";
432                let variables = {
433                    ids: ids
434                };
435                let token = graphQLHelper.operationTokens[operationName];
436                var endpoint = "https://" + window.location.hostname + "/graphql";
437                var payload = [{
438                    "token": token,
439                    "variables": variables,
440                    "operationName": operationName
441                }];
442
443                var authToken = window.CONF.authToken;
444                if (authToken == "") {
445                    authToken = graphQLHelper.authToken;
446                }
447
448                // console.log("CLA Auth Token:", authToken);
449                fetch(endpoint, {
450                    method: 'post',
451                    headers: {
452                        "Accept": "*/*",
453                        "Accept-Encoding": "gzip, deflate, br",
454                        "Accept-Language": "en-US,en;q=0.9",
455                        "Content-type": "application/json",
456                        "authToken": authToken,
457                        "Connection": "keep-alive",
458                        "nonce": window.CONF.nonce,
459                        "Sec-Fetch-Mode": "cors",
460                        "Sec-Fetch-Site": "same-origin"
461                    },
462                    credentials: "include",
463                    body: JSON.stringify(payload),
464                })
465                    .then(function (response) {
466                        // replace the CLA elements with the returned content
467                        resolve(response.json());
468                    })
469                    .catch(function (error) {
470                        reject(error);
471                    });
472
473            }
474        } else {
475            resolve(htmlString);
476        }
477    });
478}
479
480function replaceCLA(htmlString, response) {
481    let ele = document.createElement("div");
482    ele.innerHTML = htmlString;
483    for (let x = 0; x < response.length; x++) {
484        let id = response[x].id;
485        let body = response[x].body;
486        if (ele.querySelector("[data-asset-id=\"" + id + "\"]")) {
487            ele.querySelector("[data-asset-id=\"" + id + "\"]").outerHTML = body;
488        }
489    }
490    // console.log("CLA HTML:", ele.innerHTML);
491    return ele.innerHTML
492}
493
494function getCourseInfo(request) {
495
496    function convertSSML(str) {
497        let wrapper = document.createElement("div");
498
499        let speak = document.createElement("speak");
500        speak.setAttribute("version", "1.0");
501        speak.setAttribute("xmlns", "http://www.w3.org/2001/10/synthesis");
502        speak.setAttribute("xml:lang", getLanguageTransl());
503
504        let voice = document.createElement("voice");
505        let voiceName = selectVoice();
506        voice.setAttribute("name", voiceName);
507
508        str = str.replace(/<span>/g, "<s>");
509        str = str.replace(/<\/span>/g, "</s>");
510
511        wrapper.appendChild(speak);
512        speak.appendChild(voice);
513        if (shouldUseFriendlyStyle(voiceName)) {
514            speak.setAttribute("xmlns:mstts", "https://www.w3.org/2001/mstts");
515            voice.innerHTML = "<mstts:express-as style='friendly'>" + str + "</mstts:express-as>";
516        } else {
517            voice.innerHTML = str;
518        }
519
520        // console.log(wrapper.innerHTML);
521        return wrapper.innerHTML;
522    }
523
524    function getSidebarText() {
525        let sidebarContent = document.createElement("div");
526        try {
527            if (getLanguageTransl() == "en-US") {
528                if (getEleInfoTransl("page").type == "text") {
529                    // console.log("sidebar")
530                    if (getEleInfoTransl("page").sidebarIsHidden == false) {
531                        // console.log("sidebar showing")
532                        if (getEleInfoTransl("page").sidebarItems._originalState) {
533                            // console.log("sidebar items original state")
534                            let len = getEleInfoTransl("page").sidebarItems._originalState.length;
535                            for (let x = 0; x < len; x++) {
536                                let item = getEleInfoTransl("page").sidebarItems._originalState[x];
537                                // console.log("sidebar item:",item)
538                                if (item._data) {
539                                    // console.log("sidebar content:",sidebarContent);
540                                    let itemContent = document.createElement("div");
541                                    if (item._data.caption) {
542                                        itemContent.innerHTML = item._data.caption;
543                                    } else if (item._data.embed) {
544                                        itemContent.innerHTML = item._data.embed;
545                                    }
546                                    sidebarContent.appendChild(itemContent);
547                                }
548                            }
549                        }
550                    }
551                }
552            } else {
553
554            }
555        } catch (e) {
556            console.error(e);
557        }
558        return sidebarContent;
559    }
560
561    function getSidebarElements() {
562        // returns translated text of all caption and embed sidebar items on the page
563        let elements = window[getEleInfoTransl('pageID') + "_" + getLanguageTransl()];
564        let sidebarItems = {}
565        let sidebarText = "";
566        for (let id in elements) {
567            if (id.includes("sidebarItems") && id.includes(getLanguageTransl()) && (id.includes('caption') || id.includes('embed'))) {
568                if (id.includes("_ml")) {
569                    let humanID = id.replace("_ml", "");
570                    if (elements[humanID]) {
571                        sidebarItems[humanID] = elements[humanID];
572                    } else {
573                        // no human transltation
574                        sidebarItems[id] = elements[id];
575                    }
576                }
577            }
578        }
579        for (let id in sidebarItems) {
580            if (sidebarItems[id].text) {
581                sidebarText = sidebarText + sidebarItems[id].text;
582            }
583        }
584
585        return sidebarText;
586    }
587
588    if (window.CONF) {
589        try {
590            var topic = window.CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:lesson/index"].model._data.topics[getLessonIndex()]._data;
591            var courseID = window.CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:course"].model.id;
592            var courseTitle = window.CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:course"].model._data.title;
593            var courseGroup = window.CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:course"].model._data.courseGroup.id;
594            var topicTitle = topic.title;
595            var topicType = topic.type;
596            // console.log('topicType: ',topicType);
597            // console.log('Data: ',topic);
598
599            if (request == "page") {
600                return topic;
601            } else if (request == "topicTitle") {
602                return topicTitle;
603            } else if (request == "topicType") {
604                return topicType;
605            } else if (request == "courseID") {
606                return courseID;
607            } else if (request == "courseTitle") {
608                return courseTitle;
609            } else if (request == "courseGroup") {
610                return courseGroup;
611            } else if (request == "text") {
612
613                if (document.querySelector("[data-tts]")) {
614                    // Overide typical page TTS behavior //
615                    // New System //
616                    if (getLanguageTransl() == "en" || getLanguageTransl() == "en-US" || getLanguageTransl() == "en-GB") {
617                        // return the english element present (but hidden) in the page and use that.
618                        let ttsElements = document.querySelectorAll("[data-tts]");
619                        let text = "";
620                        for (let x = 0; x < ttsElements.length; x++) {
621                            let str = ttsElements[x].innerHTML;
622                            text = text + str;
623                        }
624                        text = convertSSML(text);
625                        return text;
626                    } else {
627                        // find the corresponding multilanguage element
628                        let returns = window[getEleInfoTransl('pageID') + "_" + getLanguageTransl()];
629                        let overrides = [];
630                        for (let id in returns) {
631                            if (id.includes("en-US")) {
632                                // check for data-tts=true
633                                let element = returns[id];
634                                let text = element.text;
635                                if (text.includes("data-tts=")) {
636                                    // found one
637                                    overrides.push(id);
638                                }
639                            }
640                        }
641                        // console.log("Overrides:",overrides);
642
643                        // https://www.w3.org/TR/spoken-html/
644                        // https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-synthesis-markup?tabs=csharp#supported-ssml-elements
645                        let text = "";
646                        for (let x = 0; x < overrides.length; x++) {
647                            var originalID = getEleInfoTransl('page').originalId;
648                            if (originalID == null) {
649                                originalID = 'source';
650                                // console.log(originalID);
651                            }
652                            let translation = findTranslation(overrides[x], originalID).text;
653                            let wrapper = document.createElement("div");
654                            wrapper.innerHTML = translation;
655                            let override = wrapper.querySelector("[data-tts]");
656                            let str = override.innerHTML;
657                            text = text + str;
658                        }
659                        // console.log("Text:",text);
660                        text = convertSSML(text);
661                        // console.log("SSML:",text);
662                        return text;
663                    }
664                } else {
665                    // Overide typical page type dictated behavior by specifying alternate cached content //
666                    // Old System //
667                    // See below to override the default text page type retrun of "topic._data.body" with "topic._data.sidebarItems._originalState[0]._data.embed"
668                    // example: data-ttsOverride="_data.sidebarItems._originalState[0]._data.embed" on the page/topic in question
669                    // Note! This does not work for other languages.
670                    if (document.querySelector("[data-ttsOverride]")) {
671                        // console.log('override - Alt Element');
672                        var target = document.querySelector("[data-ttsOverride]").getAttribute("data-ttsOverride");
673                        return parseOverride(target);
674                    } else if (document.querySelector("[data-ttsAlt]")) {
675                        // console.log('override - Alt Text');
676                        var target = document.querySelector("[data-ttsAlt]").getAttribute("data-ttsAlt");
677                        return target;
678                    }
679
680                    // regular page content
681                    if (getLanguageTransl() == "en" || getLanguageTransl() == "en-US" || getLanguageTransl() == "en-GB") {
682                        if (topicType == "text") {
683                            let body = document.createElement('div');
684                            body.innerHTML = topic.body;
685                            let sidebar = getSidebarText();
686                            body.appendChild(sidebar);
687                            // this is returning stuff with cla elements still in it
688                            // all of these probably are
689                            return body.innerHTML;
690                        } else if (topicType == "embed") {
691                            return topic.scripts;
692                        } else if (topicType == "image" || topicType == "listRoll" || topicType == "matchPairSet" || topicType == "flipCardSet" || topicType == "slideshow") {
693                            var preText = "";
694                            if (topic.preTextBlock) {
695                                preText = topic.preTextBlock;
696                            }
697                            var description = "";
698                            if (topic.description) {
699                                description = topic.description;
700                            } else if (topic.caption) {
701                                description = topic.caption;
702                            }
703                            var postText = "";
704                            if (topic.postTextBlock) {
705                                postText = topic.postTextBlock;
706                            }
707                            return preText + description + postText;
708                        } else if (topicType == "notebook") {
709                            return topic.body;
710                        } else if (topicType == "quiz") {
711                            // topic.questions[0].body
712                            // topic.questions[0].choices[0].value
713                            // topic.questions[0].choices[0].response
714                            return topic.startMessage;
715                        }
716                        else if (topicType == "highlightZoneSet") {
717                            return topic.preTextBlock;
718                        }
719                        else if (topicType == "workbook") {
720                            // topic.questions[0].body;
721                            // topic.questions[0].choices[0].value
722                            // topic.questions[0].openEndedResponse
723                            return topic.questions[0].body;
724                        } else {
725                            return false;
726                        }
727                    } else {
728                        // pull from translation return window[{pageID}][{elementID}]
729                        if (topicType == "text" || topicType == "notebook") {
730                            let body = findTranslatedText("body");
731                            // console.log("body",body);
732                            let sidebarItems = getSidebarElements();
733                            // console.log("sidebarItems",sidebarItems);
734                            return body + sidebarItems;
735                        } else if (topicType == "embed") {
736                            return findTranslatedText("scripts");
737                        } else if (topicType == "image" || topicType == "listRoll" || topicType == "matchPairSet" || topicType == "flipCardSet" || topicType == "slideshow") {
738                            var preText = "";
739                            if (topic.preTextBlock) {
740                                preText = findTranslatedText("preTextBlock");
741                            }
742                            var description = "";
743                            if (topic.description) {
744                                description = findTranslatedText("description");
745                            } else if (topic.caption) {
746                                description = findTranslatedText("caption");
747                            }
748                            var postText = "";
749                            if (topic.postTextBlock) {
750                                postText = findTranslatedText("postTextBlock");
751                            }
752                            return preText + description + postText;
753                        } else if (topicType == "notebook") {
754                            return findTranslatedText("body");
755                        } else if (topicType == "quiz" || topicType == "survey") {
756
757                            return findTranslatedText("startMessage");
758                        } else if (topicType == "workbook") {
759                            return findTranslatedText("questionsUpdate.0.body");
760                            // return topic.questionsUpdate[0].body;
761                        }
762                        else if (topicType == "highlightZoneSet") {
763                            return findTranslatedText("preTextBlock");
764                        }
765                        else {
766                            return false;
767                        }
768                    }
769                }
770            }
771        } catch (err) {
772            // console.log(err.stack);
773        }
774    } else {
775        return false;
776    }
777}
778
779// function waitForTranslations(){
780//     return new Promise(function (resolve, reject) {
781//         let count = 0;
782//         let interval = setInterval(function(){
783//             if (window[getEleInfoTransl('pageID')+"_"+getLanguageTransl()]){
784//                 clearInterval(interval);
785//                 resolve();
786//             }
787//             count++;
788//             if (count>100){
789//                 clearInterval(interval);
790//                 reject();
791//             }
792//         },50);
793//     });
794// }
795
796function findTranslatedText(type) {
797
798    try {
799        var originalID = getEleInfoTransl('page').originalId;
800        if (originalID == null) {
801            originalID = 'source';
802            // console.log(originalID);
803        }
804        var courseID = getEleInfoTransl("courseID");
805        var pageID = getEleInfoTransl('pageID');
806        var elementID = courseID + ".topicsByID." + pageID + "." + type;
807        // console.log('elementID: ', elementID, "oringinalID: ", originalID);
808        // what happens if there is no translation?
809        let translation = findTranslation(elementID, originalID);
810        if (typeof (translation) == "object") {
811            // console.log("Translation Object:",translation);
812            if (translation['text']) {
813                translation = translation['text'];
814                // console.log("Translation Text:",translation);
815            } else {
816                translation = "";
817            }
818        }
819        return translation;
820    } catch (e) {
821        // console.log(e.stack);
822    }
823}
824
825function shouldUseFriendlyStyle(voiceName) {
826    return voiceName === "en-US-SerenaMultilingualNeural" || voiceName === "en-US-NancyMultilingualNeural";
827}
828
829function selectVoice() {
830    let language = getLanguageTransl();
831    let defaultVoice = "en-US-NovaTurboMultilingualNeural";
832    // let defaultVoice = "en-US-SerenaMultilingualNeural";
833    // let defaultVoice = "en-US-EvelynMultilingualNeural";
834    // let defaultVoice = "en-US-NancyMultilingualNeural";
835    let voiceName = "";
836    if (location.pathname == "/InJoyLibrary/injoy/html/tts.html") {
837        language = document.querySelector("#ttsVoiceSelect").getAttribute("data-locale");
838        voiceName = document.querySelector("#ttsVoiceSelect");
839    } else {
840        if (language == "en-US") {
841            voiceName = defaultVoice;
842        } else if (language == "fr-CA" || language == "fr") {
843            voiceName = defaultVoice;
844        } else if (language == "es-ES" || language == "es") {
845            voiceName = defaultVoice;
846        } else if (language == "pt" || language == "pt-PT") {
847            voiceName = defaultVoice;
848        } else if (language == "zh" || language == "zh-Hans" || language == "zh-hans") {
849            voiceName = defaultVoice;
850        } else if (language == "ja" || language == "ja-JP") {
851            voiceName = defaultVoice;
852        } else if (language == "de" || language == "de-DE") {
853            voiceName = defaultVoice;
854        } else if (language == "ar" || language == "ar-EG" || language == "ar-eg") {
855            voiceName = "ar-EG-SalmaNeural";
856        } else if (language == "hi" || language == "hi-IN") {
857            voiceName = "hi-IN-SwaraNeural";
858        } else if (language == "ht" || language == "ht-HT") {
859            voiceName = "fr-FR-DeniseNeural";
860        } else if (language == "ps" || language == "ps-AF") {
861            voiceName = "ps-AF-LatifaNeural";
862        } else if (language == "so" || language == "so-SO") {
863            voiceName = "so-SO-UbaxNeural";
864        } else if (language == "sw" || language == "sw-KE") {
865            voiceName = "sw-KE-ZuriNeural";
866        } else if (language == "ne" || language == "ne-NP") {
867            voiceName = "ne-NP-HemkalaNeural";
868        } else if (language == "vi" || language == "vi-VN") {
869            voiceName = "vi-VN-HoaiMyNeural";
870        } else {
871            voiceName = defaultVoice;
872        }
873    }
874    return voiceName;
875}
876
877function generateSpeechAzure() {
878
879    var trimmedContent = ttsContent['trimmedContent'];
880    // console.log(trimmedContent);
881
882    if (trimmedContent == "" || trimmedContent == null || trimmedContent == undefined) {
883        removeEmptyPlayer(); // this is why the quiz play button is disappearing
884    } else {
885
886        var target = "https://toolbox.injoyonline.com/InJoyLibrary/injoy/php/ttsAzure.php";
887        var language = "en-US";
888        var voiceName = selectVoice();
889        try {
890            if (getLanguageTransl) {
891                language = getLanguageTransl();
892            }
893        } catch (e) {
894
895            if (document.querySelector("#ttsVoiceSelect")) {
896                if (document.querySelector("#ttsVoiceSelect").value != "" && document.querySelector("#ttsVoiceSelect").value != null && document.querySelector("#ttsVoiceSelect").value != undefined) {
897                    voiceName = document.querySelector("#ttsVoiceSelect").value;
898                    language = document.querySelector("#ttsVoiceSelect").getAttribute("data-locale");
899                }
900            }
901        }
902
903        // var fileName = window.btoa(encodeURI(window.location.pathname));
904        var fileName;
905        if (document.querySelector("#ttsVoiceSelect")) {
906            fileName = document.getElementById("textToSpeechName").value;
907        } else {
908            fileName = getEleInfoTransl('pageID') + "_" + language;
909        }
910
911        try {
912            if (getEleInfoTransl('originalId')) {
913                fileName = getEleInfoTransl('originalId') + "_" + language;
914            }
915        } catch (e) {
916            // console.log(e.stack);
917        }
918
919        var requestKey = fileName;
920        ttsCurrentKey = requestKey;
921
922        var formData = new FormData();
923        formData.append("text", trimmedContent);
924        formData.append("lang", language);
925        formData.append("gender", "Female");
926        formData.append("voiceName", voiceName);
927        formData.append("fileName", fileName);
928        formData.append("title", getCourseInfo('topicTitle'));
929        formData.append("type", getCourseInfo('topicType'));
930        formData.append("rate", "1");
931
932        var xmlhttp = new XMLHttpRequest();
933        xmlhttp.onreadystatechange = function () {
934            if (this.readyState == 4 && this.status == 200) {
935                if (requestKey !== ttsCurrentKey) {
936                    return;
937                }
938                // var output = JSON.parse(this.responseText);
939                var output = this.responseText;
940                if (output.match('error') != null) {
941                    // console.log("error",output);
942                } else {
943                    // console.log("no error",output);
944                    buildAudioElement(output, true, language);
945                }
946            }
947        };
948        xmlhttp.open("POST", target, true);
949        xmlhttp.send(formData);
950    }
951}
952
953function generatePlayButton(original) {
954
955    // console.log('generatePlayButton');
956    // Don't create multiple copies of the tts play button
957    let pageID = InJoy.pageID();
958    let locale = getLanguageTransl();
959    let pageKey = pageID + "_" + locale;
960
961    removeStalePlayButton(pageKey);
962
963    if (document.querySelector("[tts-id='" + pageKey + "']")) {
964        return;
965    }
966
967
968
969    var span1 = document.createElement("SPAN");
970    span1.style.fontSize = "1rem";
971    span1.style.fontFamily = "sans-serif";
972    span1.style.right = "1.5rem";
973    span1.style.display = "inline-flex";
974    span1.style.alignItems = "center";
975    span1.id = "playSpan"
976
977    span1.setAttribute("tts-id", pageKey);
978
979    var a = document.createElement("A");
980    a.id = "playButton";
981    if (!original) {
982        a.setAttribute("onclick", "playTTS()");
983    } else {
984        a.setAttribute("onclick", "playOriginalTTS()");
985    }
986
987    a.setAttribute("class", "btn btn--tiny audio-player__play-button");
988    a.style.marginBottom = "0";
989    a.setAttribute("style", "margin-bottom: 0px;visibility: visible;display: inline-flex;justify-content: center;align-items: center;");
990
991    var div = document.createElement("I");
992    div.id = "playIcon";
993    div.setAttribute("class", "icon-volume");
994
995    var i = document.createElement("I");
996    i.id = "pauseIcon";
997    i.setAttribute("class", "icon-pause");
998    i.setAttribute("aria-label", "pause");
999    i.style.display = "none";
1000
1001    var img = document.createElement("img");
1002    img.id = "loadingIcon";
1003    img.src = "https://toolbox.injoyonline.com/js/webworkers/loading_sm.svg"
1004    img.setAttribute("aria-label", "loading");
1005    img.setAttribute("style", "display:none;");
1006
1007    var span2 = document.createElement("SPAN");
1008    span2.id = "playerText";
1009    span2.setAttribute("style", "font-size:.8em;margin-left:.5em;");
1010    span2.innerHTML = "Play Audio";
1011    if (getLanguageTransl() == "fr-CA") {
1012        span2.innerHTML = "Lire le son";
1013    } else if (getLanguageTransl() == "es-ES") {
1014        span2.innerHTML = "Reproducir audio";
1015    } else if (getLanguageTransl() == "pt-PT") {
1016        span2.innerHTML = "Tocar áudio";
1017    } else if (getLanguageTransl() == "zh-Hans" || getLanguageTransl() == "zh-hans") {
1018        span2.innerHTML = "播放音頻";
1019    } else if (getLanguageTransl() == "ar-EG" || getLanguageTransl() == "ar" || getLanguageTransl() == "ar") {
1020        span2.innerHTML = "تشغيل الصوت";
1021    } else if (getLanguageTransl() == "de-DE") {
1022        span2.innerHTML = "Audio abspielen";
1023    } else if (getLanguageTransl() == "ja-JP") {
1024        span2.innerHTML = "オーディオを再生";
1025    }
1026
1027    // translate stuff //
1028    var select = document.createElement("select");
1029    select.id = "ttsLangSelect";
1030    select.setAttribute("style", "width:100px;height:100%;float: left;margin-right: 1em;margin-bottom:0;position:relative;top:.15em;display:none;");
1031    select.setAttribute("onchange", "removeOldPlayer()");
1032    var option1 = document.createElement("option");
1033    option1.value = "en";
1034    option1.setAttribute("data-voice", "en-US-Wavenet-C");
1035    option1.setAttribute("data-languageCode", "en-US");
1036    option1.innerHTML = "English";
1037    option1.selected = true;
1038    var option2 = document.createElement("option");
1039    option2.value = "es";
1040    option2.innerHTML = "Spanish";
1041    var option3 = document.createElement("option");
1042    option3.value = "fr";
1043    option3.innerHTML = "French";
1044
1045    select.appendChild(option1);
1046    select.appendChild(option2);
1047    select.appendChild(option3);
1048
1049    span1.appendChild(a);
1050    a.appendChild(div);
1051    a.appendChild(i);
1052    a.appendChild(img);
1053    span1.appendChild(span2);
1054
1055    // target.appendChild(span1);
1056    // element.insertBefore(span1, element.childNodes[0]);
1057    if (oldPlayerCheck == true) {
1058        if (document.getElementsByClassName("audio__player")[0]) {
1059            oldPlayerCheck = true;
1060        } else {
1061            oldPlayerCheck = false;
1062        }
1063    }
1064
1065    if (location.href.indexOf("learn/article") >= 0) {
1066        // console.log('article');
1067        if (document.querySelector(".ij-text-to-speech")) {
1068            // console.log('play button target');
1069            document.querySelector(".ij-text-to-speech").appendChild(span1);
1070        }
1071    } else {
1072        // console.log('not article')
1073        span1.style.position = "absolute";
1074        span1.style.top = ".3rem";
1075
1076        if (document.querySelectorAll(".audio__player")[0]) {
1077            // console.log("hiding ",document.querySelectorAll(".audio__player")[0])
1078            document.querySelectorAll(".audio__player")[0].style.display = "none";
1079        }
1080
1081        span1.appendChild(select); // currently required even though not active
1082
1083        // Wait for .bookmark-dropdown__button to appear in the DOM (it may not be
1084        // present yet on SPA navigation), then insert the play button beside it.
1085        var _bmlAttempts = 0;
1086        function _insertPlaySpan() {
1087            var bookmarkLink = document.querySelector(".bookmark-dropdown__button");
1088            if (bookmarkLink) {
1089                var bmlParent = bookmarkLink.parentElement;
1090                var bmlGrandparent = bmlParent.parentElement;
1091                bmlGrandparent.insertBefore(span1, bmlParent);
1092            } else if (_bmlAttempts < 50) {
1093                _bmlAttempts++;
1094                setTimeout(_insertPlaySpan, 50);
1095            }
1096            // else: give up gracefully after ~2.5 s
1097        }
1098        _insertPlaySpan();
1099    }
1100}
1101
1102function findIframeSource(url, last, element, junkText, unorderedListText, orderedListText) {
1103    // console.log("findIframeSource");
1104    var iframeContent;
1105    try {
1106        if (url.match("s3.amazonaws.com/injoyassets") != null) {
1107            var xmlhttp = new XMLHttpRequest();
1108            xmlhttp.onreadystatechange = function () {
1109                if (this.readyState == 4 && this.status == 200) {
1110                    var output = this.responseText;
1111                    var wrapper = document.createElement("div");
1112                    wrapper.innerHTML = output;
1113                    // this should only target the header of ij activities //
1114                    var header = wrapper.querySelector(".header, .ij-activity");
1115                    iframeContent = stripHTML(header);
1116                    ttsContent['iframeText'].push(iframeContent);
1117                    if (last == true) {
1118                        cleanText(element, junkText, unorderedListText, orderedListText, true);
1119                    }
1120                }
1121            };
1122            xmlhttp.open("GET", url, true);
1123            xmlhttp.send();
1124        }
1125    } catch (err) {
1126        // console.log(err.stack);
1127        return err;
1128    }
1129}
1130
1131
1132async function checkForText(pageContent) {
1133
1134    // console.log("checkForText",pageContent);
1135
1136    try {
1137        if (!pageContent) {
1138            pageContent = getCourseInfo("text");
1139        }
1140
1141        // console.log("Page Content (potential object)",pageContent);
1142
1143        if (pageContent) {
1144            if (typeof (pageContent) == 'object') {
1145                pageContent = pageContent['text'];
1146            }
1147
1148            if (pageContent.includes("central-library-item")) {
1149                let response = await retrieveCLA(pageContent);
1150                response = response[0].data.AssetsByIds;
1151                // console.log("CLA Response:",response);
1152                pageContent = replaceCLA(pageContent, response);
1153            }
1154
1155            if (pageContent.includes("</speak>")) {
1156                // console.log("pageContent includes </speak>");
1157                generatePlayButton();
1158                ttsContent['trimmedContent'] = pageContent;
1159                updateTtsPageKey();
1160
1161            } else {
1162                // console.log("Page Content does not include </speak>");
1163
1164                var wrapper = document.createElement("div");
1165                wrapper.innerHTML = pageContent;
1166                // get sidebar text
1167                preprocessElement(wrapper);
1168            }
1169        }
1170    } catch (err) {
1171        // console.log(err.stack)
1172    }
1173}
1174
1175// function claFix(element){
1176//     // return new Promise(function (resolve, reject) {
1177//         try{
1178//         let claArr = element.querySelectorAll(".central-library-item");
1179//             for (let x=0;x<claArr.length;x++){
1180//                 let item = claArr[0];
1181//                 let id = item.getAttribute("[data-asset-id]");
1182//                 if (window[getCourseInfo("pageID") + "_" + getLanguageTransl()]){
1183//                 // console.log(window[getCourseInfo("pageID") + "_" + getLanguageTransl()]);
1184//                     if (window[getCourseInfo("pageID") + "_" + getLanguageTransl()][id + "_" + getLanguageTransl()]){
1185//                     // console.log(window[getCourseInfo("pageID") + "_" + getLanguageTransl()][id + "_" + getLanguageTransl()]);
1186//                     } else if (window[getCourseInfo("pageID") + "_" + getLanguageTransl()][id + "_" + getLanguageTransl() + "_ml"]){
1187//                     // console.log(window[getCourseInfo("pageID") + "_" + getLanguageTransl()][id + "_" + getLanguageTransl() + "_ml"]);
1188//                     }
1189//                 }
1190//             }
1191//             resolve('done');
1192//         }catch(e){
1193//             reject('error');
1194//         }
1195//     // })
1196// }
1197
1198function preprocessElement(element) {
1199    // Strip out Style and Script tag innerText
1200    // console.log("preprocessElement");
1201    if (element != undefined && ttsAuthCheck()) {
1202        // console.log("processElement",element);
1203        generatePlayButton();
1204
1205        var styles = element.getElementsByTagName("style");
1206        var styleCount = styles.length;
1207        var junkText = [];
1208        if (styleCount > 0) {
1209            for (var x = 0; x < styleCount; x++) {
1210                junkText.push(stripHTML(styles[x]));
1211            }
1212        }
1213
1214        var editWarning = element.getElementsByClassName("edit-warning");
1215        if (editWarning.length > 0) {
1216            for (var x = 0; x < editWarning.length; x++) {
1217                junkText.push(stripHTML(editWarning[x]));
1218            }
1219        }
1220
1221        var tooltip = element.getElementsByClassName("tooltip");
1222        if (tooltip.length > 0) {
1223            for (var x = 0; x < tooltip.length; x++) {
1224                junkText.push(stripHTML(tooltip[x]));
1225            }
1226        }
1227
1228        var scripts = element.getElementsByTagName("script");
1229        if (scripts.length > 0) {
1230            for (var x = 0; x < scripts.length; x++) {
1231                junkText.push(stripHTML(scripts[x]));
1232            }
1233        }
1234
1235        var libraryItems = element.querySelectorAll(".central-library-item");
1236        if (libraryItems.length > 0) {
1237            for (var x = 0; x < libraryItems.length; x++) {
1238                junkText.push(stripHTML(libraryItems[x]));
1239            }
1240        }
1241
1242        var paragraphs = element.getElementsByTagName("p");
1243        var paragraphCount = paragraphs.length;
1244        if (paragraphCount > 0) {
1245            for (var x = 0; x < paragraphCount; x++) {
1246                var p = paragraphs[x];
1247                var text = p.innerHTML;
1248                text = text.replace(/\u21B5/g, "");
1249                text = text.replace(/\n/g, "");
1250                paragraphs[x].innerHTML = text;
1251            }
1252        }
1253
1254        var unorderedListText = [];
1255        var unorderedLists = element.getElementsByTagName("ul"); // ul
1256        if (unorderedLists.length > 0) {
1257            for (var x = 0; x < unorderedLists.length; x++) {
1258                unorderedListText[x] = [];
1259                var unorderedList = unorderedLists[x];
1260                var items = unorderedList.getElementsByTagName("li");
1261                if (items.length > 0) {
1262                    for (var i = 0; i < items.length; i++) {
1263                        var item = items[i]
1264                        unorderedListText[x].push(stripHTML(item));
1265                    }
1266                }
1267            }
1268        }
1269
1270        var orderedListText = [];
1271        var orderedLists = element.getElementsByTagName("ol"); // ol
1272        if (orderedLists.length > 0) {
1273            for (var x = 0; x < orderedLists.length; x++) {
1274                orderedListText[x] = [];
1275                var orderedList = orderedLists[x];
1276                var items = orderedList.getElementsByTagName("li");
1277                if (items.length > 0) {
1278                    for (var i = 0; i < items.length; i++) {
1279                        var item = items[i]
1280                        orderedListText[x].push(stripHTML(item));
1281                    }
1282                }
1283            }
1284        }
1285
1286        ttsContent['element'] = element;
1287        ttsContent['junkText'] = junkText;
1288        ttsContent['unorderedListText'] = unorderedListText;
1289        ttsContent['orderedListText'] = orderedListText;
1290
1291        // console.log("Processed Element:",element);
1292
1293        cleanText(element, junkText, unorderedListText, orderedListText);
1294    }
1295}
1296
1297function resizeIframeElement(event) {
1298    try {
1299        if (event.origin !== "https://toolbox.injoyonline.com") { return; } // only accept messages from the specified domain // toolbox.injoyonline.com or *.injoyonline.com?
1300        else if (isNaN(event.data)) { return; } // only accept something which can be parsed as a number
1301        var height = parseInt(event.data) + 32; // add some extra height to avoid scrollbar
1302        // console.log("change height to: " + height);
1303        // ele.height = height + "px";
1304        if (iframeElement) {
1305            var ele = iframeElement;
1306            ele.style.height = height + "px";
1307            var parent = ele.parentElement;
1308            var classCheck = parent.getAttribute("class");
1309            if (classCheck.match("iframe-height") != null) {
1310                parent.removeAttribute("class");
1311                parent.style.height = height + 32 + "px";
1312            }
1313        }
1314    } catch (err) {
1315        // console.log(err.stack);
1316    }
1317}
1318
1319function resizeCrossDomainIframe(ele, other_domain) {
1320    window.addEventListener('message', resizeIframeElement, false);
1321}
1322
1323function correctPronunciation(content) {
1324    content = content.replace(/caesareans/gi, "cesareans");
1325    content = content.replace(/caesarean/gi, "cesarean");
1326    content = content.replace(/NO/g, "no");
1327    content = content.replace(/labour/gi, "labor");
1328    content = content.replace(/511/g, "5 1 1");
1329    content = content.replace(/311/g, "3 1 1");
1330    content = content.replace(/911/g, "9 1 1");
1331    content = content.replace(/5-1-1/g, "5 1 1");
1332    content = content.replace(/3-1-1/g, "3 1 1");
1333    content = content.replace(/9-1-1/g, "9 1 1");
1334    content = content.replace(/St. John/g, "Saint John");
1335    content = content.replace(/Dr. /gi, "Doctor ");
1336    content = content.replace(/37.5/g, "37&#46;5");
1337    content = content.replace(/99.5/g, "99&#46;5");
1338    content = content.replace(/48.8/g, "48&#46;8");
1339    content = content.replace(/100.4/g, "100&#46;4");
1340    content = content.replace(/37.2/g, "37&#46;2");
1341    content = content.replace(/IVs/g, "ivies");
1342    content = content.replace(/IV/g, "I V");
1343    content = content.replace(/EXPAND/g, "expand ");
1344    content = content.replace(/B.R.A.I.N./g, "brain");
1345    content = content.replace(/ vs. /gi, " versus ");
1346    content = content.replace(/ vs /gi, " versus ");
1347    content = content.replace(/\u21B5/g, ".");
1348    content = content.replace(/\u0009/g, ".");
1349    content = content.replace(/\n/g, ".");
1350    content = content.replace(/Play Audio/ig, "");
1351
1352    return content;
1353}
1354
1355function escapeForSsml(content) {
1356    if (content == null) {
1357        return "";
1358    }
1359    content = content.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, "");
1360    content = content.replace(/&/g, "&amp;");
1361    content = content.replace(/</g, "&lt;");
1362    content = content.replace(/>/g, "&gt;");
1363    content = content.replace(/\"/g, "&quot;");
1364    content = content.replace(/'/g, "&apos;");
1365    return content;
1366}
1367
1368function cleanText(element, junkText, unorderedListText, orderedListText, iframeBool) {
1369
1370    // console.log("Cleaning Text:",element);
1371
1372    var content = stripHTML(element);
1373
1374    // console.log("Content:",content);
1375
1376    if (junkText) {
1377        var len = junkText.length;
1378        for (var x = 0; x < len; x++) {
1379            content = content.replace(junkText[x], "");
1380        }
1381    }
1382
1383    if (unorderedListText) {
1384        let cache = [];
1385        for (var x = 0; x < unorderedListText.length; x++) {
1386            // get last character of listText[x] and check for .
1387            var unorderedList = unorderedListText[x];
1388            for (var i = 0; i < unorderedList.length; i++) {
1389                var item = unorderedList[i];
1390                item = item.trim();
1391                var lastChar = item.substr(item.length - 1);
1392                if (lastChar != ".") {
1393                    item = item + '.';
1394                }
1395                var regexp = new RegExp(unorderedListText[x][i], "gi");
1396                content = content.replace(regexp, item);
1397            }
1398        }
1399        // console.log("unordered list:",unorderedListText);
1400    }
1401
1402    // console.log("unordered list content:",content);
1403
1404    if (orderedListText) {
1405        for (var x = 0; x < orderedListText.length; x++) {
1406            // get last character of orderedListText[x] and check for .
1407            var orderedList = orderedListText[x];
1408            for (var i = 0; i < orderedList.length; i++) {
1409                var item = orderedList[i];
1410                item = item.trim();
1411                var num = i + 1;
1412                var lastChar = item.substr(item.length - 1);
1413                if (lastChar != ".") {
1414                    item = num + ". <break strength='strong' />" + item + '.';
1415                } else {
1416                    item = num + ". <break strength='strong' />" + item;
1417                }
1418                content = content.replace(orderedListText[x][i], item);
1419            }
1420        }
1421    }
1422
1423    if (iframeBool) {
1424        var len = ttsContent['iframeText'].length;
1425        for (var x = 0; x < len; x++) {
1426            var iframeContent = ttsContent['iframeText'][x] + ".";
1427            content = content + iframeContent;
1428        }
1429        // clear iframeText after each use
1430        ttsContent['iframeText'] = [];
1431    }
1432
1433    // Correct Pronunciation 
1434    content = correctPronunciation(content);
1435
1436    var breakToken = "__IJ_TTS_BREAK__";
1437    var decimalToken = "__IJ_TTS_DECIMAL__";
1438    content = content.replace(/&#46;/g, decimalToken);
1439    content = content.replace(/: /g, breakToken + ": ");
1440    content = escapeForSsml(content);
1441    content = content.replace(new RegExp(decimalToken, "g"), "&#46;");
1442
1443    content = content.split(".");
1444    // console.log("Content 1212:",content);
1445
1446    for (var x = 0; x < content.length; x++) {
1447        var element = content[x];
1448        element = element.trim();
1449        element = element.replace(/&#46;/g, ".");
1450
1451        content[x] = element;
1452    }
1453
1454    // console.log("Content 1222:",content);
1455
1456    function emptyStringFilter(str) {
1457        return str != "";
1458    }
1459    content = content.filter(emptyStringFilter);
1460    // console.log("Content 1228:",content);
1461
1462    function stringToSentence(item, index) {
1463
1464        content[index] = '<s>' + item + '.</s> ';
1465    }
1466
1467    content.forEach(stringToSentence);
1468    content = content.join("");
1469
1470    // console.log("Content 1238:",content);
1471
1472    // Remove Play Audio button innerText and new lines
1473    content = content.replace(new RegExp(breakToken, "g"), "<break />");
1474
1475    // console.log("Content 1243:",content);
1476
1477    var voiceName = selectVoice();
1478    var expressOpen = "";
1479    var expressClose = "";
1480    if (shouldUseFriendlyStyle(voiceName)) {
1481        expressOpen = "<mstts:express-as style='friendly'>";
1482        expressClose = "</mstts:express-as>";
1483    }
1484    var trimmedContent = "<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='https://www.w3.org/2001/mstts' xml:lang='" + getLanguageTransl() + "'><voice name='" + voiceName + "'><mstts:silence  type='Leading' value='1s'/>" + expressOpen + "<p>" + content + "</p>" + expressClose + "</voice></speak>";
1485    // save to global variable
1486    ttsContent['trimmedContent'] = trimmedContent;
1487    updateTtsPageKey();
1488    // console.log("1246",trimmedContent);
1489    // check here?
1490    // if (trimmedContent.match("<speak><p></p></speak>")){
1491    //     // catches slow loading surveys
1492    //     // textToSpeech('remove'); // this one's ok
1493    //   // console.log(trimmedContent);
1494    //     quickRemove("trimmedContent");
1495    // } else if (iframeBool) {
1496    //     // fixes iframes diabled by fixing the slow loading surveys
1497    //     var playButton = document.getElementById("playButton");
1498    //     var playFunction = playButton.getAttribute("onclick");
1499    //     if (playFunction!="playTTS()"){
1500    //         playButton.removeAttribute("onclick");
1501    //         playButton.setAttribute("onclick","playTTS()");
1502    //     }
1503    // }
1504}
1505
1506function monitorOriginalTTS() {
1507    // var position = window.EmberAudio.instance.position;
1508    // var duration = window.EmberAudio.instance.duration;
1509    var status = emberAudioStatus();
1510    if (status == "ended" || status == "paused") {
1511        // paused or ended //
1512        document.getElementById("pauseIcon").style.display = "none";
1513        document.getElementById("playIcon").style.display = "";
1514    } else if (status == "playing") {
1515        // playing //
1516        document.getElementById("pauseIcon").style.display = "";
1517        document.getElementById("playIcon").style.display = "none";
1518        setTimeout(monitorOriginalTTS, 100);
1519    }
1520}
1521
1522function emberAudioStatus() {
1523    var playing = window.EmberAudio.instance.playing;
1524    var ended = window.EmberAudio.instance.ended;
1525    if (playing === true && ended === false) {
1526        return "playing";
1527    }
1528    if (ended === true && playing === false) {
1529        return "ended";
1530    }
1531    if (ended === false && playing === false) {
1532        return "paused";
1533    }
1534}
1535
1536function playOriginalTTS() {
1537    if (document.getElementsByClassName("audio-player__play-button")[0]) {
1538        var playbuttons = document.querySelectorAll("a.audio-player__play-button");
1539        for (var x = 0; x < playbuttons.length; x++) {
1540            var button = playbuttons[x];
1541            if (button.id != "playButton") {
1542                button.click();
1543                setTimeout(monitorOriginalTTS, 100);
1544            }
1545        }
1546    }
1547}
1548
1549function playTTS() {
1550
1551    // console.log("PlayTTS");
1552    var pageKey = getTtsPageKey();
1553    if (pageKey && ttsContent['pageKey'] !== pageKey) {
1554        clearTtsAudio();
1555        ttsContent['trimmedContent'] = null;
1556        ttsContent['pageKey'] = null;
1557        setTtsUiState("loading");
1558        checkForText();
1559        waitForTtsContent(pageKey, 0);
1560        return;
1561    }
1562
1563    var source = ttsContent['TTS'];
1564    var trimmedContent = ttsContent['trimmedContent'];
1565
1566    if (source == null) {
1567
1568        // console.log("source = null");
1569
1570        setTtsUiState("loading");
1571
1572        generateSpeechAzure();
1573    } else {
1574
1575        // console.log("source != null");
1576
1577        try {
1578
1579            audioPlayer = document.getElementById("roboVoice");
1580            setTtsUiState("playing");
1581
1582            audioPlayer.play();
1583
1584        } catch (err) {
1585            // console.log(err.stack);
1586        }
1587    }
1588}
1589
1590function waitForTtsContent(pageKey, attempts) {
1591    if (ttsContent['trimmedContent'] && ttsContent['pageKey'] === pageKey) {
1592        generateSpeechAzure();
1593        return;
1594    }
1595    if (attempts > 40) {
1596        setTtsUiState("paused");
1597        return;
1598    }
1599    setTimeout(function () {
1600        waitForTtsContent(pageKey, attempts + 1);
1601    }, 50);
1602}
1603
1604function pauseTTS() {
1605    // console.log("pauseTTS");
1606    try {
1607        setTtsUiState("paused");
1608
1609        audioPlayer = document.getElementById("roboVoice");
1610        audioPlayer.pause();
1611    } catch (err) {
1612        // console.log(err.stack);
1613    }
1614}
1615
1616function resetTTS() {
1617    // console.log("reset");
1618    try {
1619        setTtsUiState("paused");
1620    } catch (err) {
1621        // console.log(err.stack);
1622    }
1623}
1624
1625function checkForPlacement() {
1626    try {
1627        if (document.getElementById("playSpan")) {
1628            var button = document.getElementById("playSpan");
1629            var parent = button.parentElement;
1630            var gParent = parent.parentElement;
1631            var parentClass = parent.getAttribute("class");
1632            if (parentClass.match("large-8") != null) {
1633                if (gParent.getElementsByClassName("large-4")[0]) {
1634                    var nextSibling = gParent.getElementsByClassName("large-4")[0];
1635                    var x = document.adoptNode(button);
1636                    nextSibling.appendChild(x);
1637                }
1638            }
1639        }
1640    } catch (err) {
1641        // console.log(err.stack)
1642    }
1643}
1644
1645function quickRemove(trigger) {
1646    console.log('quickRemove', trigger);
1647    ttsCurrentKey = null;
1648    ttsContent['trimmedContent'] = null;
1649    ttsContent['TTS'] = null;
1650    ttsContent['iframeText'] = [];
1651    ttsContent['pageKey'] = null;
1652    // maybe write a general "safeRemove" function for ij-library that safely removes elements based on provided query selector?
1653
1654    // remove TTS robovoice and content
1655    if (document.getElementById("roboVoice")) {
1656        try {
1657            document.getElementById("roboVoice").parentNode.removeChild(document.getElementById("roboVoice"));
1658            resetTTS();
1659            ttsContent['trimmedContent'] = null;
1660            ttsContent['TTS'] = null;
1661            ttsContent['pageKey'] = null;
1662        } catch (err) {
1663            // console.log(err.stack);
1664        }
1665    }
1666
1667    if (document.getElementById("playSpan")) {
1668        let pageID = InJoy.pageID();
1669        let locale = getLanguageTransl();
1670        // remove play button
1671        if (!document.querySelector("[tts-id='" + pageID + "_" + locale + "']")) {
1672            document.getElementById("playSpan").remove();
1673        }
1674    }
1675}
1676
1677function ttsAuthCheck() {
1678    if (getLanguageTransl() == "hmn") {
1679        return false;
1680    }
1681    return true; // temp override
1682    var currentCourse = getCourseInfo('courseID');
1683    var currentCourseGroup = getCourseInfo('courseGroup');
1684
1685    var ttsCourses = [
1686        // CANADIAN MASTERS //
1687        "5a8d462f-9b85-4c15-9cae-e52b5af8de90", // CANADIAN MASTER: Understanding Breastfeeding eClass
1688        "063b4631-b63b-47e7-858e-479634c00887", // CANADIAN MASTER: Understanding Pregnancy eClass
1689        "af469596-b1a5-4ac9-bfdc-6484810230d2", // CANADIAN MASTER: Understanding Your Newborn eClass
1690        "f72ace4c-e167-4bc3-b3dc-03a9f11cbdb7", // CANADIAN MASTER: Understanding Birth eClass
1691        // Region of Peel // 
1692        // "1a397f89-fccf-4352-a1a0-2a6626da22c6", // Services & Information - Region of Peel (PPU)
1693        // "5e2bac70-2251-5b7d-b15d-c959022de5ae", // Region of Peel Services & Information
1694        "2bf89b57-0cae-4586-aaf0-522fe4571801", // Understanding Breastfeeding eClass - Region of Peel (PPU)
1695        "c9cbad5e-cc78-5591-a683-c45406608e4a", // Understanding Breastfeeding eClass - Region of Peel
1696        "c1ae3939-51e7-43c7-b6c3-4167008bf88b", // Understanding Birth eClass - Region of Peel (CPU)
1697        "43a7166a-f89e-5e45-b42b-51e810c02c7e", // Understanding Birth eClass - Region of Peel
1698        "fd2048b7-456e-40d6-a905-3636e2bd9332", // Understanding Your Newborn eClass - Region of Peel (PPU)
1699        "17692d64-94c5-5f7a-bf17-00d7e6a24f49", // Understanding Your Newborn eClass - Region of Peel
1700        "8bc333c4-7502-4e30-9072-06417fa83c13", // Understanding Pregnancy eClass - Region of Peel (PPU)
1701        "46fab74e-e6e3-51cf-b05b-14916801b8a7", // Understanding Pregnancy eClass - Region of Peel
1702        // Wellington //
1703        "1d82396d-f3fe-4239-8f8e-7f427b101663", // Understanding Birth eClass - Wellington-Dufferin-Guelph Public Health Parent
1704        "1ec9b34f-470e-5a03-bc96-0f4ef007d2b8", // Understanding Birth eClass - Wellington-Dufferin-Guelph Public Health Child
1705        "3a8a7747-3a42-428d-8432-776bc9f626f2", // Understanding Your Newborn eClass - Wellington-Dufferin-Guelph Public Health Parent
1706        "5c93b8fc-7742-509a-95cd-712eb2721fa8", // Understanding Your Newborn eClass - Wellington-Dufferin-Guelph Public Health Child
1707        "15732967-07b9-4282-bd48-1e08724cc0bd", // Understanding Pregnancy eClass - Wellington-Dufferin-Guelph Public Health Parent
1708        "6e80e6dc-45bb-51d8-86ff-26b3f24cf042", // Understanding Pregnancy eClass - Wellington-Dufferin-Guelph Public Health Child
1709        "26269b8b-edae-4cc5-bbe4-c191a681aa32", // Understanding Breastfeeding eClass - Wellington-Dufferin-Guelph Public Health Parent
1710        "f8cf692a-f9ce-5630-82de-776c3729f743", // Understanding Breastfeeding eClass - Wellington-Dufferin-Guelph Public Health Child
1711
1712        "34944c6d-4d06-4844-aeea-8b37ff6d74b7", // Free Birth Stories Class
1713
1714        // Fatherhood eClass
1715        // "b9cca747-3fc1-4119-a4ca-ea78e78a6dd4", // Fatherhood eClass - Parent Locked - Course Group // See Below under All PL Course Groups
1716        "011c4d28-e672-450d-8197-cb77f46b9e8a", // Fatherhood eClass - Parent Unlocked - Course Group
1717        "0c3ca955-b04f-497f-abdc-4f323c484af4", // Fatherhood eClass - Free Preview - Course Group
1718        "2b02090a-a29d-4e06-a468-cc70b26d1798", // Understanding Fatherhood eClass - Parent Course
1719        // "17ee653c-9527-50b0-bf57-757d04ee4a99",
1720        // "0d4f5e73-d5bf-521f-83c2-06044d75f9d3",
1721        // "061601d9-59db-559f-9ea7-20f8ed15ffe9",
1722
1723        // Understanding Pregnancy eClass
1724        // "b303f5d8-31a9-45ad-869d-62cf9a9a507e", // Understanding Pregnancy eClass - Parent Locked - Course Group // See Below under All PL Course Groups
1725        "2c71bec8-a68d-45b2-a80d-bbb5c4c69d9f", // Understanding Pregnancy eClass - Northwestern Health Unit - Course Group
1726        "0793b672-d86d-40c7-85f7-ab5a5093213c", // Understanding Pregnancy eClass - FREE PREVIEW - Course Group
1727        "a53e791b-7c60-4f94-b93c-1529e03c9e8d", // Understanding Pregnancy eClass - Durham Region Health Department - Course Group
1728        "44539fe1-008f-4aa8-bd33-b524dbc5195b", // Understanding Pregnancy eClass - Peterborough Public Health - Course Group
1729        "c06e1e8f-e4aa-4c02-8ed9-cc35ded0afe5", // Understanding Pregnancy eClass - Perth District Health Unit - Course Group
1730        "72558c00-a210-46e3-a6c9-937fafb6f4d3", // Understanding Pregnancy eClass - Niagara Region Public Health - Course Group
1731        "48b4d20f-d516-440a-931e-8a8f221d6fb7", // Understanding Pregnancy eClass - City of Hamilton Public Health - Course Group
1732        "a5cd75a0-87ff-45e8-914a-cbca74bbdf6a", // Understanding Pregnancy eClass - Chatham-Kent Public Health - Course Group
1733        "fadf9e7c-5216-4371-9596-d611b865bbb8", // Understanding Pregnancy eClass - Region of Peel - Course Group
1734        "6c6bf351-dc58-4c2a-9f5d-6e9b27f1b1a0", // Understanding Pregnancy eClass - Haldimand Norfolk Health Unit - Course Group
1735        "a1fb1c12-277a-445d-b06a-9154db2dcf32", // Understanding Pregnancy eClass - HKPR Health Unit - Course Group
1736        "10c3b49a-c2d5-4b57-bc56-f9335788860d", // Understanding Pregnancy eClass - Region of Waterloo - Course Group
1737        "72068cb0-7a16-48dd-80d1-6eda5c3e96ad", // Understanding Pregnancy eClass - Brant County Health Unit - Course Group
1738        "876cf17b-84c4-4499-aefc-d48e6e2f970e", // Understanding Pregnancy eClass - Algoma Public Health - Course Group
1739        "d04adf58-3e3b-4374-a674-28d4bf17d090", // Understanding Pregnancy eClass - Halton Region - Course Group
1740        "dfd60136-2827-40c6-88e7-d78297468b58", // Understanding Pregnancy eClass - Leeds, Grenville and Lanark District Health Unit - Course Group
1741        "23552ec2-c579-4590-9858-db115c1bfc88", // Understanding Pregnancy eClass - Hastings Prince Edward Public Health - Course Group
1742        "11b58b55-f049-439d-abf4-1a31724ac01d", // Understanding Pregnancy eClass - Southwestern Public Health - Course Group
1743        "3510f865-8cc1-47c6-873e-c7f32d5fcb8b", // Understanding Pregnancy eClass - Middlesex-London Health Unit - Course Group
1744        "a44a11a6-d4a4-4d9c-9e3b-2a5d603bf4e8", // Understanding Pregnancy eClass - Wellington-Dufferin-Guelph - Course Group
1745        "2af02240-46f2-4918-bd68-047c6ea186ce", // Understanding Pregnancy eClass - Lehigh Valley Health Network - Course Group
1746        "1b014285-1f4b-4332-9097-0fb593ce6904", // Understanding Pregnancy eClass - Southcentral Foundation - Course Group
1747        "b0fe4c65-e7ac-48a0-9fd7-1d6e0ec2eaa2", // Understanding Pregnancy eClass - Parent Locked - Course Group
1748        // also turn on all child courses (on monday) using the Course Group
1749
1750        // All English Course Groups for Parent Locked Courses
1751        "6c5494ff-55d0-4885-a49f-52ffe5716319", // Understanding Breastfeeding PL Course Group
1752        "5aa9a8c8-c17e-42ec-a8ff-5acbf0ce3445", // Understanding Birth PL Course Group
1753        "e36e94e3-f805-4ee9-9e4d-8360a9179c24", // Understanding Cesarean Birth PL Course Group
1754        "b9cca747-3fc1-4119-a4ca-ea78e78a6dd4", // Understanding Fatherhood PL Course Group
1755        "b5071c4e-f6b1-4dc7-ac4e-b481c1fa8183", // Understanding Infant & Child Safety PL Course Group
1756        "b31fe522-27bf-42d3-85ec-4c2e4e1427bc", // Understanding Your Multiples PL Course Group
1757        "65f8c6a8-6610-4a17-8f78-0dd3a30a3055", // Understanding Your Newborn PL Course Group
1758        "b303f5d8-31a9-45ad-869d-62cf9a9a507e", // Understanding Pregnancy PL Course Group
1759        "79b6181d-0cf4-44f7-b496-cda3516497bc", // Understanding Mother & Baby Care PL Course Group
1760
1761        // All Spanish Course Groups for Parent Locked Courses
1762        "3e48fdb2-1b5d-48a9-942c-e27acf6c7192", // Historias de parto y esenciales en línea
1763        "c865f239-e431-4ac7-92ff-177753a39d4c", // Clase en línea de Entendiendo el parto por cesárea // tagged wrong?
1764        "05d7fec3-d4ac-48c7-be9a-c31774a31f87", // Clase en línea de Entendiendo el parto por cesárea // tagged wrong?
1765        "35fcb438-a3c7-4ce2-bb83-f9340e6102cc", // Clase en línea de Entendiendo el parto
1766        "7b37ac9e-0918-4c0a-97e7-7f771a92a32d", // Clase en línea de Entendiendo la lactancia materna
1767        "e7608ce3-d1de-4eac-8da3-fcd121ad78cd", // Clase en línea de Entendiendo al recién nacido
1768        "24486fdb-ae45-4603-a54f-f315632e13c7", // Clase en línea de Entendiendo el embarazo
1769        "69e6e84b-3918-4382-9d9e-0cb7780da5cc", // Clase en línea de Entendiendo el cuidado de la mamá y el bebé
1770
1771        // All English Course Groups for Parent Unlocked Courses
1772        "e36586f5-1430-4451-9ae4-f2fe5bcef598", // Understanding Infant & Child Safety eClass
1773        "6bd23c21-09da-4e40-a16c-fb0a3a8af5eb", // Understanding Your Multiples eClass
1774        "011c4d28-e672-450d-8197-cb77f46b9e8a", // Understanding Fatherhood eClass
1775        "49fd656f-9573-45ae-bf7d-b19a115bef99", // Understanding Cesarean Birth eClass
1776        "a692333e-8812-487d-8824-90cd74932e0e", // Understanding Your Newborn eClass
1777        "7c7162e9-fcff-4c4d-8b80-33fe43f5c70f", // Understanding Breastfeeding eClass
1778        "32ee1da0-351c-46c1-9b01-9c36ae8fa549", // Understanding Breastfeeding eClass
1779        "b0fe4c65-e7ac-48a0-9fd7-1d6e0ec2eaa2", // Understanding Pregnancy eClass
1780        "3c78b055-fb82-4e97-b32a-cb98b2f7e6f0", // Understanding Birth eClass
1781        "6ec21d35-4229-43eb-9303-d4d91d9a1e1a", // Understanding Mother & Baby Care eClass
1782
1783        // All Spanish Course Groups for Parent Unlocked Courses
1784        "b8c4c0be-3943-4bb4-84bf-a578d5530b8c", // Clase en línea de Entendiendo el embarazo
1785        "d722626e-6ff8-4c09-ab3a-9f2970de8e8c", // Clase en línea de Entendiendo el parto
1786        "fe0c9f13-6364-4510-b461-a9ba9128d9bc", // Clase en línea de entendiendo la lactancia materna
1787        "617573cf-add8-42b5-aa22-5db9dd5d9e69", // Clase en línea de entendiendo al recién nacido
1788        "42a618ab-f2b6-46ee-8083-1d508264d4a8", // Clase en línea de Entendiendo el cuidado de la mamá y el bebé
1789
1790        // Understanding Postpartum Health & Baby Care eClass
1791        "75b11b93-fb1c-491a-8d3c-0c915bffb2d9", // Understanding Postpartum Health & Baby Care eClass InJoy Locked
1792        "1ee1897b-871a-469c-8e09-5b32e8660a77", // Understanding Postpartum Health & Baby Care eClass CourseGroup
1793        "659af41e-538c-4841-b92d-df301596ba7c",
1794        "746b1ae9-1161-4992-aa3f-cb654fd6687b",
1795
1796        // Grandparenting
1797        "fa9478ca-318d-40ff-a199-edbad1babc16",
1798
1799        // Formula Feeding Test Course
1800        "bbe0549b-44cf-43b1-9a34-008952930673",
1801
1802        // Understanding Sibling Preparation eClass
1803        "198b2f85-1ccb-4c85-b8e3-5e8f4b5a888b",
1804
1805        // Understanding Birth eClass UB4 //
1806        "e09b649b-528b-40fd-8030-1c5b4594fe07",
1807
1808        // Understanding Natural Childbirth Techniques //
1809        "e3194d48-dd8c-48b6-9280-5ba82884590c",
1810
1811        // Preparing for Your Hospital Care & Recovery //
1812        "c84b91a9-5ecb-4318-8071-11bc408e5808",
1813        "5008c4b8-6881-4d4d-a146-e353e2076b56",
1814
1815        // Understanding Self Care Web App //
1816        "511bfc85-1911-44c0-89e4-5581667641ed",
1817
1818        // Understanding Breastfeeding eClass - UBF3
1819        "e998930f-2ef0-4cc5-b631-f2ea256b3697",
1820
1821        // Understanding Your Newborn eClass - UYN3
1822        "1b32dbde-4bb7-484b-8da0-ae093d739a7b",
1823
1824        // Understanding Pregnanacy Eclass
1825        "a9a1d1a0-7ffc-4bc2-b4d7-c7a97838122e",
1826
1827        // Dev Testing Course
1828        "42d18937-cfe6-457e-9365-92ca45c65126",
1829        // "52057089-6faf-4a0a-ac5d-aefdda81e3ce"
1830
1831    ];
1832
1833    function checkCourse(id) {
1834        if (id == currentCourse || id == currentCourseGroup) {
1835            return true;
1836        }
1837    }
1838
1839    if (ttsCourses.find(checkCourse) != undefined) {
1840        return true;
1841    } else {
1842        return false;
1843    }
1844}
1845
1846function setColumnWidth() {
1847    if (document.querySelectorAll(".large-4")[0] && document.querySelectorAll(".large-4")[0]) {
1848        document.querySelectorAll(".large-4")[0].style.width = "33.3%";
1849        document.querySelectorAll(".large-8")[0].style.width = "66.6%";
1850        document.querySelectorAll(".large-8")[0].id = "mainColumn";
1851    } else if (document.querySelector("#mainColumn")) {
1852        document.querySelector("#mainColumn").style.width = "100%";
1853    }
1854}
1855
1856function textToSpeech(translations, origin) {
1857    // console.log('textToSpeech',origin);
1858    if (!translations) {
1859        //get translations?
1860        translations = true;
1861    }
1862
1863    let pageID = InJoy.pageID();
1864    let locale = getLanguageTransl();
1865    let pageKey = pageID + "_" + locale;
1866    updateTtsPageKey();
1867    removeStalePlayButton(pageKey);
1868
1869    if (document.querySelector("[tts-id='" + pageKey + "']")) {
1870        return;
1871    }
1872
1873    if (getLanguageTransl() == "en-US" || translations) {
1874        // console.log('textToSpeech');
1875        // quickRemove('textToSpeech');
1876        // remove old player if necessary
1877
1878        var n = 0
1879        function internalCheck() {
1880            try {
1881                if (location.href.indexOf("learn/article") >= 0) {
1882                    // console.log('article');
1883                    let contentDom = document.getElementsByClassName("article-content")[0];
1884                    let targerElements = contentDom.getElementsByTagName("SCRIPT");
1885                    targerElements.forEach(function (currentValue, index, arr) {
1886                        // console.log(currentValue,index,arr);
1887                        // currentValue.parentElement.removeChild(currentValue);
1888                        currentValue.id = "index" + index;
1889                    });
1890
1891                    function removeElements(elements) {
1892                        for (let id in elements) {
1893                            let element = elements[id];
1894                            if (element.parentElement) {
1895                                element.parentElement.removeChild(element);
1896                            }
1897                        }
1898                        if (elements.length > 0) {
1899                            removeElements(elements);
1900                        }
1901                    }
1902
1903                    removeElements(targerElements);
1904                    // console.log("textToSpeech 1562",contentDom); // clean!
1905                    preprocessElement(contentDom);
1906
1907                } else {
1908                    loadTestWindow('CONF.preload.currentUser.currentUser.role.container.cache.dict["controller:lesson/index"].model._data', function () {
1909                        setColumnWidth();
1910                        if (ttsAuthCheck()) {
1911                            if (!document.querySelector("[data-tts=false]")) {
1912                                var pageType = getCourseInfo("topicType");
1913                                if (pageType != 'quiz' && pageType != "survey") {
1914                                    removeOldPlayer();
1915                                    checkForText();
1916                                    checkForPlacement();
1917                                } else {
1918                                    quickRemove("ttsAuthCheck");
1919                                }
1920
1921                            } else {
1922                                quickRemove("ttsAuthCheck");
1923                            }
1924                        }
1925                    });
1926                }
1927            } catch (err) {
1928                // console.log(err.stack);
1929                if (n < 100) {
1930                    n = n + 1;
1931                    setTimeout(internalCheck, 25);
1932                }
1933            }
1934        }
1935        internalCheck();
1936    }
1937}
1938
1939async function surveyTTS() {
1940    // triggered by fetchResponse() in ij-library.js
1941    // "source.e09b649b-528b-40fd-8030-1c5b4594fe07.topicsByID.ee9f89c8-fb6e-47cb-9918-8cdb96e6e9d3.questionsUpdate.0.preText_es-ES_ml",
1942    await InJoy.translator.pageReady()
1943    var elements = window[getEleInfoTransl("pageID") + "_" + getLanguageTransl()];
1944
1945    function getElementIdFromHint(hint) {
1946        for (let id in elements) {
1947            if (id.includes(hint) && id.includes(getLanguageTransl())) {
1948                let humanID = id.replace("_ml", "");
1949                if (id.includes("_ml") && elements[humanID]) {
1950                    // element is ML and human translated exists
1951                    return humanID;
1952                } else if (!id.includes("_ml")) {
1953                    // human translated
1954                    return humanID;
1955                } else {
1956                    // ml and no human
1957                    return id;
1958                }
1959            }
1960        }
1961        return false;
1962    }
1963
1964    var elementOrder = [];
1965    var txt = "";
1966
1967    if (getLanguageTransl() == "en-US") {
1968        // // console.log("English");
1969
1970        if (document.querySelector("[data-tts]")) {
1971            txt = document.querySelector("[data-tts]").innerHTML;
1972        }
1973        else if (getEleInfoTransl("page").startMessage) {
1974            // console.log("data-tts not found");
1975            // txt = txt + getEleInfoTransl("page").startMessage;
1976        }
1977
1978        if (window['quizQuestions'][InJoy.pageID()]) {
1979            let questions = window['quizQuestions'][InJoy.pageID()].questions;
1980            // console.log("Questions",questions);
1981            for (let x = 0; x < questions.length; x++) {
1982                let question = questions[x];
1983                if (question.preTextDisplayOption == "always" && question.preText) {
1984                    // console.log("preText",question.preText);
1985                    txt = txt + question.preText;
1986                }
1987                if (question.preTextDisplayOption2 == "always" && question.preText2) {
1988                    // console.log("preText2",question.preText2);
1989                    txt = txt + question.preText2;
1990                }
1991                // reads the question itself
1992                // if (question.body){
1993                //   // console.log("body",question.body);
1994                //     txt = txt + question.body;
1995                // }
1996            }
1997            // // console.log("Survey Text",txt);
1998        } else {
1999            // console.log('Questions not loaded');
2000        }
2001    } else {
2002        // console.log("Not English");
2003        if (window[getEleInfoTransl("pageID") + "_" + getLanguageTransl()] && window['quizQuestions']) {
2004            // console.log("Translations and Questions");
2005            var elements = window[getEleInfoTransl("pageID") + "_" + getLanguageTransl()];
2006
2007            // console.log("elements:",elements);
2008            if (getEleInfoTransl("page").startMessage) {
2009                let hint = "startMessage";
2010                let id = getElementIdFromHint(hint);
2011                if (id) {
2012                    if (!elementOrder.includes(id)) {
2013                        elementOrder.push(elementOrder);
2014                        txt = txt + elements[id].text;
2015                    }
2016                }
2017            }
2018
2019            let questions = window['quizQuestions'];
2020            for (let x = 0; x < questions.length; x++) {
2021                let question = questions[x];
2022                // console.log("question:",question);
2023                if (question.preTextDisplayOption == "always" && question.preText) {
2024                    let hint = "questionsUpdate." + x + ".preText";
2025                    let id = getElementIdFromHint(hint);
2026                    // console.log("hint:",hint);
2027                    // console.log("id:",id);
2028                    if (id) {
2029                        if (!elementOrder.includes(id)) {
2030                            elementOrder.push(elementOrder);
2031                            txt = txt + elements[id].text;
2032                        }
2033                    }
2034                }
2035                if (question.preTextDisplayOption2 == "always" && question.preText2) {
2036                    let hint = "questionsUpdate." + x + ".preText2";
2037                    let id = getElementIdFromHint(hint);
2038                    if (id) {
2039                        if (!elementOrder.includes(id)) {
2040                            elementOrder.push(elementOrder);
2041                            txt = txt + elements[id].text;
2042                        }
2043                    }
2044                }
2045                if (question.body) {
2046                    let hint = "questionsUpdate." + x + ".body";
2047                    let id = getElementIdFromHint(hint);
2048                    if (id) {
2049                        if (!elementOrder.includes(id)) {
2050                            elementOrder.push(elementOrder);
2051                            txt = txt + elements[id].text;
2052                        }
2053                    }
2054                }
2055            }
2056            // // console.log("Survey Text",txt);
2057        } else {
2058            // console.log("Translations Aren't loaded yet");
2059        }
2060    }
2061
2062    if (txt) {
2063        // console.log("Survey Text Found",txt);
2064        removeOldPlayer();
2065        checkForText(txt);
2066        checkForPlacement();
2067    }
2068}
2069
2070
2071
2072//////////////////////////////////////////////////////////////////////////////////////
2073//////////////////////////////////////////////////////////////////////////////////////
2074//////////////////////////////////////////////////////////////////////////////////////
2075//////////////////////////////////////////////////////////////////////////////////////
2076//////////////////////////////////////////////////////////////////////////////////////
2077
2078// used for the TTS download page https://s3.amazonaws.com/injoyassets/code/tts.html or https://injoyvideos.sharepoint.com/:w:/r/ProdDev/_layouts/15/Doc.aspx?sourcedoc={74C4DF6B-AC46-4A15-9FCB-3814A4E9F97B}&file=Code_Generation_Tools.docx&action=default&mobileredirect=true&DefaultItemOpen=1
2079function downloadTTS(text) {
2080    ttsContent['trimmedContent'] = text;
2081    updateTtsPageKey();
2082    generateSpeechAzure();
2083}
2084
2085function populateVoices() {
2086    if (document.querySelector("#ttsVoiceSelect")) {
2087        document.querySelector("#ttsVoiceSelect").innerHTML = "";
2088
2089        var select = document.createElement("option");
2090        select.selected = true;
2091        select.disabled = true;
2092        select.innerHTML = "Select";
2093        select.value = "";
2094        document.querySelector("#ttsVoiceSelect").appendChild(select);
2095
2096        var target = "https://toolbox.injoyonline.com/InJoyLibrary/injoy/php/ttsAzure.php?voiceList=true";
2097        var xmlhttp = new XMLHttpRequest();
2098        xmlhttp.onreadystatechange = function () {
2099            if (this.readyState == 4 && this.status == 200) {
2100                var output = JSON.parse(this.responseText);
2101                // console.log(output);
2102                output.map(function (value, index) {
2103                    // console.log(value,index);
2104                    var locale = value.Locale;
2105
2106                    if (locale == "en-US" || locale == "es-ES" || locale == "es-MX" || locale == "fr-CA" || locale == "fr-FR" || locale == "pt-PT") {
2107
2108                        var gender = value.Gender;
2109                        if (gender == "Female") {
2110                            var shortName = value.ShortName;
2111                            var option = document.createElement("option");
2112                            option.value = shortName;
2113                            option.innerHTML = shortName;
2114                            option.setAttribute("data-locale", locale);
2115                            document.querySelector("#ttsVoiceSelect").appendChild(option);
2116                        }
2117                    }
2118                });
2119
2120
2121            }
2122        };
2123        xmlhttp.open("POST", target, true);
2124        xmlhttp.send();
2125    }
2126}
2127
2128function setLocale() {
2129    if (document.querySelector("#ttsVoiceSelect")) {
2130        var options = document.querySelector("#ttsVoiceSelect").children;
2131        // console.log(options);
2132        for (var x = 0; x < options.length; x++) {
2133            var currentValue = options[x];
2134            if (currentValue.selected === true) {
2135                var locale = currentValue.getAttribute("data-locale");
2136                document.querySelector("#ttsVoiceSelect").setAttribute("data-locale", locale);
2137            }
2138        }
2139    }
2140}
2141
2142populateVoices();

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.