KaniWani audio

Play audio in KaniWani

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         KaniWani audio
// @namespace    http://tampermonkey.net/
// @version      0.22 alpha
// @description  Play audio in KaniWani
// @author       CometZero
// @match        https://kaniwani.com/kw/review/
// @match        https://www.kaniwani.com/kw/review/
// @grant        GM_xmlhttpRequest
// ==/UserScript==

//https://kaniwani.com/kw/review/

var buttonHtml =
`<a id="playAudio" class="button -addsynonym" href="#">Play Audio</a>`;
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 audio = null;
var isPendingPlay = false;
var isLoadingAudio = false;


var onAudioReady = function(){
    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';

    // TODO test if my service is still working (wanikaniaudio.herokuapp.com)
    // and notify the user to motivate me to enable the service

    initElements();

    // loads audio for the first time
    loadAudio();


    onNewWordObserver(function(mutations, observer) {
        // loads audio everytime the word DOM changes
        loadAudio();
    });


    playSoundButton.onclick = function(){
        playAudio();
    };


    document.getElementById('submitAnswer').onclick = function(){
        playAudio();
    };

})();

// plays the audio
// finds the word than loads the audo if needed and plays it
function playAudio(){
    var word = getWord();

    if(word === null) {
        console.log("Cannot get word :(");
        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, config);
}

// accepts function that is triggered when user has answered correctly
function onCorrectAnswerObserver(f){
    MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    var observer = new MutationObserver(f);

    // configuration of the observer:
    var config = { attributes: true, childList: true, characterData: true };

    // TODO find target and change the config
    // select the target node
    var target = null;
    
    // 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);
    //buttonWraper.innerHTML = buttonHtml;

    // insert
    var answerPanel = document.getElementById('answerPanel');
    answerPanel.parentNode.insertBefore(playSoundButton, answerPanel.nextSibling);    
}

// get the dom that is containg word that it has to play
function getWordDom(){
    var detailKanjiDiv = document.getElementById("detailKanji");
    var kanjisDom = detailKanjiDiv.getElementsByClassName("text");

    if(kanjisDom && kanjisDom.length >= 1){
        return kanjisDom[0];
    }
    
    return null;
}

// finds the word that it has to play
function getWord(){
    var kanjisDom = getWordDom();

    if(kanjisDom != null){
        // get just the first kanji
        var kanjis =  kanjisDom.innerHTML;
        var splitKanjis = kanjis.split("<br>");

        return splitKanjis[0];;
    } else {
        return null;
    }
}

// get audio url for a word and play it
function loadAudio(){
    vocubKanji = getWord();
    if(isEmpty(vocubKanji)) throw "vocubKanji cannot be empty!";

    audio = null;
    onAudioLoading();

    GM_xmlhttpRequest ( {
        method: 'GET',
        url:    'https://wanikaniaudio.herokuapp.com/url/' + vocubKanji,
        accept: 'text/xml',
        onreadystatechange: function (response) {
            console.log(response);

            if (response.readyState != 4)
                return;

            // get responseTxt
            var responseTxt = response.responseText;

            // check if responseTxt is valid
            if (!isEmpty(responseTxt) && !responseTxt.startsWith("Cannot")){
                audio = new Audio(responseTxt);
                onAudioReady(audio);
            } else {
                console.log("Invalid response " + responseTxt);
            }
        }
    } );
}

// check if string is empty
function isEmpty(str) {
    return (!str || 0 === str.length);
}

/**
 * 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;
}