// ==UserScript== // @name Duolingo HearEverything // @namespace http://tampermonkey.net/ // @version 0.17 // @description Let's you hear phrases and words from single choice questions based on your browsers speech synthesis (right now only phrases). // @author Esh // @match https://*.duolingo.com/* // @grant none // @downloadURL none // ==/UserScript== // 0.7: Mutation Observer instead of setInterval // 0.6: Add voice to choices on click // 0.6.1: check why not the innerText of the answer is displayed in the full sentence? // 0.8.1: fix speaking numbers for options // 0.9: Move speak button near the continue button // 0.10.2: set better newPage = true - deleted // maybe bind it to the continue button // Continue // data-test="blame blame-correct" and blame-incorrect could be a good try // maybe bound it to the check button? // if check-button && reset = false newPage = true, reset = true // if read-button created reset = false // div class= _10vOG && data-test="player-next" && disabled // 0.10.3: debug quirks from setting newPage // 0.11: cleaned up some code // 0.12: finally got rid of the new page problem // 0.13: show some debug infos on the page // 0.14: more working reading // 0.15: added more challenges to read // 0.16: Challenges, which work (some partially) // FORM, TRANSLATE, DIALOGUE, GAP_FILL, COMPLETE_REVERSE_TRANSLATION, TAP_COMPLETE // 0.17: added shortcut ALT+l // TODO: add listening button for question at DIALOGUE // TODO: at DIALOG should maybe read only after right answer (or make option for this) // TODO: clean up the script // TODO: translateInput // data-test="challenge-translate-input // but data-test="challenge challenge-listen" is not good // TODO: selectTranscription should not bind EventListeners because they already have some from Duo // data-test="challenge challenge-selectTranscription" // TODO: gapFill // data-test="challenge challenge-gapFill" to find out if it is such a challenge // TODO: data-test="challenge challenge-translate // data-test="challenge-translate-prompt // select words to a sentence, word bank // TODO: challenge dialogue // data-test="challenge challenge-dialogue" // looks like the same rules as challenge-form-prompt // data-test="challenge-choice" instead of aria-select? // TODO: completeReverseTranslation // data-test="challenge challenge-completeReverseTranslation" // make harder gives you: data-test="challenge-translate-input" // TODO: start also on first page // TODO: Tipp-Page // TODO: Hoots // TODO: selector where the speak button should be // TODO: translateInput speaker position top - where to place? // TODO: Voice selector (automagic) - 1/4 done // TODO: Add autoplay selector // more examples on the bottom const buttonPosition = 'bottom'; // bottom / top allowed const voiceSelect = 10; // insert voice number here const lang = 'fr'; const autoplay = false; const DEBUG = false; /* Find your language number here 0: SpeechSynthesisVoice {voiceURI: "Microsoft Katja Online (Natural) - German (Germany)", name: "Microsoft Katja Online (Natural) - German (Germany)", lang: "de-DE", localService: false, default: true} 1: SpeechSynthesisVoice {voiceURI: "Microsoft Natasha Online (Natural) - English (Australia)", name: "Microsoft Natasha Online (Natural) - English (Australia)", lang: "en-AU", localService: false, default: false} 2: SpeechSynthesisVoice {voiceURI: "Microsoft Clara Online (Natural) - English (Canada)", name: "Microsoft Clara Online (Natural) - English (Canada)", lang: "en-CA", localService: false, default: false} 3: SpeechSynthesisVoice {voiceURI: "Microsoft Mia Online (Natural) - English (United Kingdom)", name: "Microsoft Mia Online (Natural) - English (United Kingdom)", lang: "en-GB", localService: false, default: false} 4: SpeechSynthesisVoice {voiceURI: "Microsoft Neerja Online (Natural) - English (India)", name: "Microsoft Neerja Online (Natural) - English (India)", lang: "en-IN", localService: false, default: false} 5: SpeechSynthesisVoice {voiceURI: "Microsoft Aria Online (Natural) - English (United States)", name: "Microsoft Aria Online (Natural) - English (United States)", lang: "en-US", localService: false, default: false} 6: SpeechSynthesisVoice {voiceURI: "Microsoft Guy Online (Natural) - English (United States)", name: "Microsoft Guy Online (Natural) - English (United States)", lang: "en-US", localService: false, default: false} 7: SpeechSynthesisVoice {voiceURI: "Microsoft Elvira Online (Natural) - Spanish (Spain)", name: "Microsoft Elvira Online (Natural) - Spanish (Spain)", lang: "es-ES", localService: false, default: false} 8: SpeechSynthesisVoice {voiceURI: "Microsoft Dalia Online (Natural) - Spanish (Mexico)", name: "Microsoft Dalia Online (Natural) - Spanish (Mexico)", lang: "es-MX", localService: false, default: false} 9: SpeechSynthesisVoice {voiceURI: "Microsoft Sylvie Online (Natural) - French (Canada)", name: "Microsoft Sylvie Online (Natural) - French (Canada)", lang: "fr-CA", localService: false, default: false} 10: SpeechSynthesisVoice {voiceURI: "Microsoft Denise Online (Natural) - French (France)", name: "Microsoft Denise Online (Natural) - French (France)", lang: "fr-FR", localService: false, default: false} 11: SpeechSynthesisVoice {voiceURI: "Microsoft Swara Online (Natural) - Hindi (India)", name: "Microsoft Swara Online (Natural) - Hindi (India)", lang: "hi-IN", localService: false, default: false} 12: SpeechSynthesisVoice {voiceURI: "Microsoft Elsa Online (Natural) - Italian (Italy)", name: "Microsoft Elsa Online (Natural) - Italian (Italy)", lang: "it-IT", localService: false, default: false} 13: SpeechSynthesisVoice {voiceURI: "Microsoft Nanami Online (Natural) - Japanese (Japan)", name: "Microsoft Nanami Online (Natural) - Japanese (Japan)", lang: "ja-JP", localService: false, default: false} 14: SpeechSynthesisVoice {voiceURI: "Microsoft SunHi Online (Natural) - Korean (Korea)", name: "Microsoft SunHi Online (Natural) - Korean (Korea)", lang: "ko-KR", localService: false, default: false} 15: SpeechSynthesisVoice {voiceURI: "Microsoft Colette Online (Natural) - Dutch (Netherlands)", name: "Microsoft Colette Online (Natural) - Dutch (Netherlands)", lang: "nl-NL", localService: false, default: false} 16: SpeechSynthesisVoice {voiceURI: "Microsoft Zofia Online (Natural) - Polish (Poland)", name: "Microsoft Zofia Online (Natural) - Polish (Poland)", lang: "pl-PL", localService: false, default: false} 17: SpeechSynthesisVoice {voiceURI: "Microsoft Francisca Online (Natural) - Portuguese (Brazil)", name: "Microsoft Francisca Online (Natural) - Portuguese (Brazil)", lang: "pt-BR", localService: false, default: false} 18: SpeechSynthesisVoice {voiceURI: "Microsoft Svetlana Online (Natural) - Russian (Russia)", name: "Microsoft Svetlana Online (Natural) - Russian (Russia)", lang: "ru-RU", localService: false, default: false} 19: SpeechSynthesisVoice {voiceURI: "Microsoft Emel Online (Natural) - Turkish (Turkey)", name: "Microsoft Emel Online (Natural) - Turkish (Turkey)", lang: "tr-TR", localService: false, default: false} 20: SpeechSynthesisVoice {voiceURI: "Microsoft Xiaoxiao Online (Natural) - Chinese (Mainland)", name: "Microsoft Xiaoxiao Online (Natural) - Chinese (Mainland)", lang: "zh-CN", localService: false, default: false} 21: SpeechSynthesisVoice {voiceURI: "Microsoft Yunyang Online (Natural) - Chinese (Mainland)", name: "Microsoft Yunyang Online (Natural) - Chinese (Mainland)", lang: "zh-CN", localService: false, default: false} 22: SpeechSynthesisVoice {voiceURI: "Microsoft HiuGaai Online (Natural) - Chinese (Hong Kong)", name: "Microsoft HiuGaai Online (Natural) - Chinese (Hong Kong)", lang: "zh-HK", localService: false, default: false} 23: SpeechSynthesisVoice {voiceURI: "Microsoft HsiaoYu Online (Natural) - Chinese (Taiwan)", name: "Microsoft HsiaoYu Online (Natural) - Chinese (Taiwan)", lang: "zh-TW", localService: false, default: false} 24: SpeechSynthesisVoice {voiceURI: "Microsoft Hedda - German (Germany)", name: "Microsoft Hedda - German (Germany)", lang: "de-DE", localService: true, default: false} 25: SpeechSynthesisVoice {voiceURI: "Microsoft Katja - German (Germany)", name: "Microsoft Katja - German (Germany)", lang: "de-DE", localService: true, default: false} 26: SpeechSynthesisVoice {voiceURI: "Microsoft Stefan - German (Germany)", name: "Microsoft Stefan - German (Germany)", lang: "de-DE", localService: true, default: false} */ var synth = window.speechSynthesis; var voices = []; //let parent = HTMLElement; var newPage = false; var addedSpeech = false; var speakerButton = ` `; // Element definitions const WRONG_ANSWER_CLASS = '._1UqAr._1sqiF'; const ANSWER_CLASS = '._1UqAr'; const RIGHT_ANSWER_CLASS = '._1UqAr._1Nmv6'; const RIGHT_CLASS = '._1Nmv6'; const WRONG_CLASS = '._1sqiF'; const ANSWER_HEADLINE = '._1x6Dk'; const ANSWER_CONTAINER = '._2ez4I'; // used page types const FORM = 'challenge challenge-form'; const TRANSLATE = 'challenge challenge-translate'; // almost the same than challenge-form // add const DIALOGUE = 'challenge challenge-dialogue'; // unused page types // almost the same as challenge-form const GAP_FILL = 'challenge challenge-gapFill'; // looks like it should be optional to read it loud const LISTEN_COMPREHENSION = 'challenge challenge-listenComprehension'; const READ_COMPREHENSION = 'challenge challenge-readComprehension'; // almost the same as challenge-translate const COMPLETE_REVERSE_TRANSLATION = 'challenge challenge-completeReverseTranslation'; const LISTEN = 'challenge challenge-listen'; // Word bank stuff // is not read out by Duo all the time (but at "Tap what you hear"); const LISTEN_TAP = 'challenge challenge-listenTap'; // has word bank and some filled up answer to read const TAP_COMPLETE = 'challenge challenge-tapComplete'; /* pack all spans and in the div only where _2Hlc9 is missing