WK Do You Even Kana?

check that the okurigana matches the answer.

  1. // ==UserScript==
  2. // @name WK Do You Even Kana?
  3. // @namespace WKDYEK
  4. // @version 0.12.1
  5. // @description check that the okurigana matches the answer.
  6. // @author Ethan
  7. // @include http*://www.wanikani.com/subjects/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12.  
  13. //patchy patch. 9-5-2023
  14.  
  15.  
  16.  
  17. var alertText = "Bro, Do you even Kana?";
  18.  
  19. // Hook into App Store
  20. try { $('.app-store-menu-item').remove(); $('<li class="app-store-menu-item"><a href="https://community.wanikani.com/t/there-are-so-many-user-scripts-now-that-discovering-them-is-hard/20709">App Store</a></li>').insertBefore($('.navbar .dropdown-menu .nav-header:contains("Account")')); window.appStoreRegistry = window.appStoreRegistry || {}; window.appStoreRegistry[GM_info.script.uuid] = GM_info; localStorage.appStoreRegistry = JSON.stringify(appStoreRegistry); } catch (e) {}
  21.  
  22. //Create regex profiles (katakana matches need hiragana counterparts included)
  23. /** Prepends Hiragana counterpart to any Katakana string input
  24. * @param {String} char - A one character long string that may be a Katakana character
  25. * @returns {String} A single character if the input is Hiragana or "ー"; A two character string of (hopefully) Hiragana-Katakana pairs in square brackets (that can form a regex) if not.
  26. * @bug Will attempt to pair any character that is not Hiragana or "ー"
  27. */
  28. function pairKatakana(char){
  29. if (/^[\u3040-\u309fー]$/.test(char)){//is char hiragana or "ー"?
  30. return char;
  31. }else{
  32. //set up pairs
  33. var offset = -6*16; //katakana block: 30a0-30ff
  34. var katakana = String.fromCharCode(char.charCodeAt(0) + offset);
  35. return "["+char+katakana+"]";
  36. }
  37. }
  38.  
  39. /** Returns true if the character is Kana
  40. */
  41. function isKana(char){
  42. return /^[\u3040-\u30ff]$/.test(char);
  43. }
  44.  
  45. /** Creates regex from a vocabulary item that matches the Kana in that item.
  46.  
  47. */
  48. function makeRegex(cV){
  49. var r = "^"; //start the regex string
  50. for (var c = 0; c < cV.length; c++){
  51. if (isKana(cV[c])){
  52. r += pairKatakana(cV[c]);
  53. }
  54. else{//we have a non-kana character
  55. if (cV[c] !== "〜"){ //I doubt WK will be adding Kana suffixes but just covering all the bases to be safe.
  56. r += "(.+)"; // unknown number of characters in reading (corresponding to kanji), capturing in groups for versatility
  57. while ((c < cV.length)&&!isKana(cV[c+1])){
  58. c++;//skip non-kana characters (already have ".+" in our regex, do not need to add more)
  59. }
  60. }
  61. }
  62. }
  63. r += "$";// End of regex
  64. return new RegExp(r);
  65. }
  66.  
  67. //Get answerChecker Object
  68. //Stimulus.controllers.filter((x)=>{return x.answerChecker;})[0]
  69. var getAnswerChecker = function(timeout) {
  70. var start = Date.now();
  71.  
  72. function waitForAnswerChecker(resolve, reject) {
  73. if (typeof Stimulus !=='undefined' && Stimulus.controllers.filter((x)=>{return x.answerChecker;})[0]){
  74. var answerChecker = Stimulus.controllers.filter((x)=>{return x.answerChecker;})[0].answerChecker;
  75. resolve(answerChecker);
  76. }
  77. else if (timeout && (Date.now() - start) >= timeout)
  78. reject(new Error("timeout"));
  79. else
  80. setTimeout(waitForAnswerChecker.bind(this, resolve, reject), 30);
  81. }
  82.  
  83.  
  84.  
  85. return new Promise(waitForAnswerChecker);
  86.  
  87.  
  88. };
  89.  
  90.  
  91. /** @typedef Evaluation
  92. * @property {boolean} [accurate] - If true, the answer matched one of the possible answers
  93. * @property {boolean} [exception] - If true, the exception animation will run and the answer will not be processed.
  94. * @property {boolean} [multipleAnswers] - If true, Wanikani has more than one correct answer for the ReviewItem, a notification will be shown saying this.
  95. * @property {boolean} [passed] - If true, The answer is determined to be close enough to pass. In the case that accurate is false, the answer will pass with a notification to check your answer.
  96. */
  97. /** Can be either a meaning or a reading
  98. * @typedef Assignment
  99. * @property {string}
  100. */
  101. var dyek = function(answerChecker){
  102. //console.log("main function loading");
  103. //Get the answerChecker object out of Stimulus Controllers
  104. //var quizController = Stimulus.controllers.filter((x)=>{return x.answerChecker;})[0];
  105. //var answerChecker = quizController&&quizController.answerChecker;
  106.  
  107. //Boy, I do love to wrap this function don't I?
  108. answerChecker.oldEvaluate = answerChecker.evaluate.bind(answerChecker);
  109. /** New evaluate function to send an exception if it doesn't meet our requirements
  110. * @param
  111. * @param
  112. * @returns {Evaluation}
  113. */
  114.  
  115. /* April 2023 evaluate now takes an object as its only argument
  116. {
  117. questionType: this.currentQuestionType,
  118. response: e,
  119. item: this.currentSubject,
  120. userSynonyms: t,
  121. inputChars: this.inputChars
  122. }
  123. */
  124. answerChecker.evaluate = function(e,n,i,t){
  125.  
  126. var getQuestionType = function(){
  127. return e.questionType; //this.currentQuestionType?
  128. //return $(".quiz-input__question-type").innerHTML.toLowerCase();
  129. };
  130. var getQuestionCategory = function(){
  131. return e.item.type.toLowerCase();
  132. //return i.subject_category.toLowerCase();
  133. //return $(".quiz-input__question-category").innerHTML.toLowerCase();
  134. };
  135. var isVoc = (() => {return getQuestionCategory() === 'vocabulary';})();
  136.  
  137. var getCurrentItem = function(){
  138. return e.item.characters;
  139. //return answerChecker.currentSubject.characters;
  140. };
  141.  
  142. var getResponse = function(){
  143. return e.response;
  144. };
  145.  
  146. //jStorage no longer used in WaniKani
  147. /** @type {string} */
  148. var questionType = getQuestionType();
  149. /** @type {ReviewItem} */
  150. var cI = getCurrentItem();
  151. // If cI is vocabulary
  152. if (isVoc&&
  153. // is a reading
  154. questionType === "reading"&&
  155. // and doesn't pass regex
  156. !makeRegex(cI).test(getResponse())){
  157. //If it's a reading and it doesn't pass regex
  158. return {
  159. action: 'retry',
  160. message: {
  161. text: alertText,
  162. type: 'answerException',
  163. },
  164. };
  165. }else{
  166. return answerChecker.oldEvaluate(e,n,i,t);
  167. }
  168. };
  169.  
  170. };
  171.  
  172. getAnswerChecker(60000).then(dyek);
  173.  
  174. //import('lib/answer_checker/answer_checker').then(dyek);
  175.  
  176. //window.addEventListener('load', dyek, false);