WaniKani Multiple Answer Input (2023)

Input multiple readings/meanings into Wanikani

目前为 2023-05-20 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name WaniKani Multiple Answer Input (2023)
  3. // @namespace http://www.wanikani.com
  4. // @version 2.0.0
  5. // @description Input multiple readings/meanings into Wanikani
  6. // @author polv
  7. // @match https://www.wanikani.com/extra_study/session*
  8. // @match https://www.wanikani.com/review/session*
  9. // @match https://www.wanikani.com/subjects/*
  10. // @match https://preview.wanikani.com/extra_study/session*
  11. // @match https://preview.wanikani.com/review/session*
  12. // @match https://preview.wanikani.com/subjects/*
  13. // @icon https://www.google.com/s2/favicons?sz=64&domain=wanikani.com
  14. // @license MIT
  15. // @homepage https://gf.qytechs.cn/en/scripts/465750-wanikani-please-check-spelling
  16. // @supportURL https://community.wanikani.com/t/userscript-multiple-answer-input-revamped/49075/44
  17. // @source https://github.com/patarapolw/wanikani-userscript/blob/master/userscripts/mulitple-answer.user.js
  18. // @grant none
  19. // ==/UserScript==
  20.  
  21. // @ts-check
  22. (function () {
  23. 'use strict';
  24.  
  25. /** @typedef {'whitelist' | 'blacklist' | 'warning'} AuxiliaryType */
  26.  
  27. /**
  28. * @typedef {{
  29. * questionType: string
  30. * item: {
  31. * type: string
  32. * characters: string
  33. * readings?: string[]
  34. * auxiliary_readings?: {
  35. * reading: string
  36. * type: AuxiliaryType
  37. * }[]
  38. * meanings: string[]
  39. * auxiliary_meanings: {
  40. * meaning: string
  41. * type: AuxiliaryType
  42. * }[]
  43. * }
  44. * userSynonyms: string[]
  45. * response: string
  46. * }} EvaluationParam
  47. */
  48.  
  49. /**
  50. * @typedef {{
  51. * action: 'pass' | 'fail' | 'retry'
  52. * message: null | {
  53. * text: string
  54. * type: 'itemInfoException' | 'answerException'
  55. * }
  56. * }} Evaluation
  57. */
  58.  
  59. /** @typedef {((e: EvaluationParam) => Evaluation)} EvaluationFunction */
  60. /** @typedef {((e: EvaluationParam, check: EvaluationFunction) => Evaluation | null)} TryEvaluationFunction */
  61.  
  62. class ModAnswerChecker {
  63. /**
  64. * @type {TryEvaluationFunction[]}
  65. */
  66. mods = [];
  67.  
  68. /**
  69. *
  70. * @param {TryEvaluationFunction} fn
  71. */
  72. register(fn) {
  73. this.mods.push(fn);
  74. }
  75.  
  76. constructor() {
  77. // Automatically init on new instance
  78. this.init();
  79. }
  80.  
  81. async init() {
  82. const answerChecker = await this.getAnswerChecker(60000);
  83.  
  84. answerChecker.oldEvaluate = answerChecker.evaluate.bind(answerChecker);
  85. answerChecker.evaluate = (e) => {
  86. for (const fn of this.mods) {
  87. const r = fn(e, answerChecker.oldEvaluate);
  88. if (r) return r;
  89. }
  90. return answerChecker.oldEvaluate(e);
  91. };
  92. }
  93.  
  94. /**
  95. * Get answerChecker Object
  96. * @param {number} timeout
  97. * @returns {Promise<{
  98. * oldEvaluate: EvaluationFunction
  99. * evaluate: EvaluationFunction
  100. * }>}
  101. */
  102. async getAnswerChecker(timeout) {
  103. //Stimulus.controllers.filter((x)=>{return x.answerChecker;})[0]
  104. const start = Date.now();
  105.  
  106. function waitForAnswerChecker(resolve, reject) {
  107. // @ts-ignore
  108. const Stimulus = window.Stimulus;
  109. if (
  110. Stimulus &&
  111. Stimulus.controllers.filter((x) => {
  112. return x.answerChecker;
  113. })[0]
  114. ) {
  115. var answerChecker = Stimulus.controllers.filter((x) => {
  116. return x.answerChecker;
  117. })[0].answerChecker;
  118. resolve(answerChecker);
  119. } else if (timeout && Date.now() - start >= timeout)
  120. reject(new Error('timeout'));
  121. else setTimeout(waitForAnswerChecker.bind(this, resolve, reject), 30);
  122. }
  123.  
  124. return new Promise(waitForAnswerChecker);
  125. }
  126. }
  127.  
  128. // @ts-ignore
  129. window.modAnswerChecker = window.modAnswerChecker || new ModAnswerChecker();
  130. /** @type {ModAnswerChecker} */
  131. // @ts-ignore
  132. const modAnswerChecker = window.modAnswerChecker;
  133.  
  134. //////////////////////////////////////////////////////////////////////////////
  135.  
  136. /**
  137. * !Multiple Answer Input section
  138. * @see https://community.wanikani.com/t/userscript-multiple-answer-input-revamped/49075/44
  139. */
  140. modAnswerChecker.register((e, tryCheck) => {
  141. /** @type {Record<string, Evaluation[]>} */
  142. const evalActionMap = {};
  143.  
  144. for (const subResponse of e.response.split(';')) {
  145. const result = tryCheck({ ...e, response: subResponse.trim() });
  146.  
  147. const sect = evalActionMap[result.action] || [];
  148. sect.push(result);
  149. evalActionMap[result.action] = sect;
  150. }
  151.  
  152. for (const actionType of ['fail', 'retry', 'pass']) {
  153. if (evalActionMap[actionType]) {
  154. return (
  155. evalActionMap[actionType].find((r) => r.message) ||
  156. evalActionMap[actionType][0]
  157. );
  158. }
  159. }
  160.  
  161. return null;
  162. });
  163. })();

QingJ © 2025

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