Wanikani Anki Mode (irrelephant mod with buttons)

Anki mode for Wanikani; modified to show Anki buttons below character & answer field so that your hand doesn't hide that information. Uses two states for the button: either one large "Show Answer" button or two "Know"/"Don't Know" buttons so that you don't have to move your finger anywhere in case you got an answer correct. You can also use "K" as the shortcut for "Know" (oKAY, I *K*now this) and "L" as the shortcut for "Don't know" (as in "this time, I *L*ose").

  1. // ==UserScript==
  2. // @name Wanikani Anki Mode (irrelephant mod with buttons)
  3. // @namespace irrelephant
  4. // @version 1.6.3
  5. // @description Anki mode for Wanikani; modified to show Anki buttons below character & answer field so that your hand doesn't hide that information. Uses two states for the button: either one large "Show Answer" button or two "Know"/"Don't Know" buttons so that you don't have to move your finger anywhere in case you got an answer correct. You can also use "K" as the shortcut for "Know" (oKAY, I *K*now this) and "L" as the shortcut for "Don't know" (as in "this time, I *L*ose").
  6. // @author irrelephant
  7. // @match https://www.wanikani.com/review/session*
  8. // @match http://www.wanikani.com/review/session*
  9. // @grant none
  10. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  11. // ==/UserScript==
  12.  
  13. //BASED ON:
  14. //Wanikani Anki Mode by Mempo and modifications by necul, see https://community.wanikani.com/t/userscripts-on-android-browser/19113
  15. //Original author: Oleg Grishin <og402@nyu.edu>
  16.  
  17. console.log('/// Start of Wanikani Anki Mode');
  18.  
  19.  
  20. // Save the original evaluator
  21. var originalChecker = answerChecker.evaluate;
  22.  
  23. var checkerYes = function (itemType, correctValue) {
  24. return {accurate : !0, passed: !0};
  25. }
  26.  
  27. var checkerNo = function (itemType, correctValue) {
  28. return {accurate : !0, passed: 0};
  29. }
  30.  
  31. var activated = false;
  32. var answerShown = false;
  33.  
  34. //AUTOSTART
  35. var autostart = false;
  36.  
  37.  
  38. MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
  39.  
  40. var observer = new MutationObserver(function(mutations, observer) {
  41. $("#user-response").blur();
  42. });
  43.  
  44. var WKANKIMODE_toggle = function () {
  45.  
  46. if (activated) {
  47. if(autostart){
  48. //DISABLE ANKI MODE
  49. $("#WKANKIMODE_anki").text("Anki Mode Off");
  50. $("#answer-form form button").prop("disabled", false);
  51. $("#user-response").off("focus");
  52. $("#user-response").focus();
  53.  
  54. answerChecker.evaluate = originalChecker;
  55. observer.disconnect();
  56.  
  57. localStorage.setItem("WKANKI_autostart", false);
  58. activated = false;
  59. autostart = false;
  60. console.log("back to #1");
  61.  
  62.  
  63. }else{
  64. //ENABLE AUTOSTART
  65. activated = true;
  66. autostart = true;
  67. localStorage.setItem("WKANKI_autostart", true);
  68.  
  69. $("#WKANKIMODE_anki").text("Anki Mode Auto Start");
  70.  
  71. // start observer to force blur
  72. observer.observe(document.getElementById("answer-form"), {
  73. childList: true,
  74. subtree: true,
  75. attributes: true,
  76. characterData: false
  77. });
  78. }
  79.  
  80.  
  81.  
  82.  
  83.  
  84. } else {
  85. //ENABLE ANKI MODE
  86. $("#WKANKIMODE_anki").text("Anki Mode On");
  87. $("#answer-form form button").prop("disabled", true);
  88. $("#user-response").on("focus", function () {
  89. $("#user-response").blur();
  90. });
  91. activated = true;
  92. autostart = false;
  93. // start observer to force blur
  94. observer.observe(document.getElementById("answer-form"), {
  95. childList: true,
  96. subtree: true,
  97. attributes: true,
  98. characterData: false
  99. });
  100. }
  101.  
  102. }
  103.  
  104.  
  105. var WKANKIMODE_hideAnswerButtons = function(){
  106. $(".WKANKIMODE_button.correct").hide();
  107. $(".WKANKIMODE_button.incorrect").hide();
  108. $(".WKANKIMODE_button.show").show();
  109. }
  110.  
  111. var WKANKIMODE_showAnswerButtons = function(){
  112. $(".WKANKIMODE_button.correct").show();
  113. $(".WKANKIMODE_button.incorrect").show();
  114. $(".WKANKIMODE_button.show").hide();
  115. }
  116.  
  117. var WKANKIMODE_showAnswer = function () {
  118. if (!$("#answer-form form fieldset").hasClass("correct") &&
  119. !$("#answer-form form fieldset").hasClass("incorrect") &&
  120. !answerShown ) {
  121. var currentItem = $.jStorage.get("currentItem");
  122. var questionType = $.jStorage.get("questionType");
  123. if (questionType === "meaning") {
  124. var answer = currentItem.en.join(", ");
  125. if (currentItem.syn.length) {
  126. answer += " (" + currentItem.syn.join(", ") + ")";
  127. }
  128. $("#user-response").val(answer);
  129. } else { //READING QUESTION
  130. var i = 0;
  131. var answer = "";
  132. if (currentItem.voc) {
  133. answer += currentItem.kana[0];
  134. } else if (currentItem.emph == 'kunyomi') {
  135. answer += currentItem.kun[0];
  136. } else if (currentItem.emph == 'nanori') {
  137. answer += currentItem.nanori[0];
  138. } else {
  139. answer += currentItem.on[0];
  140. }
  141. $("#user-response").val(answer);
  142. }
  143. answerShown = true;
  144. WKANKIMODE_showAnswerButtons();
  145. }
  146. };
  147.  
  148. var WKANKIMODE_answerYes = function () {
  149. if (answerShown) {
  150. answerChecker.evaluate = checkerYes;
  151. $("#answer-form form button").click();
  152. answerShown = false;
  153. answerChecker.evaluate = originalChecker;
  154. return;
  155. }
  156.  
  157. // if answer is shown, press '1' one more time to go to next
  158. if ($("#answer-form form fieldset").hasClass("correct") ||
  159. $("#answer-form form fieldset").hasClass("incorrect") ) {
  160. $("#answer-form form button").click();
  161. WKANKIMODE_hideAnswerButtons();
  162. }
  163.  
  164. };
  165.  
  166. var WKANKIMODE_answerNo = function () {
  167. if (answerShown) {
  168. answerChecker.evaluate = checkerNo;
  169. $("#answer-form form button").click();
  170. answerShown = false;
  171. answerChecker.evaluate = originalChecker;
  172. return;
  173. }
  174.  
  175. if ($("#answer-form form fieldset").hasClass("correct") ||
  176. $("#answer-form form fieldset").hasClass("incorrect") ) {
  177. $("#answer-form form button").click();
  178. WKANKIMODE_hideAnswerButtons();
  179. }
  180.  
  181. };
  182.  
  183.  
  184. /*jshint multistr: true */
  185. var css = "\
  186. #WKANKIMODE_anki { \
  187. background-color: #000099; \
  188. margin: 0 5px; \
  189. } \
  190. #WKANKIMODE_yes { \
  191. background-color: #009900; \
  192. margin: 0 0 0 5px; \
  193. } \
  194. #WKANKIMODE_no { \
  195. background-color: #990000; \
  196. } \
  197. .WKANKIMODE_button { \
  198. width: 50%; \
  199. display: inline-block; \
  200. text-align:center; \
  201. font-size: 0.8125em; \
  202. color: #FFFFFF; \
  203. cursor: pointer; \
  204. padding: 10px 0; \
  205. margin-bottom: 5px; \
  206. } \
  207. .WKANKIMODE_buttons { \
  208. display: inline-block; \
  209. width:100%; \
  210. } \
  211. .WKANKIMODE_buttons .incorrect { \
  212. background-color: #990000; \
  213. } \
  214. .WKANKIMODE_buttons .correct { \
  215. background-color: #009900; \
  216. } \
  217. .WKANKIMODE_buttons .show { \
  218. background-color: #000099; \
  219. width:100%;\
  220. } \
  221. #WKANKIMODE_anki.hidden { \
  222. display: none; \
  223. } ";
  224.  
  225.  
  226.  
  227. function addStyle(aCss) {
  228. var head, style;
  229. head = document.getElementsByTagName('head')[0];
  230. if (head) {
  231. style = document.createElement('style');
  232. style.setAttribute('type', 'text/css');
  233. style.textContent = aCss;
  234. head.appendChild(style);
  235. return style;
  236. }
  237. return null;
  238. }
  239.  
  240. var addButtons = function () {
  241. //CHECK AUTOSTART
  242. autostart = localStorage.getItem('WKANKI_autostart')==="true"?true:false;
  243.  
  244. $("<div />", {
  245. id : "WKANKIMODE_anki",
  246. title : "Anki Mode",
  247. })
  248. .text("Anki Mode Off")
  249. .addClass("WKANKIMODE_button")
  250. .on("click", WKANKIMODE_toggle)
  251. .prependTo("footer");
  252.  
  253.  
  254. $("<div />", {
  255. id : "WKANKIMODE_buttons"
  256. })
  257. .addClass("WKANKIMODE_buttons")
  258. .appendTo("#answer-form");
  259.  
  260. $("<div />", {
  261. id : "WKANKIMODE_anki_incorrect",
  262. title : "Shortcut: L",
  263. })
  264. .text("Don't know")
  265. .addClass("WKANKIMODE_button incorrect")
  266. .on("click", WKANKIMODE_answerNo)
  267. .prependTo("#WKANKIMODE_buttons");
  268.  
  269. $("<div />", {
  270. id : "WKANKIMODE_anki_show",
  271. title : "Shortcut: Space",
  272. })
  273. .text("Show Answer")
  274. .addClass("WKANKIMODE_button show")
  275. .on("click", WKANKIMODE_showAnswer)
  276. .prependTo("#WKANKIMODE_buttons");
  277.  
  278. $("<div />", {
  279. id : "WKANKIMODE_anki_correct",
  280. title : "Shortcut: K",
  281. })
  282. .text("Know")
  283. .addClass("WKANKIMODE_button correct")
  284. .on("click", WKANKIMODE_answerYes)
  285. .prependTo("#WKANKIMODE_buttons");
  286.  
  287. // TO-DO
  288. // add physical buttons to press yes/no/show answer
  289.  
  290. // var yesButton = "<div id='WKANKIMODE_yes' class='WKANKIMODE_button' title='Correct' onclick='WKANKIMODE_correct();'>Correct</div>";
  291. // var noButton = "<div id='WKANKIMODE_no' class='WKANKIMODE_button' title='Incorrect' onclick='WKANKIMODE_incorrect();'>Incorrect</div>";
  292.  
  293. // $("footer").prepend($(noButton).hide());
  294. // $("footer").prepend($(yesButton).hide());
  295.  
  296. };
  297.  
  298. var autostartFeature = function() {
  299. console.log("///////////// AUTOSTART: " + autostart);
  300. if(autostart){
  301. $("#WKANKIMODE_anki").text("Anki Mode Auto Start");
  302. $("#answer-form form button").prop("disabled", true);
  303. $("#user-response").on("focus", function () {
  304. $("#user-response").blur();
  305. });
  306. activated = true;
  307. // start observer to force blur
  308. observer.observe(document.getElementById("answer-form"), {
  309. childList: true,
  310. subtree: true,
  311. attributes: true,
  312. characterData: false
  313. });
  314. }
  315. }
  316.  
  317. var bindHotkeys = function () {
  318. $(document).on("keydown.reviewScreen", function (event)
  319. {
  320. if ($("#reviews").is(":visible") && !$("*:focus").is("textarea, input"))
  321. {
  322. switch (event.keyCode) {
  323. //key: Space
  324. case 32:
  325. event.stopPropagation();
  326. event.preventDefault();
  327.  
  328. if (activated)
  329. WKANKIMODE_showAnswer();
  330.  
  331. return;
  332. break;
  333. //key: "K" (like "oKAY, I got this")
  334. case 75:
  335. event.stopPropagation();
  336. event.preventDefault();
  337.  
  338. if (activated)
  339. WKANKIMODE_answerYes();
  340.  
  341. return;
  342. break;
  343. //key: "L" like "loser" (sorry, you are not a loser! It's all for the sake of the mnemonics)
  344. case 76:
  345.  
  346. event.stopPropagation();
  347. event.preventDefault();
  348.  
  349. if (activated)
  350. WKANKIMODE_answerNo();
  351.  
  352. return;
  353. break;
  354. }
  355. }
  356. });
  357. };
  358.  
  359.  
  360. addStyle(css);
  361. addButtons();
  362. autostartFeature();
  363. bindHotkeys();
  364. WKANKIMODE_hideAnswerButtons();

QingJ © 2025

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