KaniWani audio

Play audio in KaniWani

  1. // ==UserScript==
  2. // @name KaniWani audio
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.22 alpha
  5. // @description Play audio in KaniWani
  6. // @author CometZero
  7. // @match https://kaniwani.com/kw/review/
  8. // @match https://www.kaniwani.com/kw/review/
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11.  
  12. //https://kaniwani.com/kw/review/
  13.  
  14. var buttonHtml =
  15. `<a id="playAudio" class="button -addsynonym" href="#">Play Audio</a>`;
  16. var colorDisabled = "hsl(0, 0%, 65%)";
  17.  
  18. var loadingImageHtml = `<img src="http://img.etimg.com/photo/45627788.cms"
  19. alt="Loading ..." style="margin-left:5px;width:15px;height:15px;display:none;">`;
  20.  
  21. var playSoundButton;
  22. var loadingImage;
  23.  
  24. var audio = null;
  25. var isPendingPlay = false;
  26. var isLoadingAudio = false;
  27.  
  28.  
  29. var onAudioReady = function(){
  30. isLoading = false;
  31. loadingImage.style.display = "none"; // hide loading image
  32. playSoundButton.style.color = ""; // set default text color
  33.  
  34. if(isPendingPlay){
  35. audio.play();
  36. isPendingPlay = false;
  37. }
  38. };
  39.  
  40. var onAudioLoading = function(){
  41. loadingImage.style.display = "inline"; // show loading image
  42. playSoundButton.style.color = colorDisabled; // dimm play button
  43. isLoading = true;
  44. };
  45.  
  46.  
  47. (function() {
  48. 'use strict';
  49.  
  50. // TODO test if my service is still working (wanikaniaudio.herokuapp.com)
  51. // and notify the user to motivate me to enable the service
  52.  
  53. initElements();
  54.  
  55. // loads audio for the first time
  56. loadAudio();
  57.  
  58.  
  59. onNewWordObserver(function(mutations, observer) {
  60. // loads audio everytime the word DOM changes
  61. loadAudio();
  62. });
  63.  
  64.  
  65. playSoundButton.onclick = function(){
  66. playAudio();
  67. };
  68.  
  69.  
  70. document.getElementById('submitAnswer').onclick = function(){
  71. playAudio();
  72. };
  73.  
  74. })();
  75.  
  76. // plays the audio
  77. // finds the word than loads the audo if needed and plays it
  78. function playAudio(){
  79. var word = getWord();
  80.  
  81. if(word === null) {
  82. console.log("Cannot get word :(");
  83. return;
  84. }
  85.  
  86. // if audio is available just play it
  87. if(audio){
  88. audio.play();
  89. return;
  90. }
  91. // audio is not available we need to load it
  92.  
  93. // make sure audio is not already loading
  94. if(!isLoadingAudio){
  95. loadAudio(word);
  96. }
  97.  
  98. // set pendingPlay true so when it loads it will play the audio
  99. isPendingPlay = true;
  100. }
  101.  
  102. // accepts function that is triggered when new word is shown
  103. function onNewWordObserver(f){
  104. MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
  105.  
  106. var observer = new MutationObserver(f);
  107.  
  108. // configuration of the observer:
  109. var config = { attributes: true, childList: true, characterData: true };
  110.  
  111. // select the target node
  112. var target = getWordDom();
  113.  
  114. // pass in the target node, as well as the observer options
  115. observer.observe(target, config);
  116. }
  117.  
  118. // accepts function that is triggered when user has answered correctly
  119. function onCorrectAnswerObserver(f){
  120. MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
  121.  
  122. var observer = new MutationObserver(f);
  123.  
  124. // configuration of the observer:
  125. var config = { attributes: true, childList: true, characterData: true };
  126.  
  127. // TODO find target and change the config
  128. // select the target node
  129. var target = null;
  130. // pass in the target node, as well as the observer options
  131. observer.observe(target, config);
  132. }
  133.  
  134. // adds all the buttons and loading images to the webpage
  135. function initElements(){
  136. // create "play audio button"
  137. playSoundButton = htmlToElement(buttonHtml);
  138. loadingImage = htmlToElement(loadingImageHtml);
  139. playSoundButton.appendChild(loadingImage);
  140. //buttonWraper.innerHTML = buttonHtml;
  141.  
  142. // insert
  143. var answerPanel = document.getElementById('answerPanel');
  144. answerPanel.parentNode.insertBefore(playSoundButton, answerPanel.nextSibling);
  145. }
  146.  
  147. // get the dom that is containg word that it has to play
  148. function getWordDom(){
  149. var detailKanjiDiv = document.getElementById("detailKanji");
  150. var kanjisDom = detailKanjiDiv.getElementsByClassName("text");
  151.  
  152. if(kanjisDom && kanjisDom.length >= 1){
  153. return kanjisDom[0];
  154. }
  155. return null;
  156. }
  157.  
  158. // finds the word that it has to play
  159. function getWord(){
  160. var kanjisDom = getWordDom();
  161.  
  162. if(kanjisDom != null){
  163. // get just the first kanji
  164. var kanjis = kanjisDom.innerHTML;
  165. var splitKanjis = kanjis.split("<br>");
  166.  
  167. return splitKanjis[0];;
  168. } else {
  169. return null;
  170. }
  171. }
  172.  
  173. // get audio url for a word and play it
  174. function loadAudio(){
  175. vocubKanji = getWord();
  176. if(isEmpty(vocubKanji)) throw "vocubKanji cannot be empty!";
  177.  
  178. audio = null;
  179. onAudioLoading();
  180.  
  181. GM_xmlhttpRequest ( {
  182. method: 'GET',
  183. url: 'https://wanikaniaudio.herokuapp.com/url/' + vocubKanji,
  184. accept: 'text/xml',
  185. onreadystatechange: function (response) {
  186. console.log(response);
  187.  
  188. if (response.readyState != 4)
  189. return;
  190.  
  191. // get responseTxt
  192. var responseTxt = response.responseText;
  193.  
  194. // check if responseTxt is valid
  195. if (!isEmpty(responseTxt) && !responseTxt.startsWith("Cannot")){
  196. audio = new Audio(responseTxt);
  197. onAudioReady(audio);
  198. } else {
  199. console.log("Invalid response " + responseTxt);
  200. }
  201. }
  202. } );
  203. }
  204.  
  205. // check if string is empty
  206. function isEmpty(str) {
  207. return (!str || 0 === str.length);
  208. }
  209.  
  210. /**
  211. * Creates dom element from string.
  212. * @param {String} HTML representing a single element
  213. * @return {Element}
  214. */
  215. function htmlToElement(html) {
  216. var template = document.createElement('template');
  217. template.innerHTML = html;
  218. return template.content.firstChild;
  219. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址