您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Play audio in KaniWani upon correct answers
// ==UserScript== // @name KaniWani audio // @namespace http://tampermonkey.net/ // @version 0.27 // @description Play audio in KaniWani upon correct answers // @author voithos // @match *://*.kaniwani.com/* // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== // Based on original version by CometZero: // https://greasyfork.org/en/scripts/25373-kaniwani-audio var buttonHtml = `<button id="playAudio">Play Audio</button>`; var colorDisabled = "hsl(0, 0%, 65%)"; var loadingImageHtml = `<img src="http://img.etimg.com/photo/45627788.cms" alt="Loading ..." style="margin-left:5px;width:15px;height:15px;display:none;">`; var playSoundButton; var loadingImage; var audioWord = null; var audio = null; var lastAudio = null; var isPendingPlay = false; var isLoadingAudio = false; var onAudioReady = function(audioNode){ audio = audioNode; audio.style.display = 'none'; document.body.appendChild(audio); isLoading = false; loadingImage.style.display = "none"; // hide loading image playSoundButton.style.color = ""; // set default text color if(isPendingPlay){ audio.play(); isPendingPlay = false; } }; var onAudioLoading = function(){ loadingImage.style.display = "inline"; // show loading image playSoundButton.style.color = colorDisabled; // dimm play button isLoading = true; }; (function() { 'use strict'; initWhenReady(); })(); // wait until the first word has been loaded so that we can complete the setup function initWhenReady(){ console.log("Trying to initialize KaniWani audio..."); var wordDom = getWordDom(); if(wordDom){ console.log("KaniWani audio initialized"); init(); }else{ //wait a bit and then try again, assuming the initial loading will be complete soon setTimeout(function(){ initWhenReady(); }, 500); } } function init(){ initElements(); // loads audio for the first time; loadAudio(); onNewWordObserver(function(mutations, observer) { // loads audio everytime the word DOM changes loadAudio(); }); onCorrectAnswerObserver(function() { playAudio(); }); playSoundButton.onclick = function(){ playAudio(); }; var initialLocation = location.pathname; // check to make sure that the location hasn't changed // if it has, redo the initialization var intervalId = setInterval(function(){ if (location.pathname !== initialLocation) { clearInterval(intervalId); initWhenReady(); } }, 1000); } // plays the audio // finds the word than loads the audo if needed and plays it function playAudio(){ var word = getWord(); if(!word) { console.log("Cannot get word for audio :("); return; } // if audio is available just play it if(audio){ audio.play(); return; } // audio is not available we need to load it // make sure audio is not already loading if(!isLoadingAudio){ loadAudio(word); } // set pendingPlay true so when it loads it will play the audio isPendingPlay = true; } // accepts function that is triggered when new word is shown function onNewWordObserver(f){ MutationObserver = window.MutationObserver || window.WebKitMutationObserver; var observer = new MutationObserver(f); // configuration of the observer: var config = { attributes: true, childList: true, characterData: true }; // select the target node var target = getWordDom(); // pass in the target node, as well as the observer options observer.observe(target.parentNode.parentNode, config); } // accepts function that is triggered when user has answered correctly function onCorrectAnswerObserver(f){ MutationObserver = window.MutationObserver || window.WebKitMutationObserver; var observer = new MutationObserver(function() { var target = document.querySelector('form > div'); console.log(window.getComputedStyle(target).backgroundColor); if (target && ( window.getComputedStyle(target).backgroundColor === 'rgb(127, 212, 104)' || window.getComputedStyle(target).backgroundColor === 'rgb(68, 119, 51)')) { // Answer was correct! f(); } }); // configuration of the observer: var config = { attributes: true, childList: true, characterData: true }; var target = document.querySelector('form'); // pass in the target node, as well as the observer options observer.observe(target, config); } // adds all the buttons and loading images to the webpage function initElements(){ // create "play audio button" playSoundButton = htmlToElement(buttonHtml); loadingImage = htmlToElement(loadingImageHtml); playSoundButton.appendChild(loadingImage); // insert var addSynonymButton = document.evaluate("//button[contains(., 'Add Synonym')]", document, null, XPathResult.ANY_TYPE, null ).iterateNext(); if (addSynonymButton) { playSoundButton.className = addSynonymButton.className; addSynonymButton.parentNode.appendChild(playSoundButton); } } // get the dom that is containg word that it has to play function getWordDom(){ var answers = document.querySelectorAll('[data-answer=true]'); if (answers && answers.length >= 1) { var first = answers[0]; return first.querySelector('[data-ruby]'); } return null; } // finds the word that it has to play function getWord(){ var kanjisDom = getWordDom(); if(!kanjisDom){ return null; } // get the word var word = kanjisDom.dataset.ruby; if (word) { var parts = word.split(/\s+/); if (parts.length !== 2) { console.log(`Malformed ruby answer! '${word}'`); return null; } return parts[0]; } } // get audio url for a word and play it function loadAudio(){ var vocabKanji = getWord(); if(!vocabKanji) throw "vocabKanji cannot be empty!"; if (vocabKanji === audioWord) return; audioWord = vocabKanji; // keep the last audio element around in case it's currently being played, // or else the audio might get cut off. if (lastAudio) { document.body.removeChild(lastAudio); } if (audio) { lastAudio = audio; } audio = null; onAudioLoading(); GM_xmlhttpRequest ( { method: 'GET', url: `https://www.wanikani.com/vocabulary/${vocabKanji}`, accept: 'text/xml', onreadystatechange: function (response) { if (response.readyState != 4) return; // get responseTxt var responseTxt = response.responseText; var domRoot = document.createElement('html'); domRoot.innerHTML = responseTxt; var audioNode = domRoot.querySelector('audio'); if (audioNode) { onAudioReady(audioNode); } } } ); } /** * Creates dom element from string. * @param {String} HTML representing a single element * @return {Element} */ function htmlToElement(html) { var template = document.createElement('template'); template.innerHTML = html; return template.content.firstChild; }