// here to add a speaker button if I want for the entry text
// or before the first data-test="hint-sentence"
// text to read = document.querySelector('[data-test="hint-sentence"]').parentNode.innerText
const READ_COMPREHENSION = 'challenge challenge-readComprehension';
const LISTEN_COMPREHENSION = 'challenge challenge-listenComprehension';
const COMPLETE_REVERSE_TRANSLATION = 'challenge challenge-completeReverseTranslation';
const TAP_COMPLETE = 'challenge challenge-tapComplete';
const GAP_FILL = 'challenge challenge-gapFill';
const NAME = 'challenge challenge-name';
// unused page types
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
// duo reads aloud
const SELECT_TRANSCRIPTION = 'challenge challenge-selectTranscription';
// allowed challenge types
const TEST = [FORM, TRANSLATE, DIALOGUE, GAP_FILL, COMPLETE_REVERSE_TRANSLATION, TAP_COMPLETE, LISTEN_COMPREHENSION, READ_COMPREHENSION, NAME];
var buttonDisabled = true;
function debug(s) {
console.debug(LOG_STRING + s);
}
window.onload = function() {
//_1UqAr - class for answer
// data-test="challenge-form-prompt" - tag with fill-ins
//data-prompt
//aria-checked tag with fill-in options
// _1Nmv6 - class for right answer
// _1sqiF - class for wrong answer
// data-test="challenge-translate-input" - free input field (lang="fr" / lang="en")
// _1Nmv6 - class for right answer
// _1UqAr - answer text (if wrong / typo)
//
${voices[i].name}`;
}
let styleCheckbox = 'style="vertical-align: bottom;"';
let configTranslate = `
`;
document.querySelector('[role="progressbar"]').insertAdjacentElement('afterend',configButton);
configButton.insertAdjacentElement('afterend', configDiv);
configButton.addEventListener('click', function () { togglePopout('hearEverythingConfig'); });
let configLanguage = document.getElementById('configLanguage')
configLanguage.querySelector('[value="' + voiceSelect + '"]').setAttribute('selected', true);
configLanguage.addEventListener('change', function() {
voiceSelect = configLanguage.options[configLanguage.selectedIndex].value;
GM_setValue('voiceSelect', voiceSelect);
setVoice();
});
document.getElementById('he_ct_auto').checked = config.he_ct_auto;
document.getElementById('he_cgf_auto').checked = config.he_cgf_auto;
document.getElementById('he_cgf_click').checked = config.he_cgf_click;
document.getElementById('he_ctc_auto').checked = config.he_ctc_auto;
document.getElementById('he_ctc_click').checked = config.he_ctc_click;
document.getElementById('he_cf_auto').checked = config.he_cf_auto;
document.getElementById('he_cf_click').checked = config.he_cf_click;
document.getElementById('he_cd_auto').checked = config.he_cd_auto;
document.getElementById('he_cd_click').checked = config.he_cd_click;
document.getElementById('he_cd_autointro').checked = config.he_cd_autointro;
document.getElementById('hearEverythingConfig').addEventListener('change', function(e) {
// console.debug(LOG_STRING + 'Target ID = ' + e.target.id);
GM_setValue(e.target.id, e.target.checked);
config[e.target.id] = e.target.checked;
});
}
}
/* function configListener(e) {
switch(e.target) {
case 'he_ct_auto':
GM_setValue('HearEverything_ct_autoplay', e.target.checked); */
function start() {
if (document.querySelector('[data-test="challenge-header"]')) {
addConfig();
buildDebug();
checkNewPage();
let challenge = getChallengeType();
if(challenge !== null) {
// if we have an accepted challenge, we want to process this page, so we set newPage;
// maybe replace it with blame exists and blame does not exist for addedSpeech ...
if (newPage === true) {
// ._3MD8I also available for wrong answers
// better use blame
if (document.querySelector(ANSWER_QS) !== null) {
renderAnswerSpeakButton();
} else if(addedSpeech===false) {
if (document.querySelectorAll(CHALLENGE_JUDGE_QS).length!==0) {
if (challenge[0] === LISTEN_COMPREHENSION) { // || challenge[0] === READ_COMPREHENSION) {
let hint = document.querySelectorAll(HINT_SENTENCE_QS)[1].innerText.replace('...', '');
addSpeech(CHALLENGE_JUDGE_QS, hint);
addedSpeech = true;
}
if (challenge[0] === FORM && config.he_cf_click === true) {
addSpeech(CHALLENGE_JUDGE_QS);
addedSpeech = true;
}
if (challenge[0] === GAP_FILL && config.he_cgf_click === true) {
addSpeech(CHALLENGE_JUDGE_QS);
addedSpeech = true;
}
if (challenge[0] === DIALOGUE && config.he_cd_click === true) {
addSpeech(CHALLENGE_JUDGE_QS);
addedSpeech = true;
}
} else if (document.querySelectorAll(CHALLENGE_TAP_TOKEN_QS).length !== 0) {
if (challenge[0] === TAP_COMPLETE && config.he_ctc_click === true) {
addSpeech(CHALLENGE_TAP_TOKEN_QS);
addedSpeech = true;
}
}
/* console.groupCollapsed('Add hearing abilities (options)');
console.debug(getChallengeType()[0]);
// if eventListeners were not bound yet
addSpeech();
addedSpeech = true;
console.debug('Now speech is attached to options');
console.groupEnd('Add hearing abilities (options)'); */
}
renderIntroSpeakButton();
}
} else {
// we detected no content to use, so we are not interested in this page
newPage = false;
}
} // end challenge-header detection
}
function prepareChallengeGapFill() {
let answer;
if (document.querySelector(RIGHT_ANSWER_QS)) {
answer = document.querySelector(RIGHT_OPTION_QS).innerText;
}
if (document.querySelector(WRONG_ANSWER_QS)) {
let answerElement = document.querySelector(ANSWER_CLASS);
if(answerElement.lastElementChild) {
answer = answerElement.lastElementChild.innerText;
} else {
answer = answerElement.innerText;
}
}
// question
let read = document.querySelector(HINT_SENTENCE_QS).parentNode.innerText;
// new type, which has two blanc places
if (answer.includes('...')) {
let answers = answer.split(' ... ');
debug('answer 1 = ' + answers[0]);
debug('answer 2 = ' + answers[1]);
let reads = read.split('\n');
debug('reads = ' + reads);
if (reads.length === 2) {
read = answers[0] + reads[0] + answers[1] + reads[1];
} else {
read = reads[0] + answers[0] + reads[1] + answers[1] + reads[2];
}
} else {
// if the answer is at the start of the sentence, there's no \n
if (read.includes('\n')) {
read = read.replace('\n', answer);
} else {
read = answer + read;
}
}
document.querySelector(HINT_SENTENCE_QS).parentNode.innerHTML = `${read}`;
return read;
}
function prepareChallengeForm() {
let answer;
if (document.querySelector(RIGHT_ANSWER_QS)) {
answer = document.querySelector(RIGHT_OPTION_QS).innerText;
}
if (document.querySelector(WRONG_ANSWER_QS)) {
let answerElement = document.querySelector(ANSWER_CLASS);
if(answerElement.lastElementChild) {
answer = answerElement.lastElementChild.innerText;
} else {
answer = answerElement.innerText;
}
}
let read = document.querySelector(FORM_PROMPT_QS).getAttribute('data-prompt').replace(/_+/, answer);
document.querySelector(FORM_PROMPT_QS).innerHTML = `${read}`;
return read;
/* let read;
let solution = document.querySelector('._1UqAr');
// if it's the right solution, we get it from the selected choice
if (solution.classList.contains('_1Nmv6')) {
read = formPrompt.getAttribute('data-prompt').replace(/_+/,document.querySelector('[aria-checked="true"] div').innerText);
console.debug('Form Prompt: right answer');
} else {
// if it's wrong, we have to get it from the right solution display
read = formPrompt.getAttribute('data-prompt').replace(/_+/,solution.innerText);
console.debug('Form Prompt: wrong answer');
}
if (DEBUG) document.querySelector('#mySentence').innerText = read;
return read;
*/
}
function prepareChallengeName() {
let read;
if (document.querySelector(RIGHT_ANSWER_QS)) {
read = document.querySelector(TEXT_INPUT_QS).value;
}
if (document.querySelector(WRONG_ANSWER_QS)) {
read = document.querySelector(ANSWER_CLASS).innerText;
}
return read;
}
function prepareChallengeTranslate() {
let read;
if (document.querySelector(RIGHT_ANSWER_QS)) {
if (document.querySelector(WORD_BANK_QS)) {
// debug('innerText = ' + document.querySelector(CHALLENGE_TAP_TOKEN_QS).parentNode.parentNode.innerText);
read = document.querySelector(CHALLENGE_TAP_TOKEN_QS).parentNode.parentNode.innerText.replace(/\n/g, ' ');
read = read.replace(/' /g, "'");
} else {
let tI = document.querySelector(TRANSLATE_INPUT_QS);
if (tI.lang === config.lang) read = tI.innerHTML;
}
}
if (document.querySelector(WRONG_ANSWER_QS)) {
let answer = document.querySelector(ANSWER_CLASS);
if(answer.lastElementChild) {
read = answer.lastElementChild.innerText;
} else {
read = answer.innerText;
}
}
if (document.querySelector(SPEAKER_BUTTON_QS)) read = '';
// console.debug('HearEverything: read = ' + read);
return read;
/*
if(document.querySelector('._1UqAr._1Nmv6')) {
read = document.querySelector('._1UqAr._1Nmv6').innerText;
} else if(document.querySelector('._1UqAr._1sqiF')) {
if (document.querySelector('._1UqAr._1sqiF').lastElementChild) {
read = document.querySelector('._1UqAr._1sqiF').lastElementChild.innerText;
} else {
read = document.querySelector('._1UqAr._1sqiF').innerText;
}
} else {
let translateInput = document.querySelector('[data-test="challenge-translate-input"]');
if (translateInput !== null && translateInput.lang === lang) {
//read = readTranslateInput(translateInput);
read = translateInput.textContent;
}
} */
}
function prepareChallengeTapComplete() {
let read;
if (document.querySelector(RIGHT_ANSWER_QS)) {
read = document.querySelector(HINT_SENTENCE_QS).parentNode.innerText.replace(/\n/g, '');
}
if (document.querySelector(WRONG_ANSWER_QS)) {
read = document.querySelector(ANSWER_CLASS).innerText;
}
// console.debug('HearEverything: read = ' + read);
return read;
}
function prepareChallengeDialogue() {
let read;
//let hints = document.querySelectorAll('[data-test="hint-token"]');
let speaker1 = document.querySelector('[class="' + DIALOGUE_SPEAKER_CLASS + '"]').innerText;
//hints.forEach(function(hint) { speaker1 += hint; });
let speaker2;
if(document.querySelector(WRONG_ANSWER_QS)) {
speaker2 = document.querySelector('._1UqAr._1sqiF').innerText;
} else {
speaker2 = document.querySelector('[aria-checked="true"]').querySelector('[data-test="challenge-judge-text"]').innerText;
}
read = speaker1 + '\n' + speaker2;
return read
}
function introChallengeDialogue() {
let read = document.querySelector(HINT_SENTENCE_QS).parentNode.innerText;
let speaker = document.createElement('div');
speaker.innerHTML = speakerButton;
speaker.children[0].id = SPEAK_INTRO;
speaker.children[0].style = 'width:40px; height:40px; background:transparent; margin-left:-16px; margin-right:0px;padding-bottom:5px';
document.querySelector(HINT_SENTENCE_QS).insertAdjacentElement('beforeBegin', speaker);
return read;
}
function prepareChallengeReadComprehension() {
let read;
//let hints = document.querySelectorAll('[data-test="hint-token"]');
let speaker1 = document.querySelector(HINT_SENTENCE_QS).innerText;
//hints.forEach(function(hint) { speaker1 += hint; });
let speaker2;
if(document.querySelector(WRONG_ANSWER_QS)) {
speaker2 = document.querySelector(ANSWER_CLASS).innerText;
} else {
speaker2 = document.querySelector(RIGHT_OPTION_QS).innerText;
}
read = speaker1 + '\n' + document.querySelectorAll(HINT_SENTENCE_QS)[1].innerText.replace('...', ' ' +speaker2);
return read
}
function renderIntroSpeakButton() {
if (document.querySelector(SPEAK_INTRO_QS) === null) {
let read = '';
let challenge = getChallengeType()[0];
if (challenge === DIALOGUE) {
read = introChallengeDialogue();
}
if (read !== '') {
debug('intro = ' + read);
let utter = generateUtter(read);
addSpeakListener(SPEAK_INTRO, utter, read);
if (challenge === DIALOGUE && config.he_cd_autointro) document.querySelector(SPEAK_INTRO_QS).click();
}
}
}
function renderAnswerSpeakButton() {
// document.querySelector('._1UqAr._1Nmv6').innerText; Always the answer?
// let test = [FORM, TRANSLATE, DIALOGUE, GAP_FILL, COMPLETE_REVERSE_TRANSLATION];
// console.groupCollapsed('Add hearing abilities (input)');
// console.debug(getChallengeType()[0]);
// console.debug('Solution detected');
let read = '';
let challenge = getChallengeType()[0];
if (challenge === FORM) {
read = prepareChallengeForm();
/*
let formPrompt = document.querySelector('[data-test="challenge-form-prompt"]');
if (formPrompt !== null) { read = readFormPrompt(formPrompt); }
*/
}
if (challenge === TRANSLATE) {
read = prepareChallengeTranslate();
}
if (challenge === DIALOGUE) {
read = prepareChallengeDialogue();
}
if (challenge === READ_COMPREHENSION) {
read = prepareChallengeReadComprehension();
}
if (challenge === NAME) {
read = prepareChallengeName();
}
if (challenge === GAP_FILL) {
read = prepareChallengeGapFill();
}
if (challenge === COMPLETE_REVERSE_TRANSLATION) {
read = prepareChallengeTranslate();
/*
let translateInput = document.querySelector('[data-test="challenge-translate-input"]');
if (translateInput !== null && translateInput.lang === lang) { read = readTranslateInput(translateInput); }
*/
}
if (challenge === TAP_COMPLETE) {
read = prepareChallengeTapComplete();
}
console.debug('HearEverything: read = ' + read);
let utter = generateUtter(read);
// add speaker button to answer and fill in the correct answer in the headline
updateText(read);
// if we have added the speaker button, we find it in the document
addSpeakListener('speak', utter, read);
// do it for every page to trigger the Duo speaker button also
document.removeEventListener('keydown', myShortcutListener);
document.addEventListener('keydown', myShortcutListener);
newPage = false;
// console.debug('Now it\'s an old page');
addedSpeech = false;
// console.debug('Reset: speech isn\'t attached to options any more');
if (DEBUG) document.querySelector('#myOptions').innerText = 'disabled';
// if you like autoplay, it waits 1 second an plays it
if (((challenge === TRANSLATE) || (challenge === COMPLETE_REVERSE_TRANSLATION)) && config.he_ct_auto === true) {
timeoutAutoplay(challenge, utter);
}
if (challenge === GAP_FILL && config.he_cgf_auto === true) {
timeoutAutoplay(challenge, utter);
}
if (challenge === TAP_COMPLETE && config.he_ctc_auto === true) {
timeoutAutoplay(challenge, utter);
}
if (challenge === FORM && config.he_cf_auto === true) {
timeoutAutoplay(challenge, utter);
}
if (challenge === DIALOGUE && config.he_cd_auto === true) {
timeoutAutoplay(challenge, utter);
}
// console.groupEnd('Add hearing abilities (input)');
}
function timeoutAutoplay(challenge, utter) {
setTimeout(function() {
console.debug(LOG_STRING + 'auto play ' + challenge);
synth.cancel();
synth.speak(utter);
},config.ap_timeout);
}
function addSpeakListener(id, utter, read) {
let speak = document.querySelector('#' + id);
if(speak) {
speak.addEventListener('click',function () { synth.cancel(); synth.speak(utter); });
// console.debug('EventListener bound to speak button');
if (DEBUG) document.querySelector('#mySentence').innerText = read;
document.getElementById(id).title = read;
} else {
console.debug('HearEverything: No speak button found');
}
}
function generateUtter(read) {
let utter = new SpeechSynthesisUtterance(read);
utter.voice = voices[voiceSelect];
utter.volume = 1;
utter.pitch = 1;
utter.rate = 1;
utter.lang = config.lang;
return utter;
}
function myShortcutListener(event) {
let speak = document.querySelector('#speak');
let duoSpeak = document.querySelector(SPEAKER_BUTTON_QS);
// ALT + l combo
if (event.altKey && event.key === 'l') {
if (speak) { speak.click(); }
else if (duoSpeak) duoSpeak.click();
console.debug(LOG_STRING + 'alt = ' + event.altKey + ' + ' + event.key);
}
}
function readTapComplete(tap) {
let read = '';
let words = tap.childNodes;
words.forEach(function(word) {
if (word.nodeName === 'SPAN') read += word.children[0].innerText;
if (word.nodeName === 'DIV') {
read += word.querySelector('[class="_2Z2xv"]').children[0].innerText;
}
});
return read;
}
// gives some debug information directly in the Duo-GUI
function buildDebug() {
if(DEBUG) {
if(!document.querySelector('#myChallenge')) {
let debug = document.createElement('div');
debug.innerHTML = `Challenge-Name: ${getChallengeType(true)[0]}Sentence to speak: Speak options: disabled`;
debug.style = "font-size: small; text-align:left; display:grid;";
document.querySelector('[data-test="challenge-header"]').insertAdjacentElement('afterend', debug);
}
}
}
function checkNewPage() {
if(!document.querySelector('#myNewPage')) {
let nP = document.createElement('div');
nP.id = 'myNewPage';
document.querySelector('[data-test="challenge-header"]').insertAdjacentElement('afterend', nP);
//console.debug('---- div - newPage ----');
console.debug('HearEverything: Challenge Type = ' + getChallengeType(true)[0]);
newPage = true;
synth.cancel();
} else {
//console.debug('---- div - oldPage ----');
}
}
function readTranslateInput(translateInput) {
/* TODO: Word bank
*/
// does not exist on right answers?!
let read = '';
let solution = document.querySelector(ANSWER_CLASS);
if (document.querySelector(ANSWER_CONTAINER).childNodes.length === 1) {
// right answer
read = translateInput.innerHTML;
console.debug('Translate Input: right answer');
/* another correct answer seems not to be recognized as correct answer
it reads the other answer given not my answer
Another correct solution:
Une pizza, s'il vous plaît.
*/
} else if (solution.classList.contains(RIGHT_CLASS)) {
read = '';
// pay attention to accents
// better read the solution here
/*
Pay attention to the accents.
J'habite en Europe.
*/
// or
/*
Julia travaille à Paris.
*/
let span = solution.querySelector('span');
if(span) {
for (let i=0; i
You have a typo.
un passeport américain
*/
/* did not work for - it reads out "undefinedundefined"
Common mistake!
Correct solution:Tu prends la voiture ?
*/
console.debug('Translate Input: right answer with little mistake');
} else {
/* // totally wrong answer
let span = solution.querySelector('span');
if(span) {
for (let i=0; i
${speakerButton}
${t}