Wanikani Double-Check

Allows retyping typo'd answers, or marking wrong when WK's typo tolerance is too lax.

目前為 2019-10-01 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Wanikani Double-Check
  3. // @namespace wkdoublecheck
  4. // @description Allows retyping typo'd answers, or marking wrong when WK's typo tolerance is too lax.
  5. // @include https://www.wanikani.com/review/session*
  6. // @version 2.2.8
  7. // @author Robin Findley
  8. // @copyright 2017+, Robin Findley
  9. // @license MIT; http://opensource.org/licenses/MIT
  10. // @run-at document-end
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. // CREDITS: This is a replacement for an original script by Wanikani user @Ethan.
  15. // Ethan's script stopped working due to some Wanikani changes. The code below is
  16. // 100% my own, but it closely replicates the functionality of Ethan's original script.
  17.  
  18. // HOTKEYS:
  19. // "+" - Marks answer as 'correct'.
  20. // "-" - Marks answer as 'incorrect'.
  21. // "Escape" or "Backspace" - Resets question, allowing you to retype.
  22.  
  23. // SEE SETTINGS BELOW.
  24.  
  25. window.doublecheck = {};
  26.  
  27. (function(gobj) {
  28.  
  29. /* global $, wkof, additionalContent, answerChecker, lastItems, Srs, wanakana */
  30.  
  31. var settings;
  32.  
  33. wkof.include('Menu,Settings');
  34. wkof.ready('document,Menu,Settings').then(setup);
  35.  
  36. //------------------------------------------------------------------------
  37. // setup() - Set up the menu link and default settings.
  38. //------------------------------------------------------------------------
  39. function setup() {
  40. wkof.Menu.insert_script_link({name:'doublecheck',submenu:'Settings',title:'Double-Check',on_click:open_settings});
  41.  
  42. var defaults = {
  43. allow_retyping: true,
  44. allow_change_correct: false,
  45. allow_change_incorrect: false,
  46. typo_action: 'ignore',
  47. wrong_answer_type_action: 'warn',
  48. delay_wrong: true,
  49. delay_multi_meaning: false,
  50. delay_slightly_off: false,
  51. delay_period: 1.5,
  52. show_lightning_button: true,
  53. lightning_enabled: false,
  54. srs_msg_period: 1.2,
  55. autoinfo_correct: false,
  56. autoinfo_incorrect: false,
  57. autoinfo_multi_meaning: false,
  58. autoinfo_slightly_off: false
  59. }
  60. return wkof.Settings.load('doublecheck', defaults)
  61. .then(init_ui.bind(null, true /* first_time */));
  62. }
  63.  
  64. //------------------------------------------------------------------------
  65. // open_settings() - Open the Settings dialog.
  66. //------------------------------------------------------------------------
  67. function open_settings() {
  68. var dialog = new wkof.Settings({
  69. script_id: 'doublecheck',
  70. title: 'Double-Check Settings',
  71. on_save: init_ui,
  72. content: {
  73. tabAnswers: {type:'page',label:'Answers',content:{
  74. grpChangeAnswers: {type:'group',label:'Change Answer',content:{
  75. allow_retyping: {type:'checkbox',label:'Allow retyping answer',default:true,hover_tip:'When enabled, you can retype your answer by pressing Escape or Backspace.'},
  76. allow_change_correct: {type:'checkbox',label:'Allow changing to "correct"',default:true,hover_tip:'When enabled, you can change your answer\nto "correct" by pressing the "+" key.'},
  77. allow_change_incorrect: {type:'checkbox',label:'Allow changing to "incorrect"',default:true,hover_tip:'When enabled, you can change your answer\nto "incorrect" by pressing the "-" key.'},
  78. }},
  79. grpCarelessMistakes: {type:'group',label:'Careless Mistakes',content:{
  80. typo_action: {type:'dropdown',label:'Action for typos',default:'ignore',content:{ignore:'Ignore',warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when answer contains typos.'},
  81. wrong_answer_type_action: {type:'dropdown',label:'Wrong answer type',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when reading was entered instead of meaning, or vice versa.'},
  82. }},
  83. }},
  84. tabMistakeDelay: {type:'page',label:'Mistake Delay',content:{
  85. grpDelay: {type:'group',label:'Delay Next Question',content:{
  86. delay_wrong: {type:'checkbox',label:'Delay when wrong',default:true,refresh_on_change:true,hover_tip:'If your answer is wrong, you cannot advance\nto the next question for at least N seconds.'},
  87. delay_multi_meaning: {type:'checkbox',label:'Delay when multiple meanings',default:false,hover_tip:'If the item has multiple meanings, you cannot advance\nto the next question for at least N seconds.'},
  88. delay_slightly_off: {type:'checkbox',label:'Delay when answer has typos',default:false,hover_tip:'If your answer contains typos, you cannot advance\nto the next question for at least N seconds.'},
  89. delay_period: {type:'number',label:'Delay period (in seconds)',default:1.5,hover_tip:'Number of seconds to delay before allowing\nyou to advance to the next question.'},
  90. }},
  91. }},
  92. tabLightning: {type:'page',label:'Lightning',content:{
  93. grpLightning: {type:'group',label:'Lightning Mode',content:{
  94. show_lightning_button: {type:'checkbox',label:'Show "Lightning Mode" button',default:true,hover_tip:'Show the "Lightning Mode" toggle\nbutton on the review screen.'},
  95. lightning_enabled: {type:'checkbox',label:'Enable "Lightning Mode"',default:true,refresh_on_change:true,hover_tip:'Enable "Lightning Mode", which automatically advances to\nthe next question if you answer correctly.'},
  96. srs_msg_period: {type:'number',label:'SRS popup time (in seconds)',default:1.2,min:0,hover_tip:'How long to show SRS up/down popup when in lightning mode. (0 = don\'t show)'},
  97. }},
  98. }},
  99. tabAutoInfo: {type:'page',label:'Item Info',content:{
  100. grpAutoInfo: {type:'group',label:'Show Item Info',content:{
  101. autoinfo_correct: {type:'checkbox',label:'After correct answer',default:false,hover_tip:'Automatically show the Item Info after correct answers.', validate:validate_autoinfo_correct},
  102. autoinfo_incorrect: {type:'checkbox',label:'After incorrect answer',default:false,hover_tip:'Automatically show the Item Info after incorrect answers.', validate:validate_autoinfo_incorrect},
  103. autoinfo_multi_meaning: {type:'checkbox',label:'When multiple meanings',default:false,hover_tip:'Automatically show the Item Info when an item has multiple meanings.', validate:validate_autoinfo_correct},
  104. autoinfo_slightly_off: {type:'checkbox',label:'When answer has typos',default:false,hover_tip:'Automatically show the Item Info when your answer has typos.', validate:validate_autoinfo_correct},
  105. }},
  106. }},
  107. }
  108. });
  109. dialog.open();
  110. }
  111.  
  112. //------------------------------------------------------------------------
  113. // validate_autoinfo_correct() - Notify user if iteminfo and lightning are both enabled.
  114. //------------------------------------------------------------------------
  115. function validate_autoinfo_correct(enabled) {
  116. if (enabled && settings.lightning_enabled) {
  117. return 'Disable "Lightning Mode"!';
  118. }
  119. }
  120.  
  121. //------------------------------------------------------------------------
  122. // validate_autoinfo_incorrect() - Notify user if iteminfo and lightning are both enabled, and wrong_delay disabled.
  123. //------------------------------------------------------------------------
  124. function validate_autoinfo_incorrect(enabled) {
  125. if (enabled && settings.lightning_enabled && !settings.delay_wrong) {
  126. return 'Disable "Lightning Mode", or<br>enable "Delay when wrong"!';
  127. }
  128. }
  129.  
  130. //------------------------------------------------------------------------
  131. // init_ui() - Initialize the user interface.
  132. //------------------------------------------------------------------------
  133. var first_time = true;
  134. function init_ui() {
  135. settings = wkof.settings.doublecheck;
  136.  
  137. if (first_time) {
  138. first_time = false;
  139. startup();
  140. }
  141.  
  142. // Migrate 'lightning' setting from localStorage.
  143. var lightning = localStorage.getItem('lightning');
  144. if (lightning === 'false' || lightning === 'true') {
  145. localStorage.removeItem('lightning');
  146. settings.lightning_enabled = lightning;
  147. wkof.Settings.save('doublecheck');
  148. }
  149.  
  150. // Initialize the Lightning Mode button.
  151. if (settings.lightning_enabled) {
  152. $('#lightning-mode').addClass('active');
  153. } else {
  154. $('#lightning-mode').removeClass('active');
  155. }
  156. $('#lightning-mode').prop('hidden', !settings.show_lightning_button);
  157.  
  158. setClass('#option-double-check', 'hidden', !(settings.allow_change_correct || settings.allow_change_incorrect));
  159. setClass('#option-retype', 'hidden', !settings.allow_retyping);
  160. resize_buttons();
  161.  
  162. if (state === 'second_submit') {
  163. setClass('#option-double-check', 'disabled', !(
  164. (new_answer.passed && settings.allow_change_incorrect) || (!new_answer.passed && settings.allow_change_correct)
  165. ));
  166. setClass('#option-retype', 'disabled', !settings.allow_retyping);
  167. }
  168. }
  169.  
  170. var old_submit_handler, old_answer_checker, ignore_submit = false, state = 'first_submit', play_audio, show_srs, srs_load;
  171. var item, itype, item_id, item_status, qtype, valid_answers, wrong_cnt, question_cnt, completed_cnt, answer, new_answer;
  172.  
  173. //------------------------------------------------------------------------
  174. // lightning_clicked() - Lightning button handler.
  175. //------------------------------------------------------------------------
  176. function lightning_clicked() {
  177. settings.lightning_enabled = !settings.lightning_enabled;
  178. wkof.Settings.save('doublecheck');
  179. $('#lightning-mode').toggleClass('active', settings.lightning_enabled);
  180. return false;
  181. }
  182.  
  183. //------------------------------------------------------------------------
  184. // toggle_result() - Toggle an answer from right->wrong or wrong->right.
  185. //------------------------------------------------------------------------
  186. function toggle_result(new_state) {
  187. if (new_state === 'toggle') new_state = (new_answer.passed ? 'incorrect' : 'correct');
  188. if (state !== 'second_submit') return false;
  189. switch (new_state) {
  190. case 'correct':
  191. if (!settings.allow_change_correct || new_answer.passed) return false;
  192. new_answer = {passed:true, accurate:true, multipleAnswers:false, exception:false};
  193. set_answer_state(new_answer, false /* show_msgs */);
  194. break;
  195. case 'incorrect':
  196. if (!settings.allow_change_incorrect || !new_answer.passed) return false;
  197. new_answer = {passed:false, accurate:false, multipleAnswers:false, exception:false};
  198. set_answer_state(new_answer, false /* show_msgs */);
  199. break;
  200. case 'retype':
  201. if (!settings.allow_retyping) return false;
  202. set_answer_state({reset:true}, false /* show_msgs */);
  203. break;
  204. }
  205. }
  206.  
  207. //------------------------------------------------------------------------
  208. // do_delay() - Disable the submit button briefly to prevent clicking past wrong answers.
  209. //------------------------------------------------------------------------
  210. function do_delay() {
  211. ignore_submit = true;
  212. setTimeout(function() {
  213. ignore_submit = false;
  214. }, settings.delay_period*1000);
  215. }
  216.  
  217. //------------------------------------------------------------------------
  218. // return_new_answer() - Alternate answer checker that overrides our results.
  219. //------------------------------------------------------------------------
  220. function return_new_answer() {
  221. return new_answer;
  222. }
  223.  
  224. //------------------------------------------------------------------------
  225. // set_answer_state() - Update the screen to show results of answer-check.
  226. //------------------------------------------------------------------------
  227. function set_answer_state(answer, show_msgs, no_redraw) {
  228. // If user requested to retype answer, reset the question.
  229. if (answer.reset) {
  230. $.jStorage.set('wrongCount', wrong_cnt);
  231. $.jStorage.set('questionCount', question_cnt);
  232. $.jStorage.set('completedCount', completed_cnt);
  233. $.jStorage.set('currentItem', item);
  234. $("#answer-exception").remove();
  235. $('#option-double-check').addClass('disabled').find('span').attr('title','Mark Right').find('i').attr('class','icon-thumbs-up');
  236. $('#option-retype').addClass('disabled');
  237. Srs.remove();
  238. state = 'first_submit';
  239. return;
  240. }
  241.  
  242. // If answer is invalid for some reason, do the shake thing.
  243. if (answer.exception) {
  244. $("#answer-exception").remove();
  245. if (!$("#answer-form form").is(":animated")) {
  246. $("#reviews").css("overflow-x", "hidden");
  247. var xlat = {onyomi:"on'yomi", kunyomi:"kun'yomi", nanori:"nanori"};
  248. var emph = xlat[item.emph];
  249. $("#answer-form form").effect("shake", {}, 300, function() {
  250. $("#reviews").css("overflow-x", "visible");
  251. if (!answer.accurate && $('#user-response').val() !== '') {
  252. if (settings.wrong_answer_type_action === 'warn' && answer.wrong_answer_type) {
  253. $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>WaniKani is looking for the '+(qtype === 'reading' ? 'Reading' : 'Meaning')+'.</span></div>').addClass("animated fadeInUp"));
  254. } else if (!answer.bad_input && qtype === 'reading') {
  255. $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>WaniKani is looking for the '+emph+" reading</span></div>").addClass("animated fadeInUp"));
  256. } else if (settings.typo_action === 'warn') {
  257. $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>Your answer was a bit off. Check the meaning to make sure you are correct.</span></div>').addClass("animated fadeInUp"));
  258. }
  259. }
  260. }).find("input").focus();
  261. }
  262. return;
  263. }
  264.  
  265. // Draw 'correct' or 'incorrect' results, enable Double-Check button, and calculate updated statistics.
  266. var new_wrong_cnt = wrong_cnt, new_completed_cnt = completed_cnt;
  267. $("#user-response").blur();
  268. if (settings.allow_retyping) $('#option-retype').removeClass('disabled');
  269. var new_status = Object.assign({},item_status);
  270. var dblchk = $('#option-double-check');
  271. var retype = $('#option-retype');
  272. setClass(retype, 'disabled', !settings.allow_retyping);
  273. if (answer.passed) {
  274. if (no_redraw !== true) {
  275. $("#answer-form fieldset").removeClass('incorrect').addClass("correct");
  276. dblchk.find('span').attr('title','Mark Wrong').find('i').attr('class','icon-thumbs-down');
  277. }
  278. setClass(dblchk, 'disabled', !settings.allow_change_incorrect);
  279. if (qtype === 'meaning') {
  280. new_status.mc = (new_status.mc || 0) + 1;
  281. } else {
  282. new_status.rc = (new_status.rc || 0) + 1;
  283. }
  284. } else {
  285. $("#answer-form fieldset").removeClass('correct').addClass("incorrect");
  286. dblchk.find('span').attr('title','Mark Right').find('i').attr('class','icon-thumbs-up');
  287. setClass(dblchk, 'disabled', !settings.allow_change_correct);
  288. new_wrong_cnt++;
  289. }
  290. if ((itype === 'r' || ((new_status.rc || 0) >= 1)) && ((new_status.mc || 0) >= 1)) {
  291. new_completed_cnt++;
  292. if (show_srs) {
  293. if (settings.lightning_enabled) {
  294. if (settings.srs_msg_period > 0) {
  295. var status = Object.assign({},new_status);
  296. var srs = item.srs;
  297. setTimeout(Srs.load.bind(Srs, status, srs), 100);
  298. setTimeout(Srs.remove, settings.srs_msg_period * 1000);
  299. }
  300. } else {
  301. Srs.load(new_status,item.srs);
  302. }
  303. }
  304. }
  305. $.jStorage.set('wrongCount', new_wrong_cnt);
  306. $.jStorage.set('questionCount', question_cnt + 1);
  307. $.jStorage.set('completedCount', new_completed_cnt);
  308. if (no_redraw !== true) {
  309. $("#user-response").prop("disabled", !0);
  310. }
  311.  
  312. // We've removed the audio play from enableButtons() so we can control playback.
  313. // Here, we are installing the audio, but telling it not to play yet.
  314. additionalContent.enableButtons();
  315. $('audio').remove(); // Thanks to Sinyaven for the hotfix!!
  316. play_audio(false);
  317.  
  318. var prependedAudio = $('audio');
  319. $('#option-audio').off('click');
  320. $('#option-audio').on('click', function() {
  321. if ($('#user-response').is(':disabled')) {
  322. prependedAudio.trigger('play');
  323. return false;
  324. }
  325. });
  326.  
  327. // Now that the audio is installed, move it so WK doesn't delete it upon submitting an answer.
  328. $('body').prepend(prependedAudio);
  329.  
  330. // And finally, play the audio (if autoplay is enabled).
  331. if (window.audioAutoplay && answer.passed) prependedAudio.trigger('play');
  332.  
  333. lastItems.disableSessionStats();
  334. $("#answer-exception").remove();
  335.  
  336. // Open item info, depending on settings.
  337. var showing_info = false;
  338. if (answer.passed && !settings.lightning_enabled &&
  339. (settings.autoinfo_correct ||
  340. (settings.autoinfo_slightly_off && !answer.accurate) ||
  341. (settings.autoinfo_multi_meaning && answer.multipleAnswers)
  342. )) {
  343. showing_info = true;
  344. $('#option-item-info').click();
  345. } else if (!answer.passed && !(settings.lightning_enabled && !settings.delay_wrong) && settings.autoinfo_incorrect) {
  346. showing_info = true;
  347. $('#option-item-info').click();
  348. }
  349.  
  350. // When user is submitting an answer, display the on-screen message that Wanikani normally shows.
  351. if (show_msgs) {
  352. var msg;
  353. if (answer.passed) {
  354. if (!answer.accurate) {
  355. msg = 'Your answer was a bit off. Check the '+qtype+' to make sure you are correct';
  356. } else if (answer.multipleAnswers) {
  357. msg = 'Did you know this item has multiple possible '+qtype+'s?';
  358. }
  359. } else {
  360. msg = 'Need help? View the correct '+qtype+' and mnemonic';
  361. }
  362. if (msg) {
  363. if (showing_info) {
  364. $("#information").prepend($('<div id="answer-exception" style="top:0;"><span>'+msg+'</span></div>').addClass("animated fadeInUp"));
  365. } else {
  366. $("#additional-content").append($('<div id="answer-exception"><span>'+msg+'</span></div>').addClass("animated fadeInUp"));
  367. }
  368. }
  369. }
  370. }
  371.  
  372. //------------------------------------------------------------------------
  373. // setClass() - Add or remove a class based on the 'enabled' state.
  374. //------------------------------------------------------------------------
  375. function setClass(elem, classname, enabled) {
  376. if (typeof elem === 'string') elem = $(elem);
  377. if (enabled) {
  378. elem.addClass(classname)
  379. } else {
  380. elem.removeClass(classname);
  381. }
  382. }
  383.  
  384. //------------------------------------------------------------------------
  385. // new_submit_handler() - Intercept handler for 'submit' button. Overrides default behavior as needed.
  386. //------------------------------------------------------------------------
  387. function new_submit_handler(e) {
  388. var fast_forward = false;
  389.  
  390. // Don't process 'submit' if we are ignoring temporarily (to prevent double-tapping past important info)
  391. if (ignore_submit) {
  392. // If the user presses <enter> during delay period,
  393. // WK enables the user input field, which makes Item Info not work.
  394. // Let's make sure the input field is disabled.
  395. setTimeout(function(){
  396. $("#user-response").prop('disabled',!0);
  397. },1);
  398. return false;
  399. }
  400.  
  401. // For more information about the state machine below,
  402. // see the "Theory of operation" info at the top of the script.
  403. switch(state) {
  404. case 'first_submit':
  405. // We intercept the first 'submit' click, and simulate normal Wanikani screen behavior.
  406. state = 'second_submit';
  407.  
  408. // Capture the state of the system before submitting the answer.
  409. item = $.jStorage.get('currentItem');
  410. itype = (item.rad ? 'r' : (item.kan ? 'k' : 'v'));
  411. item_id = itype + item.id;
  412. item_status = $.jStorage.get(item_id) || {};
  413. qtype = $.jStorage.get('questionType');
  414. wrong_cnt = $.jStorage.get('wrongCount');
  415. question_cnt = $.jStorage.get('questionCount');
  416. completed_cnt = $.jStorage.get('completedCount');
  417. show_srs = $.jStorage.get('r/srsIndicator');
  418.  
  419. // Ask Wanikani if the answer is right (but we don't actually submit the answer).
  420. answer = old_answer_checker(qtype, $("#user-response").val());
  421.  
  422. // Update the screen to reflect the results of our checked answer.
  423. $("html, body").animate({scrollTop: 0}, 200);
  424. new_answer = Object.assign({},answer);
  425.  
  426. var text = $('#user-response').val();
  427. if ((qtype === 'reading' && answerChecker.isNonKanaPresent(text)) ||
  428. (qtype === 'meaning' && answerChecker.isKanaPresent(text)) ||
  429. (text === '')) {
  430. answer.exception = true;
  431. answer.bad_input = true;
  432. }
  433.  
  434. // Close but no cigar
  435. if (answer.passed && !answer.accurate) {
  436. switch (settings.typo_action) {
  437. case 'warn': answer.exception = true; break;
  438. case 'wrong': answer.passed = false; break;
  439. }
  440. }
  441.  
  442. // Check for reading/meaning mixups
  443. answer.wrong_answer_type = false;
  444. if (!answer.passed) {
  445. if (qtype === 'meaning') {
  446. var accepted_readings = [].concat(item.kana, item.on, item.kun, item.nanori);
  447. var answer_as_kana = wanakana.toKana($('#user-response').val());
  448. if (accepted_readings.indexOf(answer_as_kana) >= 0) {
  449. answer.wrong_answer_type = true;
  450. }
  451. } else {
  452. var accepted_meanings = item.en.concat(item.syn, answerChecker.filterAuxiliaryMeanings(item.auxiliary_meanings, 'whitelist'));
  453. var meanings_as_hiragana = accepted_meanings.map(m => wanakana.toHiragana(m.toLowerCase()).replace(/\s/g,''));
  454. var answer_as_hiragana = Array.from($('#user-response').val().toLowerCase()).map(c => wanakana.toHiragana(c)).join('');
  455. if (meanings_as_hiragana.indexOf(answer_as_hiragana) >= 0) {
  456. answer.wrong_answer_type = true;
  457. }
  458. }
  459. }
  460. if ((settings.wrong_answer_type_action === 'warn') && answer.wrong_answer_type) answer.exception = true;
  461.  
  462. if (answer.exception) {
  463. set_answer_state(answer, true /* show_msgs */);
  464. state = 'first_submit';
  465. return false;
  466. }
  467.  
  468. // Optionally (according to settings), temporarily ignore any additional clicks on the
  469. // 'submit' button to prevent the user from clicking past important info about the answer.
  470. if ((!answer.passed && settings.delay_wrong) ||
  471. (answer.passed &&
  472. ((!answer.accurate && settings.delay_slightly_off) || (answer.multipleAnswers && settings.delay_multi_meaning))
  473. )
  474. )
  475. {
  476. set_answer_state(answer, true /* show_msgs */);
  477. do_delay();
  478. return false;
  479. } else {
  480. set_answer_state(answer, true /* show_msgs */, settings.lightning_enabled /* no_redraw */);
  481. if (settings.lightning_enabled) fast_forward = true;
  482. }
  483.  
  484. if (!fast_forward) return false;
  485. /* no break */
  486. // eslint-disable-line no-fallthrough
  487.  
  488. case 'second_submit':
  489. // We intercepted the first submit, allowing the user to optionally modify their answer.
  490. // Now, either the user has clicked submit again, or lightning is enabled and we are automatically clicking submit again.
  491. // Since Wanikani didn't see the first submit (because we intercepted it), now we need to simulate two submits for Wanikani:
  492. // 1. One for Wanikani to check the (possibly corrected) result, and
  493. // 2. One for Wanikani to move on to the next question.
  494.  
  495. // Reset the screen to pre-submitted state, so Wanikani won't get confused when it tries to process the answer.
  496. // Wanikani code will then update the screen according to our forced answer-check result.
  497. $('#option-double-check').addClass('disabled').find('span').attr('title','Double-Check').find('i').attr('class','icon-thumbs-up');
  498. $('#option-retype').addClass('disabled');
  499. $('#user-response').removeAttr('disabled');
  500. $('#option-audio audio').remove();
  501. $.jStorage.set('wrongCount', wrong_cnt);
  502. $.jStorage.set('questionCount', question_cnt);
  503. $.jStorage.set('completedCount', completed_cnt);
  504.  
  505. // Prevent WK from posting a second SRS notice.
  506. srs_load = Srs.load;
  507. Srs.load = function(){};
  508.  
  509. // This is the first submit actually forwarded to Wanikani.
  510. // It will check our (possibly corrected) answer.
  511. var result = old_submit_handler.apply(this, arguments);
  512.  
  513. // This is hidden third click from above, which Wanikani thinks is the second click.
  514. // Wanikani will move to the next question.
  515. state = 'first_submit';
  516.  
  517. // We need to disable the input field, so Wanikani will see this as the second click.
  518. $('#user-response').attr('disabled','disabled');
  519.  
  520. // Restore the SRS message function, which we disabled in second_submit above.
  521. Srs.load = srs_load;
  522.  
  523. // This is the second submit actually forwarded to Wanikani.
  524. // It will move on to the next question.
  525. return old_submit_handler.apply(this, arguments);
  526.  
  527. default:
  528. return false;
  529. }
  530.  
  531. return false;
  532. }
  533.  
  534. //------------------------------------------------------------------------
  535. // Resize the buttons according to how many are visible.
  536. //------------------------------------------------------------------------
  537. function resize_buttons() {
  538. var buttons = $('#additional-content ul>li');
  539. var btn_count = buttons.length - buttons.filter('.hidden,[hidden]').length;
  540. $('#additional-content ul > li').css('width',Math.floor(9900/btn_count)/100 + '%');
  541. }
  542.  
  543. //------------------------------------------------------------------------
  544. // External hook for @polv's script, "WaniKani Disable Default Answers"
  545. //------------------------------------------------------------------------
  546. gobj.set_state = function(_state) {
  547. state = _state;
  548. };
  549.  
  550. //------------------------------------------------------------------------
  551. // startup() - Install our intercept handlers, and add our Double-Check button and hotkey
  552. //------------------------------------------------------------------------
  553. function startup() {
  554. // Disconnect the audio player so we can control it manually.
  555. if (additionalContent.audio) {
  556. play_audio = additionalContent.audio;
  557. additionalContent.audio = function(){};
  558. }
  559.  
  560. // Check if we can intercept the submit button handler.
  561. try {
  562. old_submit_handler = $._data( $('#answer-form button')[0], 'events').click[0].handler;
  563. old_answer_checker = answerChecker.evaluate;
  564. } catch(err) {}
  565. if (typeof old_submit_handler !== 'function' || typeof old_answer_checker !== 'function') {
  566. alert('Wanikani Double-Check script is not working.');
  567. return;
  568. }
  569.  
  570. // Replace the handler.
  571. $._data( $('#answer-form button')[0], 'events').click[0].handler = new_submit_handler;
  572.  
  573. // Install the Lightning Mode button.
  574. $('head').append('<style>#lightning-mode.active {color:#ff0; opacity:1.0;}</style>');
  575. $('#summary-button').append('<a id="lightning-mode" href="#" hidden ><i class="icon-bolt" title="Lightning Mode - When enabled, auto-\nadvance after answering correctly."></i></a>');
  576. $('#lightning-mode').on('click', lightning_clicked);
  577.  
  578. // Install the Double-Check features.
  579. $('#additional-content ul').css('text-align','center').append(
  580. '<li id="option-double-check" class="disabled"><span title="Double Check"><i class="icon-thumbs-up"></i></span></li>'+
  581. '<li id="option-retype" class="disabled"><span title="Retype"><i class="icon-undo"></i></span></li></ul>'
  582. );
  583. $('#option-double-check').on('click', toggle_result.bind(null,'toggle'));
  584. $('#option-retype').on('click', toggle_result.bind(null,'retype'));
  585. $('body').on('keypress', function(event){
  586. if (event.which === 43) toggle_result('correct');
  587. if (event.which === 45) toggle_result('incorrect');
  588. return true;
  589. });
  590. $('body').on('keydown', function(event){
  591. if ((event.which === 27 || event.which === 8) &&
  592. (state !== 'first_submit') &&
  593. (event.target.nodeName === 'BODY') &&
  594. (!document.querySelector('#wkofs_doublecheck')))
  595. {
  596. toggle_result('retype');
  597. } else if (event.ctrlKey && event.key === 'l') {
  598. lightning_clicked();
  599. return false;
  600. }
  601. return true;
  602. });
  603. $('head').append(
  604. '<style>'+
  605. '#additional-content>ul>li.hidden {display:none;}'+
  606. '</style>');
  607. answerChecker.evaluate = return_new_answer;
  608. }
  609.  
  610. })(window.doublecheck);

QingJ © 2025

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