// ==UserScript== // @name Duolingo Helper // @namespace http://tampermonkey.net/ // @version 1.1.9 // @description This tool helps you listen to music while studying,auto solve // @author @kietxx_163 and @bot1.py // @match https://*.duolingo.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @icon https://d35aaqx5ub95lt.cloudfront.net/images/leagues/da3da435ad26e5c57d4c5235406ff938.svg // @downloadURL none // ==/UserScript== (function() { 'use strict'; let basePing = 100; // Basic Ping (ms) let baseFps = 60; // Basic FPS let ping = basePing; // Current ping value (ms) let fps = baseFps; // Current FPS value let sessionStartTime = Date.now(); // Session start time function isLocalStorageSupported() { try { const testKey = '__testKey'; localStorage.setItem(testKey, testKey); localStorage.removeItem(testKey); return true; } catch (error) { return false; } } if (!isLocalStorageSupported()) { console.error('LocalStorage is not supported.'); return; } const style = document.createElement('style'); style.textContent = ` :root { --text-color: black; /* Default text color */ --background-color: white; /* Default background color */ } @keyframes rainbow-border { 0% { border-color: red; } 14% { border-color: orange; } 28% { border-color: yellow; } 42% { border-color: green; } 57% { border-color: blue; } 71% { border-color: indigo; } 85% { border-color: violet; } 100% { border-color: red; } } #performanceMonitor { position: fixed; top: 10px; right: 10px; padding: 8px; border: 5px solid; border-radius: 8px; font-family: Arial, sans-serif; font-size: 14px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); width: 200px; height: auto; text-align: left; overflow: hidden; cursor: pointer; z-index: 9999; transition: opacity 0.3s ease-in-out, width 0.3s ease-in-out, transform 0.3s ease-in-out; background-color: var(--background-color); /* Use the CSS variable for background color */ background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAAA...'); background-size: 32px 32px; background-repeat: no-repeat; background-position: 10px center; animation: rainbow-border 3s linear infinite; color: var(--text-color); /* Use the CSS variable for text color */ } #performanceMonitor.hidden { width: 80px; transform: scale(0.9); } #performanceContentWrapper { transition: opacity 0.3s ease-in-out; } #performanceContentWrapper.hidden { opacity: 0; height: 0; overflow: hidden; } #performanceMonitor button { display: block; margin-bottom: 5px; cursor: pointer; background-color: #444; /* Fixed button background color */ color: white; /* Fixed button text color */ border: none; border-radius: 4px; padding: 4px 8px; font-size: 12px; transition: background-color 0.3s, transform 0.3s; } #performanceMonitor button:hover { background-color: #666; transform: scale(1.05); } .modal { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0); background-color: rgba(255, 255, 255, 0.9); /* Default modal background */ color: var(--text-color); /* Use the CSS variable for modal text color */ border-radius: 8px; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 10000; transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; opacity: 0; } .modal.show { transform: translate(-50%, -50%) scale(1); opacity: 1; } .modal h3 { margin-bottom: 10px; } .modal label { display: block; margin-bottom: 5px; } .modal input[type="email"], .modal textarea { width: 100%; padding: 8px; margin-bottom: 10px; border: 1px solid #666; border-radius: 4px; background-color: #f9f9f9; /* Default input background color */ color: var(--text-color); /* Use the CSS variable for input text color */ } .modal textarea { height: 100px; /* Set height for feedback textarea */ resize: vertical; /* Allow vertical resizing */ } .modal button { margin-top: 10px; padding: 8px 16px; background-color: #1cb0f6; color: white; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.3s; } .modal button:hover { background-color: #0a7bb0; } #settingsPanel { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0); background-color: rgba(255, 255, 255, 0.9); /* Default settings panel background */ color: var(--text-color); /* Use the CSS variable for settings panel text color */ border-radius: 8px; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 10001; transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; opacity: 0; } #settingsPanel.show { transform: translate(-50%, -50%) scale(1); opacity: 1; } #settingsPanel h3 { margin-bottom: 10px; } #musicMenu { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0); background-color: rgba(255, 255, 255, 0.9); color: var(--text-color); border-radius: 8px; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 10001; transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; opacity: 0; max-height: 80%; overflow-y: auto; } #musicMenu.show { transform: translate(-50%, -50%) scale(1); opacity: 1; } #musicMenu button { display: block; margin-bottom: 5px; cursor: pointer; background-color: #444; color: white; border: none; border-radius: 4px; padding: 4px 8px; font-size: 12px; transition: background-color 0.3s, transform 0.3s; } #musicMenu button:hover { background-color: #666; transform: scale(1.05); } `; document.head.appendChild(style); // Add audio element const audio = document.createElement('audio'); audio.id = 'backgroundMusic'; audio.src = 'https://ia600605.us.archive.org/8/items/NeverGonnaGiveYouUp/jocofullinterview41.mp3'; // Thay thế URL bằng liên kết đến file nhạc của bạn audio.loop = true; // Lặp lại nhạc liên tục document.body.appendChild(audio); const container = document.createElement('div'); container.id = 'performanceMonitor'; container.title = 'Click to hide/show'; document.body.appendChild(container); const contentWrapper = document.createElement('div'); contentWrapper.id = 'performanceContentWrapper'; container.appendChild(contentWrapper); const content = document.createElement('div'); content.id = 'performanceContent'; contentWrapper.appendChild(content); const toggleButton = document.createElement('button'); toggleButton.textContent = 'Hide'; toggleButton.addEventListener('mouseover', () => { toggleButton.style.backgroundColor = '#666'; }); toggleButton.addEventListener('mouseout', () => { toggleButton.style.backgroundColor = '#444'; }); toggleButton.addEventListener('click', () => { const isVisible = !contentWrapper.classList.contains('hidden'); contentWrapper.classList.toggle('hidden', isVisible); toggleButton.textContent = isVisible ? 'Show' : 'Hide'; const monitor = document.getElementById('performanceMonitor'); monitor.classList.toggle('hidden', isVisible); }); container.appendChild(toggleButton); const reloadButton = document.createElement('button'); reloadButton.textContent = 'Reload'; reloadButton.addEventListener('click', () => { location.reload(); }); contentWrapper.appendChild(reloadButton); const discordButton = document.createElement('button'); discordButton.textContent = 'Discord'; discordButton.addEventListener('click', () => { window.open('https://discord.gg/XSXPtD5hD4', '_blank'); }); contentWrapper.appendChild(discordButton); // New Choose Music Button const chooseMusicButton = document.createElement('button'); chooseMusicButton.textContent = 'Choose Music'; chooseMusicButton.addEventListener('click', () => { showMusicMenu(); }); contentWrapper.appendChild(chooseMusicButton); // Music Menu const musicMenu = document.createElement('div'); musicMenu.id = 'musicMenu'; musicMenu.innerHTML = `

Select Music

{ musicMenu.classList.remove('show'); }); musicMenu.querySelectorAll('button[data-music-url]').forEach(button => { button.addEventListener('click', () => { const musicUrl = button.getAttribute('data-music-url'); const audioElement = document.getElementById('backgroundMusic'); audioElement.src = musicUrl; audioElement.play(); chooseMusicButton.textContent = 'Choose Music'; musicMenu.classList.remove('show'); }); }); const feedbackButton = document.createElement('button'); feedbackButton.textContent = 'Feedback'; feedbackButton.addEventListener('click', () => { showFeedbackModal(); }); contentWrapper.appendChild(feedbackButton); const settingsButton = document.createElement('button'); settingsButton.textContent = 'Settings'; settingsButton.addEventListener('click', () => { showSettingsPanel(); }); contentWrapper.appendChild(settingsButton); async function measurePing(url) { try { const start = performance.now(); const response = await fetch(url, { method: 'HEAD' }); await response; const end = performance.now(); const pingValue = Math.round(end - start) + ' ms'; updateDisplay(pingValue); } catch (error) { console.error('Ping Error:', error); updateDisplay('Error'); } } let lastFrameTime = performance.now(); let frameCount = 0; function measureFPS() { const now = performance.now(); const delta = now - lastFrameTime; frameCount++; if (delta >= 1000) { const fpsValue = Math.round((frameCount * 1000) / delta); updateDisplay(null, fpsValue); frameCount = 0; lastFrameTime = now; } requestAnimationFrame(measureFPS); } function updateDisplay(pingValue, fpsValue) { if (pingValue !== undefined) { ping = pingValue; } if (fpsValue !== undefined) { fps = fpsValue; } const elapsedTime = formatSessionTime(Date.now() - sessionStartTime); const display = document.getElementById('performanceContent'); display.innerHTML = `
Ping: ${ping}
FPS: ${fps}
Session Time: ${elapsedTime}
`; } function formatSessionTime(milliseconds) { let seconds = Math.floor(milliseconds / 1000); const hours = Math.floor(seconds / 3600); seconds %= 3600; const minutes = Math.floor(seconds / 60); seconds %= 60; return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; } const feedbackModal = document.createElement('div'); feedbackModal.className = 'modal'; feedbackModal.innerHTML = `

Send Feedback

`; document.body.appendChild(feedbackModal); const settingsPanel = document.createElement('div'); settingsPanel.id = 'settingsPanel'; settingsPanel.innerHTML = `

Settings

`; document.body.appendChild(settingsPanel); function hideAllPanels() { feedbackModal.classList.remove('show'); settingsPanel.classList.remove('show'); musicMenu.classList.remove('show'); document.getElementById('performanceContentWrapper').classList.remove('hidden'); } document.getElementById('sendFeedback').addEventListener('click', () => { const feedback = document.getElementById('feedbackMessage').value; if (feedback) { // Normally, you would send the feedback to your backend here. console.log('Feedback:', feedback); alert('Feedback sent!'); feedbackModal.classList.remove('show'); } else { alert('Please enter your feedback.'); } }); document.getElementById('cancelFeedback').addEventListener('click', () => { feedbackModal.classList.remove('show'); }); document.getElementById('applySettings').addEventListener('click', () => { const fontSize = document.getElementById('fontSize').value + 'px'; const backgroundColor = document.getElementById('backgroundColor').value; const isTransparent = document.getElementById('transparentBackground').checked; const performanceMonitor = document.getElementById('performanceMonitor'); performanceMonitor.style.fontSize = fontSize; performanceMonitor.style.backgroundColor = isTransparent ? 'rgba(255, 255, 255, 0)' : backgroundColor; // Update text color based on background color const textColor = getContrastColor(backgroundColor); document.documentElement.style.setProperty('--text-color', textColor); alert('Settings applied.'); }); document.getElementById('resetSettings').addEventListener('click', () => { document.getElementById('fontSize').value = '14'; document.getElementById('backgroundColor').value = '#ffffff'; document.getElementById('transparentBackground').checked = false; const performanceMonitor = document.getElementById('performanceMonitor'); performanceMonitor.style.fontSize = '14px'; performanceMonitor.style.backgroundColor = 'white'; // Reset text color to default document.documentElement.style.setProperty('--text-color', 'black'); alert('Settings reset to default.'); }); document.getElementById('cancelSettings').addEventListener('click', () => { settingsPanel.classList.remove('show'); }); function showFeedbackModal() { hideAllPanels(); feedbackModal.classList.add('show'); } function showSettingsPanel() { hideAllPanels(); settingsPanel.classList.add('show'); } function showMusicMenu() { hideAllPanels(); musicMenu.classList.add('show'); } function getContrastColor(hex) { // Calculate luminance and return black or white based on contrast const r = parseInt(hex.substring(1, 3), 16); const g = parseInt(hex.substring(3, 5), 16); const b = parseInt(hex.substring(5, 7), 16); const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; return luminance > 128 ? 'black' : 'white'; } measureFPS(); setInterval(() => { measurePing('https://www.google.com'); }, 30000); })(); const keys = () => { const d = (t) => `[data-test="${t}"]`; return { AUDIO_BUTTON: d('audio-button'), BLAME_INCORRECT: d('blame blame-incorrect'), CHALLENGE: '[data-test~="challenge"]', CHALLENGE_CHOICE: d('challenge-choice'), CHALLENGE_CHOICE_CARD: d('challenge-choice-card'), CHALLENGE_JUDGE_TEXT: d('challenge-judge-text'), CHALLENGE_LISTEN_SPELL: d('challenge challenge-listenSpell'), CHALLENGE_LISTEN_TAP: d('challenge-listenTap'), CHALLENGE_TAP_TOKEN: '[data-test*="challenge-tap-token"]', CHALLENGE_TAP_TOKEN_TEXT: d('challenge-tap-token-text'), CHALLENGE_TEXT_INPUT: d('challenge-text-input'), CHALLENGE_TRANSLATE_INPUT: d('challenge-translate-input'), CHALLENGE_TYPE_CLOZE: d('challenge challenge-typeCloze'), CHALLENGE_TYPE_CLOZE_TABLE: d('challenge challenge-typeClozeTable'), CHARACTER_MATCH: d('challenge challenge-characterMatch'), PLAYER_NEXT: [d('player-next'), d('story-start')].join(','), PLAYER_SKIP: d('player-skip'), STORIES_CHOICE: d('stories-choice'), STORIES_ELEMENT: d('stories-element'), STORIES_PLAYER_DONE: d('stories-player-done'), STORIES_PLAYER_NEXT: d('stories-player-continue'), STORIES_PLAYER_START: d('story-start'), TYPE_COMPLETE_TABLE: d('challenge challenge-typeCompleteTable'), WORD_BANK: d('word-bank'), PLUS_NO_THANKS: d('plus-no-thanks'), PRACTICE_HUB_AD_NO_THANKS_BUTTON: d('practice-hub-ad-no-thanks-button') }; }; const TIME_OUT = 500; window.dynamicInput = (element, text) => { const tag = element.tagName === 'SPAN' ? 'textContent' : 'value'; const input = element; const lastValue = input[tag]; input[tag] = text; const event = new Event('input', { bubbles: true }); event.simulated = true; const tracker = input._valueTracker; if (tracker) { tracker.setValue(lastValue); } input.dispatchEvent(event); }; window.clickEvent = new MouseEvent('click', { view: window, bubbles: true, cancelable: true, }); // https://stackoverflow.com/a/39165137 // https://github.com/Venryx/mobx-devtools-advanced/blob/master/Docs/TreeTraversal.md window.getReactFiber = (dom) => { const key = Object.keys(dom).find((key) => { return ( key.startsWith('__reactFiber$') || // react 17+ key.startsWith('__reactInternalInstance$') // react <17 ); }); return dom[key]; }; // Gets Challenge Object function getElementIndex(element) { let result = null; if (element instanceof Array) { for (let i = 0; i < element.length; i++) { result = getElementIndex(element[i]); if (result) break; } } else { for (let prop in element) { if (prop == 'challenge') { if (typeof element[prop] == 'object') return element; return element[prop]; } if (element[prop] instanceof Object || element[prop] instanceof Array) { result = getElementIndex(element[prop]); if (result) break; } } } return result; } function getProps(element) { let propsClass = Object.keys(element).filter((att) => /^__reactProps/g.test(att))[0]; return element[propsClass]; } // Gets the Challenge function getChallenge() { const dataTestDOM = document.querySelectorAll(keys().CHALLENGE); if (dataTestDOM.length > 0) { let current = 0; for (let i = 0; i < dataTestDOM.length; i++) { if (dataTestDOM[i].childNodes.length > 0) current = i; } const currentDOM = dataTestDOM[current]; const propsValues = getProps(currentDOM); const { challenge } = getElementIndex(propsValues); return challenge; } } // Solves the Challenge function classify() { const challenge = getChallenge(); if (!challenge) return; window.actions[challenge.type](challenge); } function pressEnter() { const clickEvent = new MouseEvent('click', { view: window, bubbles: true, cancelable: false, }); const isPlayerNext = document.querySelector(keys().PLAYER_NEXT); if (isPlayerNext !== null) isPlayerNext.dispatchEvent(clickEvent); } // Main Function function main() { try { const isPlayerNext = document.querySelectorAll(keys().PLAYER_NEXT); const isAdScreen = document.querySelector([keys().PLUS_NO_THANKS, keys().PRACTICE_HUB_AD_NO_THANKS_BUTTON].join(',')); if (isPlayerNext !== null && isPlayerNext.length > 0) { if (isPlayerNext[0].getAttribute('aria-disabled') === 'true') classify(); } else if (isAdScreen !== null && isAdScreen.length > 0) { isAdScreen.click(); } setTimeout(pressEnter, 10); // pressEnter(); } catch (e) { // terminal.log(e); } } // To not mess duolingo's own log function setConsole() { const iframe = document.createElement('iframe'); iframe.id = 'logger'; iframe.style.display = 'none'; document.body.appendChild(iframe); window.terminal = iframe.contentWindow.console; } // Calls main() let mainInterval; function solveChallenge() { if (document.getElementById('logger') == null) setConsole(); // Check if its a Skill / Alphabet / Checkpoint URL if (/lesson|practice/gi.test(window.location.href) == true) { clearInterval(mainInterval); mainInterval = setInterval(main, TIME_OUT); } } (solveChallenge)(); window.keys = keys(); window.actions = {}; window.actions.assist = window.actions.definition = (challenge) => { const { choices, correctIndex } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_CHOICE); tokens.forEach((e, i) => { if (i == correctIndex) e.dispatchEvent(clickEvent); }); return { choices, correctIndex }; }; window.actions.characterMatch = (challenge) => { const { pairs } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TAP_TOKEN); pairs.forEach((pair) => { for (let i = 0; i < tokens.length; i++) { if ( tokens[i].innerText === pair.fromToken || tokens[i].innerText === pair.learningToken ) { tokens[i].dispatchEvent(clickEvent); } } }); return { pairs }; }; window.actions.select = window.actions.gapFill = window.actions.readComprehension = window.actions.selectPronunciation = window.actions.listenComprehension = window.actions.characterSelect = (challenge) => { const { choices, correctIndex } = challenge; const { CHALLENGE_CHOICE } = window.keys; document.querySelectorAll(CHALLENGE_CHOICE)[correctIndex].dispatchEvent(clickEvent); return { choices, correctIndex }; }; window.actions.completeReverseTranslation = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TEXT_INPUT); let i = 0; displayTokens.forEach((token) => { if (token.isBlank) { dynamicInput(tokens[i], token.text); i++; } }); return { displayTokens }; }; window.actions.characterIntro = window.actions.dialogue = (challenge) => { const { choices, correctIndex } = challenge; document.querySelectorAll(window.keys.CHALLENGE_JUDGE_TEXT)[correctIndex].dispatchEvent(clickEvent); return { choices, correctIndex }; }; window.actions.judge = (challenge) => { const { correctIndices } = challenge; document.querySelectorAll(window.keys.CHALLENGE_JUDGE_TEXT)[correctIndices[0]].dispatchEvent(clickEvent); return { correctIndices }; }; window.actions.listen = (challenge) => { const { prompt } = challenge; let textInputElement = document.querySelectorAll(window.keys.CHALLENGE_TRANSLATE_INPUT)[0]; dynamicInput(textInputElement, prompt); return { prompt }; }; window.actions.listenComplete = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TEXT_INPUT); let i = 0; displayTokens.forEach((token) => { if (token.isBlank) dynamicInput(tokens[i], token.text); }); return { displayTokens }; }; window.actions.listenIsolation = (challenge) => { const { correctIndex } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_CHOICE); tokens.forEach((e, i) => { if (i == correctIndex) { e.dispatchEvent(clickEvent); } }); return { correctIndex }; }; window.actions.listenMatch = (challenge) => { const { pairs } = challenge; const tokens = document.querySelectorAll('button'.concat(window.keys.CHALLENGE_TAP_TOKEN)); for (let i = 0; i <= 3; i++) { const dataset = getReactFiber(tokens[i]).return.child.stateNode.dataset.test; const word = dataset.split('-')[0]; tokens[i].dispatchEvent(clickEvent); for (let j = 4; j <= 7; j++) { const text = tokens[j].querySelector(window.keys.CHALLENGE_TAP_TOKEN_TEXT).innerText; if (/\s/g.test(dataset) && text.endsWith(` ${word}`)) { tokens[j].dispatchEvent(clickEvent); } else if (text == word) { tokens[j].dispatchEvent(clickEvent); } } } return { pairs } }; window.actions.listenSpell = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_LISTEN_SPELL.concat(' input[type="text"]:not([readonly])')); let i = 0; displayTokens.forEach((word) => { if (!isNaN(word.damageStart)) { for (let c of word.text.substring(word.damageStart, word.damageEnd ?? word.text.length)) { dynamicInput(tokens[i], c); i++; } } }); return { displayTokens }; }; window.actions.listenTap = (challenge) => { const { correctTokens } = challenge; const tokens = Array.from(document.querySelectorAll(window.keys.CHALLENGE_TAP_TOKEN)).filter(e => e.tagName === 'BUTTON'); for (let word of correctTokens) { for (let i of Object.keys(tokens)) { if (tokens[i].innerText === word) { tokens[i].dispatchEvent(clickEvent); tokens.splice(i, 1); break; } } } return { correctTokens }; }; window.actions.match = (challenge) => { const { pairs } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TAP_TOKEN_TEXT); pairs.forEach((pair) => { for (let i = 0; i < tokens.length; i++) { if ( tokens[i].innerText === pair.fromToken || tokens[i].innerText === pair.learningToken ) { tokens[i].dispatchEvent(clickEvent); } } }); return { pairs }; }; window.actions.name = (challenge) => { const { correctSolutions, articles, grader } = challenge; let tokens = document.querySelectorAll(window.keys.CHALLENGE_TEXT_INPUT); if (articles) { correctSolutions.forEach((solution) => { solution = solution.split(' '); solution.forEach((word) => { let i = articles.indexOf(word); if (i > -1) { document.querySelectorAll(window.keys.CHALLENGE_CHOICE)[i].dispatchEvent(clickEvent); solution.splice(solution.indexOf(word), 1); dynamicInput(tokens[0], solution.join(' ')); } }); }); } else { correctSolutions.forEach((solution) => { dynamicInput(tokens[0], solution); }); } return { correctSolutions, articles, grader }; }; window.actions.partialReverseTranslate = (challenge) => { const { displayTokens, grader } = challenge; let tokens = document.querySelectorAll('[contenteditable=true]'); let value = ''; displayTokens.forEach((token) => { if (token.isBlank) value = value + token.text; }); dynamicInput(tokens[0], value); return { displayTokens, grader }; }; window.actions.speak = (challenge) => { const { prompt } = challenge; document.querySelectorAll(window.keys.PLAYER_SKIP)[0].dispatchEvent(clickEvent); return { prompt }; }; window.actions.selectTranscription = (challenge) => { const { choices, correctIndex } = challenge; document.querySelectorAll(window.keys.CHALLENGE_JUDGE_TEXT)[correctIndex].dispatchEvent(clickEvent); return { choices, correctIndex }; }; window.actions.tapCloze = (challenge) => { const { choices, correctIndices } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TAP_TOKEN); for (let i = 0; i < correctIndices.length; i++) { choices.forEach((value, j) => { if (correctIndices[i] == j) { for (let k = 0; k < tokens.length; k++) { if (tokens[k].innerText == value) { tokens[k].dispatchEvent(clickEvent); } } } }); } return { choices, correctIndices }; }; window.actions.tapClozeTable = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TAP_TOKEN_TEXT); displayTokens.forEach((line) => { line.forEach((column) => { column.forEach((word) => { if (word.damageStart) { tokens.forEach((token) => { if (token.innerText == word.text.substring(word.damageStart, word.text.length)) { token.dispatchEvent(clickEvent); } }); } }); }); }); return { displayTokens }; }; window.actions.tapComplete = (challenge) => { const { choices, correctIndices } = challenge; const tokens = document.querySelectorAll(window.keys.WORD_BANK.concat(' ', window.keys.CHALLENGE_TAP_TOKEN_TEXT)); correctIndices.forEach((i) => { tokens[i].dispatchEvent(clickEvent); }); return { choices, correctIndices }; }; window.actions.tapCompleteTable = (challenge) => { const { choices, displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.WORD_BANK.concat(' ', window.keys.CHALLENGE_TAP_TOKEN)); displayTokens.forEach((line) => { line.forEach((column) => { if (column[0].isBlank == true) { tokens.forEach((e) => { if (e.innerText == column[0].text) { e.dispatchEvent(clickEvent); } }); } }); }); return { choices, displayTokens }; }; window.actions.translate = (challenge) => { const { correctTokens, correctSolutions } = challenge; if (correctTokens) { const tokens = document.querySelectorAll(window.keys.CHALLENGE_TAP_TOKEN_TEXT); let ignoreTokeIndexes = []; for (let correctTokenIndex in correctTokens) { for (let tokenIndex in tokens) { const token = tokens[tokenIndex]; if (ignoreTokeIndexes.includes(tokenIndex)) continue; if (token.innerText === correctTokens[correctTokenIndex]) { token.dispatchEvent(clickEvent); ignoreTokeIndexes.push(tokenIndex); break; } } } } else if (correctSolutions) { let textInputElement = document.querySelectorAll( window.keys.CHALLENGE_TRANSLATE_INPUT )[0]; dynamicInput(textInputElement, correctSolutions[0]); } return { correctTokens }; }; window.actions.typeCloze = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TYPE_CLOZE.concat(' input')); let i = 0; displayTokens.forEach((word) => { if (word.damageStart) { dynamicInput(tokens[i], word.text.substring(word.damageStart, word.text.length)); i++; } }); return { displayTokens }; }; window.actions.typeClozeTable = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.CHALLENGE_TYPE_CLOZE_TABLE.concat(' input')); let i = 0; displayTokens.forEach((line) => { line.forEach((column) => { column.forEach((word) => { if (word.damageStart) { dynamicInput(tokens[i], word.text.substring(word.damageStart, word.text.length)); i++; } }); }); }); return { displayTokens }; }; window.actions.typeCompleteTable = (challenge) => { const { displayTokens } = challenge; const tokens = document.querySelectorAll(window.keys.TYPE_COMPLETE_TABLE.concat(' input')); let index = 0; displayTokens.forEach((line) => { line.forEach((column) => { if (column[0].isBlank == true) { dynamicInput(tokens[index], column[0].text); index++; } }); }); return { displayTokens }; };